Prism6下的MEF:观察者模式下的PubSubEvent
PubSub(发布-订阅)是现代软件流行的应用程序内部通信的方式。Prism也实现了自己的PubSub的模型,它将所有接收消息的事件都注册到一个事件聚合器(EventAggregator)里面。Prism的PubSubEvent是观察者模式的一种优雅的实现。当然我们之前的文章也涉及到了一些设计模式,比如说Logger的设计是典型的工厂模式,MEF本身设计的理念也基于组合模式,再比如Cache的设计就体现出了享元模式,Export属性标记也支持共享的导出体现出了单例模式等等。虽然我们并未有可以去追求这些,但是随着我们软件开发的不断地深入,这些设计思想已经渐渐融入到我们的骨血之中。
创建我们自己的事件聚合器
首先,我们需要先整理下我们之前的代码。我们新建一个PrismSample.Infrastructure.Common的项目,然后把我们之前Export和Import关键字中的常量字符串提取出来:
现在,从nuget中添加PubSubEvent的引用:
然后我们在创建一个实现了事件聚合器的单例的类:
using Microsoft.Practices.Prism.PubSubEvents;
namespace PrismSample.Infrastructure.Common.Until
{
public class Messager
{
public IEventAggregator EventAggregator { get; private set; }
private Messager()
{
EventAggregator = new EventAggregator();
}
public static readonly Messager Default = new Messager();
}
}
我们现在要做的第一件事情就是打开我们的第二个窗口,先实现我们的事件的参数:
using System;
namespace PrismSample.Infrastructure.Common.Event
{
public class OnNewWindowEventArgs : EventArgs
{
public string ViewModelName { get; private set; }
public object Parameter { get; private set; }
public Action AfterShowAction { get; private set; }
public OnNewWindowEventArgs(string viewModelName, object parameter)
{
ViewModelName = viewModelName;
Parameter = parameter;
}
public OnNewWindowEventArgs(string viewModelName, object parameter, Action afterShowAction) :
this(viewModelName, parameter)
{
AfterShowAction = afterShowAction;
}
}
}
定义一个Event的类,给聚合器用:
using Microsoft.Practices.Prism.PubSubEvents;
namespace PrismSample.Infrastructure.Common.Event
{
public class OnNewWindowEvent : PubSubEvent<OnNewWindowEventArgs>
{
}
}
这样我们的基础参数就准备完毕了。
新建第二个View
建立一个新的类库项目:PrismSample.ProjectSample,分别添加我们的View和ViewModel:
using Microsoft.Practices.Prism.Mvvm;
using PrismSample.Infrastructure.Common.Constant;
using System.ComponentModel.Composition;
using System.Windows.Controls;
namespace PrismSample.ProjectSample.View
{
[Export(ExportView.SecondView, typeof(IView))]
public partial class SecondView : UserControl , IView
{
public SecondView()
{
InitializeComponent();
}
}
}
using Microsoft.Practices.Prism.Mvvm;
using Prism.Logging;
using PrismSample.Infrastructure.Abstract.Presentation.AbstractClass;
using PrismSample.Infrastructure.Abstract.Presentation.Interface;
using PrismSample.Infrastructure.Common.Constant;
using System.ComponentModel.Composition;
namespace PrismSample.ProjectSample.ViewModel
{
[Export(ExportViewModel.SecondViewModel,typeof(IViewModel))]
public class SecondViewModel : ViewModelBase
{
[Import]
ILoggerFacade _logger;
[ImportingConstructor]
public SecondViewModel([Import(ExportView.SecondView, typeof(IView))]IView view)
{
this.View = view;
this.View.DataContext = this;
}
public override void InitializeContext(object arg)
{
base.InitializeContext(arg);
_logger.Log("SecondViewModel Initialized", Category.Info, Priority.None);
}
}
}
我们在ViewModel的接口中添加了一个InitializeContext方法,用来初始化我们新开窗口的上下文。当然,我们依然要添加如下生成后事件:
xcopy "$(TargetPath)" "$(SolutionDir)\PrismSample\bin\Debug\" /Y
实现Pub和Sub
修改我们的Bootstrapper,添加构造函数和订阅方法如下:
...省略...
public Bootstrapper() : base()
{
Messager.Default.EventAggregator.GetEvent<OnNewWindowEvent>().Subscribe(OnNewWindow);
}
...省略...
private void OnNewWindow(OnNewWindowEventArgs args)
{
IViewModel viewModel = this.Container.GetExportedValue<IViewModel>(args.ViewModelName);
Window window = new Window();
window.Content = viewModel.View;
viewModel.InitializeContext(args.Parameter);
window.Show();
args.AfterShowAction?.Invoke();
}
...省略...
给View添加一个Click事件:
private void Button_Click(object sender, RoutedEventArgs e)
{
OnNewWindowEventArgs arg = new OnNewWindowEventArgs(ExportViewModel.SecondViewModel, "Hi, Second View", AfterOpenSecondView);
Messager.Default.EventAggregator.GetEvent<OnNewWindowEvent>().Publish(arg);
}
运行结果
小结
本篇演示了一个如何利用PubSubEvent来传递消息并打开一个窗口。
源码下载