首页 > 代码库 > 【WP8】自定义EventAggregator

【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 类型消息

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类型消息

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发布消息

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