Prism for WPF再探(基于Prism事件的模块间通信)

上篇博文链接

一、简单介绍: 

 在上一篇博文中初步搭建了Prism框架的各个模块,但那只是搭建了一个空壳,里面的内容基本是空的,在这一篇我将实现各个模块间的通信,在上一篇博文的基础上改的。

      先上效果图:初步介绍下,图中虚线分割为四个模块,每个模块可向另外三个模块发消息。这里还是基于模块化开发CS端程序的思路,模块之间低耦合,如果项目做大,好处自然体现出来了。

  图中的效果已经实现了一个模块朝其他三个模块发送消息。这里我使用的事Prism框架中的PubSubEvent事件,其优点是简单易用,直接Publish和Subscribe即可。

二、基本思路

  项目结构图:

  四个模块间基础和共用的东西我放在Desktop.Infrastructure中。A、B、C、D四个模块都保持对Desktop.Infrastructure的引用,各自间无引用,相互独立,以后需要添加删除模块或者改动既有模块,都不影响其他模块的功能。

1、事件与接口,代码很简单。

接口代码:接口定义空的就行,后面Event需要Publish的Model继承自接口IBaseModel。

namespace Desktop.Infrastucture.Interface
{
    public interface IBaseModel
    {

    }
}

事件代码:自定义事件 SendMessageEvent 继承自Prism框架的PubSubEvent。定义好Event,之后只需要在IEventAggregator的实现中Publish和Subscribe即可。

namespace Desktop.Infrastucture.Event
{
    public class SendMessageEvent : PubSubEvent<IBaseModel>
    {

    }
}

从下图可以看到PubSubEvent的定义,其Subscribe支持过滤。

实现原理中其实是个模块都订阅了同一个事件,所以每个模块发一次消息它本身也会接收到,而第一张的效果图中发送消息的模块本身并没有显示出接收到消息,是因为我在Subscribe的时候将本身发的消息的过滤了。

2、Model的实现。

发送的数据为ModelData,所以ModelData肯定要继承自IBaseModel,由于WPF经常需要实现通功能,也就是必须继承自INotifyPropertyChanged接口(这点是WPF的内容),所以我定义了一个BaseNotificationObject来继承INotifyPropertyChanged和IBaseModel,ModelData继承自BaseNotificationObject。

namespace Desktop.Infrastucture.Model
{
    [Export(typeof(ModelData))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class ModelData: BaseNotificationObject
    {
        /// <summary>
        /// 模块名称
        /// </summary>
        private ModuleNameEnum _ModuleName;

        public ModuleNameEnum ModuleName
        {
            get { return _ModuleName; }
            set { _ModuleName = value;
            }
        }
        /// <summary>
        /// 消息内容
        /// </summary>
        private string _Message;

        public string Message
        {
            get { return _Message; }
            set { _Message = value;
                OnPropertyChanged("Message");
            }
        }

    }
}

3、ViewModel的实现。

每个模块的界面都需要ViewModel,所以我把通用的功能抽象出来单独写成一个类BaseViewModel。代码如下:

首先是BaseNotify,通过MEF的构造函数导入来注入IRegionManager 与IEventAggregator 的实现。其子类也就可以直接使用了。

namespace Desktop.Infrastucture.ViewModel
{
    public class BaseNotify:BaseNotificationObject
    {
        public List<SubscriptionToken> SubscriptionTokens = new List<SubscriptionToken>();
        public readonly IRegionManager regionManager;
        public readonly IEventAggregator eventAggregator;
        public BaseNotify()
        {

        }

        [ImportingConstructor]
        public BaseNotify(IRegionManager regionManager,IEventAggregator eventAggregator)
        {
            this.regionManager = regionManager;
            this.eventAggregator = eventAggregator;
        }
    }
}

BaseViewModel是所有模块ViewModel的父类。按钮触发的是BtnCommand收到消息后执行的是CallBack,这个CallBack定义成Virtual是为了子类可以重载从而执行自己特定的操作。模块的的View中绑定的数据是Data的Message。

namespace Desktop.Infrastucture.ViewModel
{
    public class BaseViewModel:BaseNotify
    {
        #region 属性、字段、命令
        //[Import]

        private Lazy<ModelData> _Data = new Lazy<ModelData>();

        public Lazy<ModelData> Data
        {
            get { return _Data; }
            set { _Data = value; }
        }

        private ICommand _BtnCommand;

        public ICommand BtnCommand
        {
            get
            {
                if (null == _BtnCommand)
                {
                    _BtnCommand = new DelegateCommand<object>((obj) =>
                    {
                        eventAggregator.GetEvent<SendMessageEvent>().Publish(Data.Value);
                    });
                }
                return _BtnCommand;
            }
            set { _BtnCommand = value; }
        }
        #endregion

        #region 构造
        [ImportingConstructor]
        public BaseViewModel(IRegionManager regionManager, IEventAggregator eventAggregator) : base(regionManager, eventAggregator)
        {
            eventAggregator.GetEvent<SendMessageEvent>().Unsubscribe(CallBack);
            SubscriptionTokens.Add(eventAggregator.GetEvent<SendMessageEvent>().Subscribe(CallBack, ThreadOption.PublisherThread, false, x =>
                {
                    if (x is ModelData)
                    {
                        var modelData = x as ModelData;
                        if (modelData.ModuleName==Data.Value.ModuleName)
                            return false;
                    }
                    return true;
                }));
        }
        #endregion

        #region 方法

        public virtual void CallBack(IBaseModel obj)
        {
            if (obj is ModelData)
            {
                var modelData = obj as ModelData;
                Data.Value.Message = "";
                Data.Value.Message += "Reciced:" + modelData.Message+"\n";
            }
        }

        #endregion
    }
}

4、模块的实现。

公共的东西都实现了,最后是模块改怎么来写。每个模块的写法基本一致,这里我以其中一个为例。这些东西简单,不多讲贴代码了。

ModelA的View

<Grid x:Class="ModuleA.View.GridA"
             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"
             >
    <StackPanel>
        <TextBox Foreground="Red" FontSize="20" Text="{Binding Data.Value.Message}"></TextBox>
        <TextBlock Foreground="Red" FontSize="20" ></TextBlock>
        <Button Height="30" Width="90" Background="LightPink" Command="{Binding BtnCommand}">ClickMe</Button>
    </StackPanel>
</Grid>
using System.ComponentModel.Composition;
using System.Windows.Controls;
using ModuleA.ViewModel;

namespace ModuleA.View
{
    /// <summary>
    /// GridA.xaml 的交互逻辑
    /// </summary>
    [PartCreationPolicy(CreationPolicy.NonShared)]
    [Export]
    public partial class GridA : Grid
    {
        [Import]
        public GridA_ViewModel ViewModel
        {
            set { this.DataContext = value; }
        }

        public GridA()
        {
            InitializeComponent();
        }

    }
}
ModelA的ViewModel
using Desktop.Infrastucture.Interface;
using Desktop.Infrastucture.Model;
using Desktop.Infrastucture.ViewModel;
using Prism.Events;
using Prism.Regions;
using System.ComponentModel.Composition;

namespace ModuleA.ViewModel
{
    [Export(typeof(GridA_ViewModel))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class GridA_ViewModel: BaseViewModel
    {

        //public new Lazy<ModelData> Data = new Lazy<ModelData>();

        [ImportingConstructor]
        public GridA_ViewModel(IRegionManager regionManager, IEventAggregator eventAggregator) : base(regionManager, eventAggregator)
        {
            Data.Value.ModuleName = ModuleNameEnum.ModuleA;

        }
        public override void CallBack(IBaseModel obj)
        {
            base.CallBack(obj);

        }

    }
}
ModuleNameEnum中定义的是ModuleA、ModuleB、ModuleC、ModuleD的枚举。Data 的定义用了懒加载,不用也一样的。如果要传更多的内容,定义ModelData就行了
讲的比较简单,代码写的也简单,这里只是作为Prism内置Event的入门,实现简单的模块间通信。真正复杂的架构设计要看个人水平了。

作者水平有限,如有不足之处还请赐教。

源码在这里!!!


原文地址:https://www.cnblogs.com/lelehellow/p/8136867.html

时间: 2024-10-07 00:00:31

Prism for WPF再探(基于Prism事件的模块间通信)的相关文章

Flex事件机制学习-自定义事件实现类间通信 .

今天,学习Flex自定义事件,可以使两个类通信,定义一个Main类. public class Main extends Sprite     {            public function Main()            { //演示ChildSprite类是与Main类通信,ChildSprite类稍后说明: var child:ChildSprite=new ChildSprite(); //指示该实例的属性值            child.flag="01";

【教程】【FLEX】#003 自定义事件、模块间通讯

本篇笔记,主要阐明 事件是如何创建 和 如何使用自定义事件达到模块之间通讯 的效果. 句子解释: 什么叫做模块之间的通讯呢?? 简单点说,就是两个模块之间可以互相传数据. A模块 可以接收到 B模块的数据,这个就叫做通讯. 所以A模块注册了一个事件, B模块触发了这个事件,A接收到. 那么 这个就是A.B模块进行通讯了. 一.自定义事件的创建(代码例子在文章底部) 1.创建一个ActionScript类,继承Event 2.声明属性 2.1  保存数据的属性(事件保存数据的对象,可多个) 2.2

【经验分享】基于创龙AM5708多核间通信的IPC例程通用开发流程

c此文章原创来源于:创龙电子 ? ? ? ??AM5708嵌入式开发板是一款由创龙基于TI AM570x浮点DSP C66x + ARM Cortex-A15工业控制及可编程音视频处理器.由SOM-TL570x工业核心板设计的开发板,它为用户提供了SOM-TL570x工业核心板的测试平台,用于快速评估SOM-TL570x工业核心板的整体性能.广泛应用于机器视觉.电力自动化.智能交通.医疗器械.自动分拣装置.高精度仪器仪表.高端数控系统等多种工业应用场合. ? ? ? ? 下面简单讲解一下创龙基于

Prism 4 文档 ---第9章 松耦合组件之间通信

当构建一个大而负责的应用程序时,通用的做法时将功能拆分到离散的模块程序集中.将模块之间的静态引用最小化.这使得模块可以被独立的开发,测试,部署和升级,以及它迫使松散耦合的沟通. 当在模块之间通信时,你需要知道不同通信方式之间的区别,那样你才能确定哪种方式对于你的特定的场景最合适,Prism类库提供了以下几种通信方式: 命令.当希望对于用户的交互马上采取动作时使用. 事件聚合.用于ViewModel,展现层,或者控制之间没有所期望的直接动作时. 区域上下文.使用它可以提供宿主和宿主区域的View之

基于Prism.Windows的UWP开发备忘

以前做UWP开发都是使用MvvmLight,主要是简单易上手,同时也写了很多MvvmLight的开发系列文章: UWP开发必备以及常用知识点总结 UWP开发之Mvvmlight实践九:基于MVVM的项目架构分享 UWP开发之Mvvmlight实践八:为什么事件注销处理要写在OnNavigatingFrom中 UWP开发之Mvvmlight实践七:如何查找设备(Mobile模拟器.实体手机.PC)中应用的Log等文件 UWP开发之Mvvmlight实践六:MissingMetadataExcept

从PRISM开始学WPF(二)Prism?

原文:从PRISM开始学WPF(二)Prism? 目录: 从PRISM开始学WPF(一)WPF? 从PRISM开始学WPF(二)Prism? 从PRISM开始学WPF(三)Prism-Region? 从PRISM开始学WPF(四)Prism-Module? 从PRISM开始学WPF(五)MVVM(一)ViewModel? 从PRISM开始学WPF(六)MVVM(二)Command? 从PRISM开始学WPF(七)MVVM(三)事件聚合器EventAggregator? 0x1 PRISM? PR

在Prism 框架中,实现主程序与模块间 UI 的通信

背景: 在模块的UI中包含 TreeView 控件,在该树形控件的每一节点前面定义了一个复选框,如图 需求: 在两个不同的应用程序中使用该控件,而它在不同应用程序中的外观则并不一致,按照本例,即一个显示复选框,一个不显示. 问题: 解决该问题的一个难处在于,Prism框架本身的设计原则——此 View 会被添加到主程序的 Shell 的 Region 中,所以在主程序中不能直接来控制该 View 的属性及其逻辑. 思路: 利用 EventAggregator 使得主程序与模块间进行通信,从而间接

再探jQuery

再探jQuery jQuery是一个JavaScript库,它极大的简化了JavaScript编程,虽然目前网络上有大量开源的JS框架,但是jQuery是目前最流行的JS框架,而且提供了大量的扩展.包括Google.Microsoft.IBM等大公司都在使用jQuery框架,值得注意的是,jQuery团体知道JS咋不同浏览器中存在这大量的兼容性问题,所以jQuery兼容所有主流浏览器,包括Internet Explorer 6! 第一部分:使用方法 那么如何使用jQuery呢?很简单,只需要在j

再探oVirt-配置一个2节点的ovirt环境

日期:2015/11/4 - 2015/11/5 主机:engine, node01, node02 目的:再探oVirt-配置一个2节点的ovirt环境 操作内容: 一.基础操作 1.资源 ovirt engine: engine ovirt node: node01, node02 2.hosts 10.50.200.141 engine.ovirt 10.50.200.101 n101.ovirt 10.50.200.102 n102.ovirt 3.防火墙放行同一个局域网内的访问限制 后