PubSub(发布-订阅)是现代软件流行的应用程序内部通信的方式。Prism也实现了自己的PubSub的模型,它将所有接收消息的事件都注册到一个事件聚合器(EventAggregator)里面。Prism的PubSubEvent是观察者模式的一种优雅的实现。当然我们之前的文章也涉及到了一些设计模式,比如说Logger的设计是典型的工厂模式,MEF本身设计的理念也基于组合模式,再比如Cache的设计就体现出了享元模式,Export属性标记也支持共享的导出体现出了单例模式等等。虽然我们并未有可以去追求这些,但是随着我们软件开发的不断地深入,这些设计思想已经渐渐融入到我们的骨血之中。

创建我们自己的事件聚合器

首先,我们需要先整理下我们之前的代码。我们新建一个PrismSample.Infrastructure.Common的项目,然后把我们之前Export和Import关键字中的常量字符串提取出来:
Image

现在,从nuget中添加PubSubEvent的引用:
Image

然后我们在创建一个实现了事件聚合器的单例的类:

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);
}

运行结果

Image

小结

本篇演示了一个如何利用PubSubEvent来传递消息并打开一个窗口。
源码下载

参考信息

观察者模式
PubSubEvent and EventAggregator