Event Aggregator
Prism Library提供了一种事件机制,支持应用程序中松散耦合组件之间的通信。这种机制基于事件聚合器服务,允许发布者和订阅者通过事件进行通信,但彼此之间仍然没有直接引用。
EventAggregator提供多播发布/订阅功能。这意味着可以有多个发布者引发同一事件,也可以有多个订阅者侦听同一事件。考虑使用EventAggregator跨模块发布事件,以及在业务逻辑代码(如控制器和演示程序)之间发送消息时发布事件。
使用Prism Library创建的事件是类型化事件。这意味着您可以在运行应用程序之前利用编译时类型检查来检测错误。在Prism库中,EventAggregator允许订阅者或发布者定位特定的EventBase。事件聚合器还允许多个发布者和多个订阅者,如下图所示。
IEventAggregator
EventAggregator类是作为一个服务在容器中提供的,可以通过IEventAggregatorinterface来检索。 事件聚合器负责定位或构建事件,并在系统中保存事件的集合。
public interface IEventAggregator
{
TEventType GetEvent<TEventType>() where TEventType : EventBase;
}
如果还没有构造事件,则EventAggregator在第一次访问时构造该事件。 这使发布者或订阅者无需确定事件是否可用。
PubSubEvent
连接发布者和订阅者的实际工作是由PubSubEvent类完成的。 这是Prism库中包含的EventBase类的唯一实现。 该类维护订阅者列表并处理向订阅者分派事件。
PubSubEvent类是一个泛型类,它要求将有效负载类型定义为泛型类型。 这有助于在编译时强制发布者和订阅者为成功的事件连接提供正确的方法。 下面的代码显示了PubSubEvent类的部分定义。
Creating an Event
PubSubEvent<TPayload>是应用程序或模块特定事件的基类。 TPayLoad是事件有效载荷的类型。 有效负载是将在发布事件时传递给订阅者的参数。
例如,下面的代码显示了TickerSymbolSelectedEvent。 有效载荷是一个包含公司符号的字符串。 注意这个类的实现是如何为空的。
public class TickerSymbolSelectedEvent : PubSubEvent<string>{}
备注:在复合应用程序中,事件经常在多个模块之间共享,因此它们被定义在一个公共的地方。 通常的做法是在共享程序集(如“核心”或“基础设施”项目)中定义这些事件。
Publishing an Event
发布者通过EventAggregator检索事件并调用Publish方法引发事件。 要访问EventAggregator,你可以通过在类构造函数中添加一个IEventAggregator类型的参数来使用依赖注入。
public class MainPageViewModel
{
IEventAggregator _eventAggregator;
public MainPageViewModel(IEventAggregator ea)
{
_eventAggregator = ea;
}
}
下面的代码演示了如何发布TickerSymbolSelectedEvent。
_eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Publish("STOCK0");
Subscribing to Events
订阅者可以使用PubSubEvent类上可用的Subscribe方法重载之一注册事件。
public class MainPageViewModel
{
public MainPageViewModel(IEventAggregator ea)
{
ea.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews);
}
void ShowNews(string companySymbol)
{
//implement logic
}
}
有几种订阅PubSubEvents的方法。 使用以下标准来帮助确定哪个选项最适合你的需求:
1.如果您需要能够在接收到事件时更新UI元素,请在UI线程上订阅接收事件。
2.如果需要筛选事件,请在订阅时提供筛选委托。
3.如果您对事件有性能问题,请考虑在订阅时使用强引用委托,然后手动从PubSubEvent取消订阅。
4.如果以上都不适用,请使用默认订阅。
以下部分将描述这些选项。
Subscribing on the UI Thread
通常,订阅者需要更新UI元素以响应事件。 在WPF中,只有UI线程可以更新UI元素。
默认情况下,订阅者接收发行者线程上的事件。 如果发布者从UI线程发送事件,则订阅者可以更新UI。 但是,如果发布者的线程是后台线程,那么订阅者可能无法直接更新UI元素。 在这种情况下,订阅者将需要使用Dispatcher类在UI线程上调度更新。
Prism库提供的PubSubEvent可以通过允许订阅者在UI线程上自动接收事件来提供帮助。 订阅者在订阅期间表示这一点,如下面的代码示例所示。
public class MainPageViewModel
{
public MainPageViewModel(IEventAggregator ea)
{
ea.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews, ThreadOption.UIThread);
}
void ShowNews(string companySymbol)
{
//implement logic
}
}
ThreadOption有以下选项:
1.PublisherThread:使用这个设置来接收发布者线程上的事件。 这是默认设置。
2.BackgroundThread:使用此设置在. net框架线程池线程上异步接收事件。
3.UIThread:使用这个设置在UI线程上接收事件。
为了让PubSubEvent在UI线程上发布到订阅者,EventAggregator必须首先在UI线程上构造。
Subscription Filtering
订阅者可能不需要处理已发布事件的每个实例。 在这些情况下,订阅者可以使用筛选器参数。 过滤参数类型为System.Predicate<TPayLoad>,是一个委托,在发布事件时执行,以确定发布事件的有效负载是否匹配调用订阅者回调所需的一组标准。 如果有效负载不满足指定的条件,则不执行订阅者回调。
这个筛选器通常以lambda表达式的形式提供,如下面的代码示例所示。
public class MainPageViewModel
{
public MainPageViewModel(IEventAggregator ea)
{
TickerSymbolSelectedEvent tickerEvent = ea.GetEvent<TickerSymbolSelectedEvent>();
tickerEvent.Subscribe(ShowNews, ThreadOption.UIThread, false,
companySymbol => companySymbol == "STOCK0");
}
void ShowNews(string companySymbol)
{
//implement logic
}
}
备注:Subscribe方法返回Prism.Events.SubscriptionToken类型的订阅令牌,可用于在以后删除对事件的订阅。当您使用匿名委托或lambda表达式作为回调委托时,或者当您使用不同的过滤器订阅相同的事件处理程序时,此令牌特别有用
备注:不建议从回调委托中修改有效负载对象,因为多个线程可能同时访问有效负载对象。 您可以让有效负载是不可变的,以避免并发性错误。
Subscribing Using Strong References
如果您在短时间内引发多个事件,并注意到它们的性能问题,则可能需要使用强委托引用进行订阅。 如果您这样做,那么您将需要在取消订阅时手动取消订阅事件。
默认情况下,PubSubEvent在订阅时维护对订阅者的处理程序和过滤器的弱委托引用。 这意味着PubSubEvent持有的引用将不会阻止订阅者的垃圾收集。 使用弱委托引用减轻了订阅者取消订阅的需要,并允许适当的垃圾收集。
但是,维护这个弱委托引用要比相应的强引用慢。 对于大多数应用程序,这种性能不会很明显,但是如果您的应用程序在短时间内发布了大量的事件,那么您可能需要对PubSubEvent使用强引用。 如果您确实使用了强委托引用,那么您的订阅者应该取消订阅,以便在不再使用订阅对象时对其进行适当的垃圾收集。
要使用强引用进行订阅,请在subscribe方法上使用keepSubscriberReferenceAlive参数,如下面的代码示例所示。
public class MainPageViewModel
{
public MainPageViewModel(IEventAggregator ea)
{
bool keepSubscriberReferenceAlive = true;
TickerSymbolSelectedEvent tickerEvent = ea.GetEvent<TickerSymbolSelectedEvent>();
tickerEvent.Subscribe(ShowNews, ThreadOption.UIThread, keepSubscriberReferenceAlive,
companySymbol => companySymbol == "STOCK0");
}
void ShowNews(string companySymbol)
{
//implement logic
}
}
keepSubscriberReferenceAlive参数的类型是bool:
1.当设置为true时,事件实例将保持对订阅者实例的强引用,从而不允许对其进行垃圾收集。 有关如何取消订阅的信息,请参阅本主题后面的取消订阅事件一节。
2.当设置为false(省略此参数时的默认值)时,事件维护对订阅者实例的弱引用,从而允许垃圾收集器在没有对订阅者实例的其他引用时处置该实例。 当收集订阅服务器实例时,将自动取消订阅事件。
Unsubscribing from an Event
public class MainPageViewModel
{
TickerSymbolSelectedEvent _event;
public MainPageViewModel(IEventAggregator ea)
{
_event = ea.GetEvent<TickerSymbolSelectedEvent>();
_event.Subscribe(ShowNews);
}
void Unsubscribe()
{
_event.Unsubscribe(ShowNews);
}
void ShowNews(string companySymbol)
{
//implement logic
}
}
下面的代码示例演示如何使用订阅token取消订阅。 token作为来自Subscribe方法的返回值提供。
public class MainPageViewModel
{
TickerSymbolSelectedEvent _event;
SubscriptionToken _token;
public MainPageViewModel(IEventAggregator ea)
{
_event = ea.GetEvent<TickerSymbolSelectedEvent>();
_token = _event.Subscribe(ShowNews);
}
void Unsubscribe()
{
_event.Unsubscribe(_token);
}
void ShowNews(string companySymbol)
{
//implement logic
}
}
以下是根据文章,写的一个demo,仅供参考学习