事件聚合器
约 1539 字大约 5 分钟
Caliburn.MicroWPFC#.net
2025-06-19
入门
如前所述,CM 提供了事件聚合器的实现。此实现实现了 IEventAggregator 接口,但是,如果需要可以提供自己的实现。请花一些时间熟悉 IEventAggregator 签名。
public interface IEventAggregator {
bool HandlerExistsFor(Type messageType);
void Subscribe(object subscriber);
void Unsubscribe(object subscriber);
void Publish(object message, Action<Action> marshal);
}创建和生命周期
要正确使用 EventAggregator,它必须作为应用程序级服务存在。这通常是通过将 EventAggregator 实例创建为单例来实现的。建议使用依赖注入来获取对实例的引用,尽管不强制这样做。下面的示例详细介绍了如何创建 EventAggregator 的实例,将其添加到 Caliburn.Micro 附带的 IoC 容器并在 ViewModel 中请求它。
// Creating the EventAggregator as a singleton.
public class Bootstrapper : BootstrapperBase {
private readonly SimpleContainer _container =
new SimpleContainer();
// … Other Bootstrapper Config
protected override void Configure(){
_container.Singleton<IEventAggregator, EventAggregator>();
}
// … Other Bootstrapper Config
}
// Acquiring the EventAggregator in a viewModel.
public class FooViewModel {
private readonly IEventAggregator _eventAggregator;
public FooViewModel(IEventAggregator eventAggregator) {
_eventAggregator = eventAggregator;
}
}请注意,在上面的代码中使用了 Bootstrapper,特别是配置方法。不需要将 EventAggregator 连接到特定位置,只需确保在首次请求之前创建它即可。
发布事件
一旦获得了 EventAggregator 实例的引用就可以开始发布事件了。我们称之为事件或消息以区分.Net 事件,可以是您喜欢的任何对象。不需要以任何特定的方式构建您的活动。正如在下面的示例中所看到的,Publish 方法可以接受任何从 System.Object 派生的实体,并且很乐意将其发布给任何感兴趣的订阅者。
public class FooViewModel {
private readonly IEventAggregator _eventAggregator;
public FooViewModel(IEventAggregator eventAggregator) {
_eventAggregator = eventAggregator;
_eventAggregator.PublishOnUIThread(new object());
_eventAggregator.PublishOnUIThread("Hello World");
_eventAggregator.PublishOnUIThread(22);
}
}按照惯例,EventAggregator 在 UI 线程上发布(使用 PublishOnUIThread() 方法)。您可以在每次发布时覆盖此设置。考虑下面的代码,它发布在后台线程上提供的消息。
_eventAggregator.Publish(new object(), action => { Task.Factory.StartNew(action); });订阅活动
任何实体都可以通过 Subscribe 方法将自身提供给 EventAggregator 来订阅任何事件。为了保持此功能易于使用,CM 提供了一个特殊的接口 (IHandle),它将订阅者标记为对给定类型的事件感兴趣。
IHandle<TMessage> : IHandle {
void Handle<TMessage>(TMessage message);
}请注意,实现上面的接口必须实现方法 Handle(T message),其中 T 是您指定感兴趣的消息类型。发布匹配的事件类型时将调用此方法。
public class FooViewModel : IHandle<object> {
private readonly IEventAggregator _eventAggregator;
public FooViewModel(IEventAggregator eventAggregator) {
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
}
public void Handle(object message) {
// Handle the message here.
}
}订阅许多活动
单个实体想要侦听多种事件类型的情况并不罕见。由于我们使用泛型,这就像向订阅者添加第二个 IHandle 接口一样简单。请注意,Handle 方法现在已被新的 Event 类型重载。
public class FooViewModel : IHandle<string>, IHandle<bool> {
private readonly IEventAggregator _eventAggregator;
public FooViewModel(IEventAggregator eventAggregator) {
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
}
public void Handle(string message) {
// Handle the message here.
}
public void Handle(bool message) {
// Handle the message here.
}
}多态订阅者
Caliburn.Micro 的 EventAggregator 尊重多态性。当选择要调用的处理程序时,EventAggregator 将触发其事件类型可从正在发送的事件分配的任何处理程序。这带来了很大的灵活性并有助于重用。
public class FooViewModel : IHandle<object>, IHandle<string> {
private readonly IEventAggregator _eventAggregator;
public FooViewModel(IEventAggregator eventAggregator) {
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
_eventAggregator.PublishOnUIThread("Hello");
}
public void Handle(object message) {
// This will be called
}
public void Handle(string message) {
// This also
}
}在上面的示例中,由于 String 派生自 System.Object,因此当发布 String 消息时,将调用两个处理程序。
查询处理程序
当订阅者被传递到 EventAggregator 时,它被分解为一个称为 Handler 的特殊对象,并维护一个弱引用。CM 提供了一种机制来查询 EventAggregator 以查看给定的事件类型是否有任何处理程序,这在假定至少存在一个处理程序的特定场景中非常有用。
public class FooViewModel : IHandle<object> {
private readonly IEventAggregator _eventAggregator;
public FooViewModel(IEventAggregator eventAggregator) {
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
}
public void Handle(object message){
if (_eventAggregator.HandlerExistsFor(typeof(SpecialMessageEvent))){
_eventAggregator.PublishOnUIThread(new SpecialEventMessage(message));
}
}
}任务感知订阅者
Caliburn.Micro 还为基于任务的订阅者提供支持,其中需要协程的异步功能,但以更轻量的方式。要利用此功能,请实现 IHandleWithTask 接口,如下所示。
public interface IHandleWithTask<TMessage> : IHandle {
Task Handle(TMessage message);
}任何实现上述接口的订阅者都可以以基于任务的方式处理事件。
public class FooViewModel : Screen, IHandleWithTask<object> {
private readonly IEventAggregator _eventAggregator;
public FooViewModel(IEventAggregator eventAggregator) {
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
}
public Task Handle(object message) {
return Task.Factory.StartNew(() => message);
}
}取消订阅和泄露
标准.Net 事件的问题是它们容易发生内存泄漏。我们通过维持对订阅者的弱引用来避免这种情况。如果唯一引用订阅者的是 EventAggregator,那么它将被允许超出范围并最终被垃圾收集。但是,我们仍然提供了一种明确的取消订阅方式,以允许进行条件处理,如下所示
public class FooViewModel : Screen, IHandle<object> {
private readonly IEventAggregator _eventAggregator;
public FooViewModel(IEventAggregator eventAggregator) {
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
}
public void Handle(object message) {
// Handle the message here.
}
protected override void OnActivate() {
_eventAggregator.Subscribe(this);
base.OnActivate();
}
protected override void OnDeactivate(bool close) {
_eventAggregator.Unsubscribe(this);
base.OnDeactivate(close);
}
}自定义结果处理
在更复杂的场景中,可能需要覆盖具有结果的处理程序的默认处理。在这种情况下,可以将现有的实现替换为您自己的实现。首先我们创建一个新的处理程序类型。
IHandleAndReturnString<T> : IHandle {
string Handle<T>(T message);
}接下来我们创建新的结果处理器。这可以在引导程序中配置。
var standardResultProcesser = EventAggregator.HandlerResultProcessing;
EventAggregator.HandlerResultProcessing = (target, result) =>{
var stringResult = result as string;
if (stringResult != null)
MessageBox.Show(stringResult);
else
standardResultProcesser(target, result);
};现在,每当处理事件返回一个字符串时,它都会被捕获并显示在 MessageBox 中。如果结果不可从字符串分配,新处理程序将回退到默认实现。然而,值得注意的是,此功能并不是为请求/响应使用而设计的,这样对待它肯定会在发布时造成瓶颈。