今天 說一下Caliburn.Micro的IEventAggregator和IHandle<T>分成兩篇去講這一篇寫一個簡單的例子
看一它的的實現和源碼
下一篇用它們做一個多語言的demo
這兩個是事件的訂閱和廣播,很強大,但用的時候要小心發生不必要的沖突。
先看一下它的實現思想
在Caliburn.Micro里EventAggregator要以單例的形式出現這樣可以做到對廣播做到統一的管理
對象實現IHand<T>接口后通過EventAggregator的subsribe方法把自己加入到Handler集合中這樣就能接叫信息
能過EventAggregator.Publish(object obj)方法去發送廣播
先看一下個小demo再去分析它的源碼是怎么實現的
效果
先寫一個消息類,這個類只是做一個IHandle<T>的類型應用沒有什么實際意義
class MyMessage { public string Str { get; set; } public override string ToString() { return Str; } }
建一個窗體MainView和一個ViewModel類
<Window x:Class="CaliburnIHandle.MyViews.MyMainView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MyMainView" Height="300" Width="300"> <StackPanel> <TextBox x:Name="StrMessage" Margin="5"/> <Button x:Name="OpenOneWin" Content="OpenOneWin" Margin="5"/> <Button Content="Publish" x:Name="Publish" Margin="5"/> </StackPanel> </Window>
窗體有一個textBox顯示消息。一個button打開窗體一個發布消息
再看一下ViewModel
實現 了兩個IHandle<T> 一個是string 類型一個是我們自己定義的MyMessage
MainViewMode發布string類型的廣播也接收string類型和MyMessage類型的消息
[Export(typeof(IShell))] class MyMainViewModel : PropertyChangedBase, IHandle<string>,IHandle<MyMessage> { readonly IEventAggregator _events; readonly IWindowManager _windowManager; string strMessage; public string StrMessage { get { return strMessage; } set { strMessage = value; NotifyOfPropertyChange(() => StrMessage); } } [ImportingConstructor] public MyMainViewModel(IEventAggregator e,IWindowManager win) { _events = e; _events.Subscribe(this); _windowManager = win; } public void Handle(string message) { StrMessage = message; } public void Handle(MyMessage message) { StrMessage = message.ToString(); } #region public void Publish() { _events.Publish(StrMessage); } #endregion #region 打開窗體 public void OpenOneWin() { OneCViewModel _one=new OneCViewModel(); _windowManager.ShowWindow(_one); } #endregion
再建一個窗體做接收和廣播
它只接收string類型的消息和發布MyMessage類型的消息
<UserControl x:Class="CaliburnIHandle.MyViews.OneCView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" Height="300" Width="300"> <StackPanel> <TextBlock FontSize="13" HorizontalAlignment="Center">1</TextBlock> <TextBox Margin="5" x:Name="OneMessage"></TextBox> <Button Margin="5" x:Name="OnePublish" Content="Publish"/> </StackPanel> </UserControl>
using Caliburn.Micro; using CaliburnIHandle.CommonC; using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using System.Text; namespace CaliburnIHandle.MyViewModels { [Export(typeof(OneCViewModel))] class OneCViewModel : PropertyChangedBase, IHandle<string> { readonly IEventAggregator _event; string oneMessage; public string OneMessage { get { return oneMessage; } set { oneMessage = value; NotifyOfPropertyChange(() => OneMessage); } } public OneCViewModel() { _event = IoC.Get<IEventAggregator>(); _event.Subscribe(this); } public void OnePublish() { _event.Publish(new MyMessage { Str = OneMessage + " One!" }); } public void Handle(string message) { OneMessage = message; } } }
這是一個很簡單的例子我們看一下Caliburn.Micro源碼它是怎么實現的
看一下IHandle<T>接口
public interface IHandle<TMessage> : IHandle { //don't use contravariance here /// <summary> /// Handles the message. /// </summary> /// <param name = "message">The message.</param> void Handle(TMessage message); }
IHandle<T>只有一個處理T事件的的方法
EventAggregator類通過
/// <summary> /// Subscribes an instance to all events declared through implementations of <see cref = "IHandle{T}" /> /// </summary> /// <param name = "subscriber">The instance to subscribe for event publication.</param> public virtual void Subscribe(object subscriber) { if (subscriber == null) { throw new ArgumentNullException("subscriber"); } lock(handlers) { if (handlers.Any(x => x.Matches(subscriber))) { return; } handlers.Add(new Handler(subscriber)); } }
把訂閱的類放到Handlers集合里
再通過Publish發布相應的消息
/// <summary> /// Publishes a message. /// </summary> /// <param name = "message">The message instance.</param> /// <remarks> /// Does not marshall the the publication to any special thread by default. /// </remarks> public virtual void Publish(object message) { if (message == null) { throw new ArgumentNullException("message"); } Publish(message, PublicationThreadMarshaller); } /// <summary> /// Publishes a message. /// </summary> /// <param name = "message">The message instance.</param> /// <param name = "marshal">Allows the publisher to provide a custom thread marshaller for the message publication.</param> public virtual void Publish(object message, Action<System.Action> marshal) { if (message == null){ throw new ArgumentNullException("message"); } if (marshal == null) { throw new ArgumentNullException("marshal"); } Handler[] toNotify; lock (handlers) { toNotify = handlers.ToArray(); } marshal(() => { var messageType = message.GetType(); var dead = toNotify .Where(handler => !handler.Handle(messageType, message)) .ToList(); if(dead.Any()) { lock(handlers) { dead.Apply(x => handlers.Remove(x)); } } }); }