【WP8】自定义EventAggregator

MVVM模式实现了ViewModel和View的分离,但是有很多时候我们需要进行页面间通信

  比如,我们在设置界面修改了某个项的值,需要同步到主页面,让主页面做相关的逻辑,由于每个页面对应一个ViewModel,ViewModel之间又是独立的,很多MVVM实现都提供了EventAggregator来实现ViewModel之间的通信,其原理就是订阅与广播

EventAggregator原理

  1、消息订阅者向EventAggregator订阅消息

  2、消息发布者向EventAggregator发布消息

  3、EventAggregator想所有订阅该消息的订阅者发送

  4、订阅者接受到消息,进行相关的逻辑处理

EventAggregator可以保证ViewModel相互独立的情况下,实现ViewModel之间的交互

 

CM(Caliburn.Micro)也提供了对EventAggregator的支持

  消息以类型区分,比如两个ViewModel都订阅了string类型的消息,EventAggregator发送了一个字符串消息的时候,这两个ViewModel都会接收到,如果是不同的消息,需要进行区分

下面简单演示一下CM中EventAggregator的使用

1、ViewModel订阅消息

  MainViewModel订阅string 类型消息

【WP8】自定义EventAggregator
public class MainViewModel : PropertyChangedBase, IHandle<string>

{

    private readonly INavigationService navigationService;



    public string Message { get; set; }

    public MainViewModel(INavigationService navigationService, IEventAggregator eventAggregator)

    {

        this.navigationService = navigationService;



        eventAggregator.Subscribe(this);

    }



    public void Nav2Page1()

    {

        navigationService.UriFor<Page1ViewModel>().Navigate();

    }



    #region 接受消息函数



    //接受string类型的消息

    public void Handle(string message)

    {

        Message = message;

        NotifyOfPropertyChange(() => Message);

    }



    #endregion

}
MainViewModel

  Page1ViewModel订阅int类型消息

【WP8】自定义EventAggregator
public class Page1ViewModel : PropertyChangedBase, IHandle<int>

{

    private readonly INavigationService navigationService;



    public string Message { get; set; }

    public Page1ViewModel(INavigationService navigationService, IEventAggregator eventAggregator)

    {

        this.navigationService = navigationService;

        eventAggregator.Subscribe(this);

    }



    public void Nav2Page2()

    {

        navigationService.UriFor<Page2ViewModel>().Navigate();

    }



    #region 接受消息函数



    //接受int类型的消息

    public void Handle(int message)

    {

        Message = message.ToString(CultureInfo.InvariantCulture);

        NotifyOfPropertyChange(() => Message);

    } 



    #endregion

}
Page1ViewModel

 

2、发布消息

  在Page2ViewModel发布消息

【WP8】自定义EventAggregator
public class Page2ViewModel : PropertyChangedBase

{

    private readonly IEventAggregator eventAggregator;



    public string Message { get; set; }



    public Page2ViewModel(IEventAggregator eventAggregator)

    {

        this.eventAggregator = eventAggregator;

    }



    public void PublishInt()

    {

        //发送一个int消息

        eventAggregator.PublishOnUIThread(120);

    }



    public void PublishString()

    {

        //发送一个string消息

        eventAggregator.PublishOnUIThread("来自Page2的消息");

    }

}
Page2ViewModel

IEventAggregator.Publish之后,订阅的MainViewModel和Page1ViewModel都能接受到消息

 

问题:

  CM只提供了基本的功能,并不能满足一起特殊的需求

  1、CM的EventAggregator底层保存了一个列表,用弱类型保存Subscriber,而CM中ViewModel的生命周期是跟随View的,当View被GC回收的时候,ViewModel也会被回收  

    但是有一个问题,就是GC的回收时间是不确定的,比如我们进入了一个页面,给该页面的ViewModel订阅消息,然后离开页面,这个时候,如果这个时候GC还没有回收该ViewModel的内存时,消息订阅器EventAggregator还是可以接受到消息去触发ViewModel执行相应的消息处理函数的,也就是说,ViewModel可能还没有被回收,还可以接受消息,所以,当我们离开页面的时候需要取消ViewModel对消息的订阅以保证页面的ViewModel不能再接受消息了

    场景:我们在MainView订阅了一个消息,然后注销登陆,到了登陆页面,然后再进入MainView,如果GC还没有对之前的ViewModel进行回收的话,这个时候就会有两个MainViewModel可以接受消息,可能会导致消息函数被执行多次,

  解决:我们需要对离开的页面注销消息的订阅

  在View离开的时候取消对消息的注册

protected override void OnNavigatedFrom(NavigationEventArgs e)

{

    if (e.NavigationMode == NavigationMode.Back)

    {

        //取消ViewModel消息注册

        var model = ViewModelLocator.LocateForView(this);

        eventsAggregator.Unsubscribe(model);

    }

}

 

 

扩展(自定义EventAggregator)

  1、需求:我们需要在消息处理完后进行回掉,消息发送者可以得到消息订阅者处理结果

   场景:A页面需要一个学校列表,但是学校列表保存在B页面中,完美需要A页面发送一个广播说:我要一个学校列表,然后B页面收到广播,然后把消息列表返回给A页面,A页面就可以得到学校列表了

   由于CM内部定义的EventAggregator暴露的属性有限,很难在不改动CM源码的前提下进行扩展,下面通过自定义一个EventAggregator已满足上面需求

public interface IEventAggregator

{

    //订阅消息

    void Subscribe<T>(object subscriber, Action<T> handler);



    //发送消息

    void Publish<TSent>(TSent data);



    //订阅消息(带回掉)

    void Subscribe<TSent, TBack>(object subscriber, Func<TSent, TBack> handler);



    //发送消息(带回掉)

    void Publish<TSent, TBack>(TSent data, Action<TBack> callback);



    //注销订阅者

    void Unsubscribe(object subscriber);



    //注销消息订阅

    void Unsubscribe<T>(object subscriber);



    //清除被回收的弱类型

    void Cleanup();



    //清除所有订阅者

    void Clear();

}

实现

/// <summary>

/// 自定义消息聚合器

/// </summary>

public class EventAggregator : IEventAggregator

{

    /// <summary>

    /// 订阅者信息(弱类型保存)

    /// </summary>

    private class Handler

    {



        public object Action { get; set; }



        /// <summary>

        /// 消息订阅者(sender)

        /// </summary>

        public WeakReference Sender { get; set; }

        

        /// <summary>

        /// 消息类型

        /// </summary>

        public Type Type { get; set; }



        /// <summary>

        /// 回掉的类型

        /// </summary>

        public Type BackType { get; set; }

    }



    /// <summary>

    /// 线程锁

    /// </summary>

    private readonly object locker = new object();



    /// <summary>

    /// 订阅者列表

    /// </summary>

    private readonly List<Handler> handlers = new List<Handler>();



    /// <summary>

    /// 发布消息

    /// </summary>

    /// <typeparam name="TSent">发送的消息类型</typeparam>

    public void Publish<TSent>(TSent data)

    {

        lock (locker)

        {

            Cleanup();



            foreach (var l in handlers.Where(a => a.Type.IsAssignableFrom(typeof(TSent))).ToList())

            {

                var action = l.Action as Action<TSent>;

                if (action != null) action(data);

            }

        }

    }



    /// <summary>

    /// 发布消息,在执行完成后调用回掉函数(订阅函数有返回值)

    /// </summary>

    /// <typeparam name="TSent">发送的消息类型</typeparam>

    /// <typeparam name="TBack">返回的消息类型</typeparam>

    /// <param name="data">发送的消息</param>

    /// <param name="callback">回掉函数</param>

    public void Publish<TSent, TBack>(TSent data, Action<TBack> callback)

    {

        lock (locker)

        {

            Cleanup();



            foreach (var l in handlers.Where(a =>

                a.Type.IsAssignableFrom(typeof (TSent)) &&

                a.BackType.IsAssignableFrom(typeof (TBack))).ToList())

            {

                var action = l.Action as Func<TSent, TBack>;

                if (action != null)

                {

                    var re = action(data);

                    callback(re);

                }

            }

        }

    }



    /// <summary>

    /// 订阅消息(带返回值)

    /// </summary>

    /// <typeparam name="TSent">发送的消息类型</typeparam>

    /// <typeparam name="TBack">返回的消息类型</typeparam>

    /// <param name="subscriber">消息订阅者</param>

    /// <param name="handler">订阅函数(带返回值)</param>

    public void Subscribe<TSent, TBack>(object subscriber, Func<TSent, TBack> handler)

    {

        lock (locker)

        {

            handlers.Add(new Handler

            {

                Action = handler,

                Sender = new WeakReference(subscriber),

                Type = typeof (TSent),

                BackType = typeof (TBack)

            });

        }

    }



    /// <summary>

    /// 订阅消息(带返回值)

    /// </summary>

    /// <typeparam name="TSent">发送的消息类型</typeparam>

    /// <param name="subscriber">消息订阅者</param>

    /// <param name="handler">订阅函数</param>

    public void Subscribe<TSent>(object subscriber, Action<TSent> handler)

    {

        lock (locker)

        {

            handlers.Add(new Handler

            {

                Action = handler,

                Sender = new WeakReference(subscriber),

                Type = typeof(TSent)

            });

        }

    }



    /// <summary>

    /// 取消消息订阅

    /// </summary>

    /// <param name="subscriber"></param>

    public void Unsubscribe(object subscriber)

    {

        lock (locker)

        {

            Cleanup();



            var query = handlers.Where(a => a.Sender.Target.Equals(subscriber));



            foreach (var h in query.ToList())

            {

                handlers.Remove(h);

            }

        }

    }



    /// <summary>

    /// 取消指定消息类型的消息订阅

    /// </summary>

    public void Unsubscribe<T>(object subscriber)

    {

        lock (locker)

        {

            Cleanup();



            var query = handlers.Where(a => a.Sender.Target.Equals(subscriber) && a.Type == typeof(T));



            foreach (var h in query.ToList())

            {

                handlers.Remove(h);

            }

        }

    }



    /// <summary>

    /// 清理被回收的订阅者

    /// </summary>

    public void Cleanup()

    {

        foreach (var l in handlers.Where(a => !a.Sender.IsAlive).ToList())

        {

            handlers.Remove(l);

        }

    }



    /// <summary>

    /// 清空所有订阅者

    /// </summary>

    public void Clear()

    {

        handlers.Clear();

    }

}

 

Demo

  http://files.cnblogs.com/bomo/EventAggregatorDemo.zip

 

 

  

 

 

 

  

你可能感兴趣的:(event)