Prism Demo系列(十五) Prism架构剖析:15-FilteringEvents
和上篇博客项目结构和内容几乎一样,唯一不同的地方是ModuleB项目的 MessageListViewModel.cs稍微改动了一下:
using Prism.Events;
using Prism.Mvvm;
using System.Collections.ObjectModel;
using UsingEventAggregator.Core;
namespace ModuleB.ViewModels
{
public class MessageListViewModel : BindableBase
{
IEventAggregator _ea;
private ObservableCollection<string> _messages;
public ObservableCollection<string> Messages
{
get { return _messages; }
set { SetProperty(ref _messages, value); }
}
public MessageListViewModel(IEventAggregator ea)
{
_ea = ea;
Messages = new ObservableCollection<string>();
//1、filter表示接受发件人Brian发过来的邮件。但是,ModuleA没有发件人Brian,所以下面这行代码收不到任何消息。
//_ea.GetEvent<MessageSentEvent>().Subscribe(MessageReceived, ThreadOption.PublisherThread, false, (filter) => filter.Contains("Brian"));
//2、类似的,filter.Contains("ABCD")也收不到消息。
//_ea.GetEvent<MessageSentEvent>().Subscribe(MessageReceived, ThreadOption.PublisherThread, false, (filter) => filter.Contains("ABCD"));
//3、true,表示拒绝接受发件人ABCD发送过来的任何消息。
_ea.GetEvent<MessageSentEvent>().Subscribe(MessageReceived, ThreadOption.PublisherThread, false, (filter) => filter.Contains("ABCD"));
}
private void MessageReceived(string message)
{
Messages.Add(message);
}
}
}
这个Demo比较简单,下面参考了一篇博客,这篇博客做了一个邮件系统:
https://www.cnblogs.com/li-xiao/archive/2011/04/20/2022962.html
Prism之使用EventAggregation进行模块间通信
在开发Silverlight程序的时候,经常需要在不同的组件间进行通信。比如点击一个button,可能就需要改变另一个控件的内容。比较直接的办法是使用事件,当然使用MVVM的时候也可以使用command,还可以定义一些全局的变量来保存一些信息等。
Prism提供了几种用于组件间通信的途径,可以使用RegionContext使不同的视图共享数据,也可以借助于容器的力量来使用共享的service来进行通信,或者使用command等。除此之外,Prism还提供了一种基于事件的多播发布/订阅方式的通信机制,使不同的组件之间能够以一种松散耦合的方式来进行通信。这就是本文要介绍的事件聚合(Event Aggregation)。
事件聚合的过程有点像收听广播,首先要有个固定的频率,然后内容就会在这个频率上广播出去,至于有没有人收听,广播电台是不知道的,它只是把内容播送了出去。而其他的人想听广播也不用跑到广播电台,只要知道频率,收听这个频率就可以了。联系广播电台和听众的就是这个频率。
在事件聚合的过程中,事件发布方(publisher)相当于广播电台,事件接收方(Subscriber)相当于听众,而事件自然就相当于频率了。
使用Event Aggregation很简单,只需要知道一个接口和一个类基本上就足够了。接口是IEventAggregator,类是CompositePresentationEvent。
要想发布或订阅事件,自然得先要有事件,所以第一件工作就是要定义事件。Prism提供了一个事件基类CompositePresentationEvent<TPayload>,自定义的事件只需要继承这个类就可以了,泛型代表的是事件发生过程中需要传递的参数类型。如:
1 2 3 |
|
上面定义了一个事件,用于在收到新邮件时使用,传递的参数是一个邮件的ViewModel。
使用的时候也很简单,使用IEventAggregator接口中的GetEvent<TEventType>方法来获取事件,然后要么发布出去,要么订阅一下就可以了。
下面是当收到一封新的邮件的时候,发布事件的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
可以看到我们直接在构造函数中传递了IEventAggregator类型的参数,如果使用Prism来搭建Silverlight程序的话,那么在默认的Bootstrapper中会在容器中添加IEventAggregator的实例,所以并不需要我们做其它更多的工作。如果对Prism或Bootstrapper不太了解的话,可以参考这两篇文章(Prism简介,Bootstrapper)。
下面是订阅ReceiveNewEmail事件的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
这样,发布出去的事件马上就可以被接收到,而且两个组件只是依赖于事件,彼此之间是松散耦合的。
事件可以订阅,也可以退订,甚至可以有选择地接受某些特定的事件。下面以一个模拟的简单的邮箱客户端来演示一下Event Agregation的使用场景。
如图所示,左边是邮件列表,会有一个定时器每隔两秒钟接收到一封邮件,这时邮箱客户端会更新邮件列表,点击左边的列表,会在右边显示邮件的内容。如果点击’将该发信人加入黑名单’,则不会再接受来自该发件人的邮件,如果点击断开连接,则停止接受邮件,再次点击会继续接收邮件。需求大致就是这样了。
首先在启动程序的时候开启一个定时器,每隔两秒钟会接收一封邮件,并发布事件通知有新邮件:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
MailList组件会订阅这个事件,并对邮件列表进行更新:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
当点击左边的邮件列表的时候,会在右边的MailContent组件中显示该邮件的信息,这个过程也是通过Event Aggregation来完成的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
当点击时,会进入相应的Command逻辑,而MailContent则订阅了ViewEmailEvent,并将传递过来的MailViewModel显示出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
当点击将该发信人加入黑名单按钮时,会发布AddRefuseSenderEvent,而接收到这一事件的MailList组件则会更新黑名单,这样filter就会过滤掉黑名单中已经存在的发件人的邮件:
1 2 3 4 5 6 7 |
|
如果点击了断开连接或重新连接的话,会发布一个ConnectOrDisconnectMailServerEvent事件。Prism的事件基类并不支持不带参数的事件,也就是说没有办法创建一个不需要传参的事件。所以这里我们使用了object类型作为参数类型,在传递参数的时候直接传了个null过去。
1 2 3 |
|
而当MailList接收到该事件的时候,首先判断一下是否已经订阅了ReceiveNewEmailEvent事件,如果订阅了就退订,如果没有订阅就重新订阅。这样来达到开启或关闭接收邮件的目的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
|
由于EventAggregation并不需要建立在Prism装配的程序上,为了操作简便,所以并没有使用Prism来管理这个程序,当然也就没有使用容器。所以我用了一个static的全局变量来保存了一个IEventAggregator的实例。
本文为了演示,所以大量地使用了Event Aggregation,希望大家在工作中要仔细斟酌使用,虽然用起来很灵活,但是如果事件太多的话,也会让人有无从下手的感觉,增加维护的难度。
示例代码可以在这里下载
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- linux编译运行c++程序
- Python的运算符 (下)
- 深入理解JVM(③)Java的锁优化
- Spring 基于 Java 的配置 - 如何不用Beans.xml照样描述bean之间的依赖关系
- Spring 中的事件处理
- Vim安装插件管理器Vundle
- Ubuntu下为vim安装YouCompleteMe插件
- 单机部署k8s
- VIM个性化配置(一)
- Django+Vue开发生鲜电商平台之6.使用Vue实现商品类别和商品数据前台显示
- 【程序源代码】基于Python开发的Markdown在线文档系统
- 深度学习Pytorch检测实战 - Notes - 第4章 两阶经典检测器:Faster RCNN
- Numpy中的两个乱序函数
- PerfDog4.0探索,支持用户自建web云
- 跨帐号访问COS资源