.NET Core 3 WPF MVVM框架 Prism系列之事件聚合器
本文将介绍如何在.NET Core3环境下使用MVVM框架Prism的使用事件聚合器实现模块间的通信
一.事件聚合器
在上一篇 .NET Core 3 WPF MVVM框架 Prism系列之模块化 我们留下了一些问题,就是如何处理同模块不同窗体之间的通信和不同模块之间不同窗体的通信,Prism提供了一种事件机制,可以在应用程序中低耦合的模块之间进行通信,该机制基于事件聚合器服务,允许发布者和订阅者之间通过事件进行通讯,且彼此之间没有之间引用,这就实现了模块之间低耦合的通信方式,下面引用官方的一个事件聚合器模型图:
二.创建和发布事件
1.创建事件
首先我们来处理同模块不同窗体之间的通讯,我们在PrismMetroSample.Infrastructure新建一个文件夹Events,然后新建一个类PatientSentEvent,代码如下:
PatientSentEvent.cs:
public class PatientSentEvent: PubSubEvent<Patient>
{
}
2.订阅事件
然后我们在病人详细窗体的PatientDetailViewModel类订阅该事件,代码如下:
PatientDetailViewModel.cs:
public class PatientDetailViewModel : BindableBase{IEventAggregator _ea;IMedicineSerivce _medicineSerivce;private Patient _currentPatient;//当前病人public Patient CurrentPatient{get { return _currentPatient; }set { SetProperty(ref _currentPatient, value); }}private ObservableCollection<Medicine> _lstMedicines;//当前病人的药物列表public ObservableCollection<Medicine> lstMedicines{get { return _lstMedicines; }set { SetProperty(ref _lstMedicines, value); }}//构造函数public PatientDetailViewModel(IEventAggregator ea, IMedicineSerivce medicineSerivce){_medicineSerivce = medicineSerivce;_ea = ea;_ea.GetEvent<PatientSentEvent>().Subscribe(PatientMessageReceived);//订阅事件}//处理接受消息函数private void PatientMessageReceived(Patient patient){this.CurrentPatient = patient;this.lstMedicines = new ObservableCollection<Medicine>(_medicineSerivce.GetRecipesByPatientId(this.CurrentPatient.Id).FirstOrDefault().LstMedicines);}}
3.发布消息
然后我们在病人列表窗体的PatientListViewModel中发布消息,代码如下:
PatientListViewModel.cs:
public class PatientListViewModel : BindableBase
{private IApplicationCommands _applicationCommands;public IApplicationCommands ApplicationCommands{get { return _applicationCommands; }set { SetProperty(ref _applicationCommands, value); }}private List<Patient> _allPatients;public List<Patient> AllPatients{get { return _allPatients; }set { SetProperty(ref _allPatients, value); }}private DelegateCommand<Patient> _mouseDoubleClickCommand;public DelegateCommand<Patient> MouseDoubleClickCommand =>_mouseDoubleClickCommand ?? (_mouseDoubleClickCommand = new DelegateCommand<Patient>(ExecuteMouseDoubleClickCommand));IEventAggregator _ea;IPatientService _patientService;/// <summary>/// 构造函数/// </summary>public PatientListViewModel(IPatientService patientService, IEventAggregator ea, IApplicationCommands applicationCommands){_ea = ea;this.ApplicationCommands = applicationCommands;_patientService = patientService;this.AllPatients = _patientService.GetAllPatients(); }/// <summary>/// DataGrid 双击按钮命令方法/// </summary>void ExecuteMouseDoubleClickCommand(Patient patient){//打开窗体this.ApplicationCommands.ShowCommand.Execute(FlyoutNames.PatientDetailFlyout);//发布消息_ea.GetEvent<PatientSentEvent>().Publish(patient);}}
效果如下:
4.实现多订阅多发布
同理,我们实现搜索后的Medicine添加到当前病人列表中也是跟上面步骤一样,在Events文件夹创建事件类MedicineSentEvent:
MedicineSentEvent.cs:
public class MedicineSentEvent: PubSubEvent<Medicine>{}
在病人详细窗体的PatientDetailViewModel类订阅该事件:
PatientDetailViewModel.cs:
public PatientDetailViewModel(IEventAggregator ea, IMedicineSerivce medicineSerivce){_medicineSerivce = medicineSerivce;_ea = ea;_ea.GetEvent<PatientSentEvent>().Subscribe(PatientMessageReceived);_ea.GetEvent<MedicineSentEvent>().Subscribe(MedicineMessageReceived);}/// <summary>// 接受事件消息函数/// </summary>private void MedicineMessageReceived(Medicine medicine){this.lstMedicines?.Add(medicine);}
在药物列表窗体的MedicineMainContentViewModel也订阅该事件:
MedicineMainContentViewModel.cs:
public class MedicineMainContentViewModel : BindableBase
{IMedicineSerivce _medicineSerivce;IEventAggregator _ea;private ObservableCollection<Medicine> _allMedicines;public ObservableCollection<Medicine> AllMedicines{get { return _allMedicines; }set { SetProperty(ref _allMedicines, value); }}public MedicineMainContentViewModel(IMedicineSerivce medicineSerivce,IEventAggregator ea){_medicineSerivce = medicineSerivce;_ea = ea;this.AllMedicines = new ObservableCollection<Medicine>(_medicineSerivce.GetAllMedicines());_ea.GetEvent<MedicineSentEvent>().Subscribe(MedicineMessageReceived);//订阅事件}/// <summary>/// 事件消息接受函数/// </summary>private void MedicineMessageReceived(Medicine medicine){this.AllMedicines?.Add(medicine);}
}
在搜索Medicine窗体的SearchMedicineViewModel类发布事件消息:
SearchMedicineViewModel.cs:
IEventAggregator _ea;private DelegateCommand<Medicine> _addMedicineCommand;public DelegateCommand<Medicine> AddMedicineCommand =>_addMedicineCommand ?? (_addMedicineCommand = new DelegateCommand<Medicine>(ExecuteAddMedicineCommand));public SearchMedicineViewModel(IMedicineSerivce medicineSerivce, IEventAggregator ea)
{_ea = ea;_medicineSerivce = medicineSerivce;this.CurrentMedicines = this.AllMedicines = _medicineSerivce.GetAllMedicines();}void ExecuteAddMedicineCommand(Medicine currentMedicine){_ea.GetEvent<MedicineSentEvent>().Publish(currentMedicine);//发布消息}
效果如下:
然后我们看看现在Demo项目的事件模型和程序集引用情况,如下图:
我们发现PatientModule和MedicineModule两个模块之间做到了通讯,但却不相互引用,依靠引用PrismMetroSample.Infrastructure程序集来实现间接依赖关系,实现了不同模块之间通讯且低耦合的情况
三.取消订阅事件
Prism还提供了取消订阅的功能,我们在病人详细窗体提供该功能,PatientDetailViewModel加上这几句:
PatientDetailViewModel.cs:
private DelegateCommand _cancleSubscribeCommand;public DelegateCommand CancleSubscribeCommand =>_cancleSubscribeCommand ?? (_cancleSubscribeCommand = new DelegateCommand(ExecuteCancleSubscribeCommand));void ExecuteCancleSubscribeCommand(){_ea.GetEvent<MedicineSentEvent>().Unsubscribe(MedicineMessageReceived);}
效果如下:
四.几种订阅方式设置
我们在Demo已经通过消息聚合器的事件机制,实现订阅者和发布者之间的通讯,我们再来看看,Prim都有哪些订阅方式,我们可以通过PubSubEvent类上面的Subscribe函数的其中最多参数的重载方法来说明:
Subscribe.cs:
public virtual SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive, Predicate<TPayload> filter);
1.action参数
其中action参数则是我们接受消息的函数
2.threadOption参数
ThreadOption类型参数threadOption是个枚举类型参数,代码如下:
ThreadOption.cs
public enum ThreadOption
{/// <summary>/// The call is done on the same thread on which the <see cref="PubSubEvent{TPayload}"/> was published./// </summary>PublisherThread,/// <summary>/// The call is done on the UI thread./// </summary>UIThread,/// <summary>/// The call is done asynchronously on a background thread./// </summary>BackgroundThread
}
三种枚举值的作用:
- PublisherThread:默认设置,使用此设置能接受发布者传递的消息
- UIThread:可以在UI线程上接受事件
- BackgroundThread:可以在线程池在异步接受事件
3.keepSubscriberReferenceAlive参数
默认keepSubscriberReferenceAlive为false,在Prism官方是这么说的,该参数指示订阅使用弱引用还是强引用,false为弱引用,true为强引用:
- 设置为true,能够提升短时间发布多个事件的性能,但是要手动取消订阅事件,因为事件实例对保留对订阅者实例的强引用,否则就算窗体关闭,也不会进行GC回收.
- 设置为false,事件维护对订阅者实例的弱引用,当窗体关闭时,会自动取消订阅事件,也就是不用手动取消订阅事件
4.filter参数
filter是一个Predicate的泛型委托参数,返回值为布尔值,可用来订阅过滤,以我们demo为例子,更改PatientDetailViewModel订阅,代码如下:
PatientDetailViewModel.cs:
_ea.GetEvent<MedicineSentEvent>().Subscribe(MedicineMessageReceived,
ThreadOption.PublisherThread,false,medicine=>medicine.Name=="当归"|| medicine.Name== "琼浆玉露");
效果如下:
五.源码
最后,附上整个demo的源代码:PrismDemo源码
.NET Core 3 WPF MVVM框架 Prism系列之事件聚合器相关推荐
- c#事件的发布-订阅模型_NET Core 3 WPF MVVM框架 Prism系列之事件聚合器
本文将介绍如何在.NET Core3环境下使用MVVM框架Prism的使用事件聚合器实现模块间的通信 一.事件聚合器# 在上一篇 .NET Core 3 WPF MVVM框架 Prism系列之模块化 ...
- prism EventAggregator(事件聚合器)
1 根据<prism 搭建项目>搭建Prism项目 2 新建类库项目Prism.UseEventAggregator,创建MessageSentEvent类,使其继承于PubSubEven ...
- 【Prism 8】事件聚合器(Event Aggregator)
步骤: 继承PubSubEvent<T>创建自定义事件类型 创建事件发送者 创建事件接收者,并订阅指定的事件 1. 事件聚合器 Prism 提供了一种机制,可以实现应用程序中松散耦合组件之 ...
- C# WPF MVVM模式Prism框架下事件发布与订阅
01 - 前言 处理同模块不同窗体之间的通信和不同模块之间不同窗体的通信,Prism提供了一种事件机制,可以在应用程序中低耦合的模块之间进行通信,该机制基于事件聚合器服务,允许发布者和订阅者之间通过事 ...
- 从PRISM开始学WPF(七)MVVM(三)事件聚合器EventAggregator?
从PRISM开始学WPF(七)MVVM(三)事件聚合器EventAggregator? 原文:从PRISM开始学WPF(七)MVVM(三)事件聚合器EventAggregator? 从PRISM开始学 ...
- 疯狂 java轻量级框架_ViewModel从未如此清爽 - 轻量级WPF MVVM框架Stylet
Stylet是我最近发现的一个WPF MVVM框架, 在博客园上搜了一下, 相关的文章基本没有, 所以写了这个入门的文章推荐给大家. Stylet是受Caliburn Micro项目的启发, 所以借鉴 ...
- 事件聚合器 - Caliburn.Micro 文档系列
文章目录 事件聚合器 (Event Aggregator) 入门 创建与生命周期 发布事件 使用自定义线程发布事件 订阅事件 订阅许多事件 多态的订阅者 查询处理程序 协同感知订阅者 任务感知订阅者 ...
- C# WPF MVVM模式Prism框架从零搭建(经典)
01 - 前言 目前最新的PRISM的版本是8.1.97,本节以6.3.0.0 讲解,可以在Github上获取PRISM的源码. Prism Github地址:https://github.com/P ...
- wpf mvvm框架_[Windows] 在 Microsoft Docs 网站中挖掘 MVVM 的各种学习资源
最近写了一些 MVVM 框架的文章,翻了一些 Microsoft Docs 的文档,顺便就对 MVVM 本身来了兴致,想看看更多当年相关的文档.在 MVVM 出现后十多年,我在不同的场合见到过多种 M ...
最新文章
- 黑盒测试方法之边界值分析法
- 交换机SHOW命令,不知道路由器可以参考不·
- 来,锁个痛快(6)—— 与lock相关的视图和简单实验
- POS 收款机资料整理
- 人人都要学一点深度学习(1)- 为什么我们需要它
- HTML5 LocalStorage 本地存储JSON数据
- UNITY 手游(安卓)如何使用C/C++代码
- maven(二)pom文件详解
- Condition.doSignal
- 在Spring MVC Web应用程序中添加社交登录:集成测试
- Android高效异步图片加载框架
- CloudStack 4.3功能前瞻
- 计算机怎么办错误,电脑日志错误怎么办
- 手工打造迅雷9超精简版
- Windows商店的Redis客户端G-dis3
- 2021-09-09
- 微信公众号前端40163解决办法
- 01-Sass 环境搭建与基本语法
- ubuntu16.04安装Preempt RT实时内核
- Unity 游戏加载进度条的开发
热门文章
- magento创建自定义页面 (Bestseller实例) Bestseller products in Magento
- php导出csv文件乱码问题解决方法
- 传参方法:sharedApplication, NSUserDefaults, protocol 和 delegate(实例)
- PHP XML操作类 xml2array -- 含节点属性
- flex3 接受外部参数
- JavaScript 字符串转换数字
- Elasticsearch整理笔记(五)
- 【vim小记】vim的复制粘贴(包括系统剪贴板)
- Tensorflow并行计算:多核(multicore),多线程(multi-thread),计算图分割(Graph Partition)
- LeetCode 75. Sort Colors