Prism事件聚合-订阅者未触发

19

我正在实现 Prism 的事件聚合功能。我有几个模块,每个模块都需要订阅事件来通知它们何时被请求。我最开始在 Shell 中同时使用订阅和发布的简单示例没有问题。但是,当我将订阅器移到我的模块中时,它们就不再触发了。更奇怪的是,它有时会起作用,但这些情况下我都在断点中等待。因此,我认为这可能是一种竞态条件,但我不明白原因。

假设: 我不需要在任何地方设置 IEventAggregator - 比如在 IoC 容器中注册?这是 Prism 内置的,所以我只有一个事件聚合器的实例,对吗?

因此,问题基本上是我应该在何时何处设置我的订阅器。是否需要按照特定的顺序执行某些操作等等?在我的简化示例中,我有一个名为 MyModule 的模块。Bootstrapper 将 MyModule 添加到目录中 - 使其被初始化:

catalog.AddModule(typeof(MyModule));

MyModule将存储聚合器并使用它来订阅MyModuleRequestedEvent。 它还使用菜单注册表在应用程序菜单中注册。 这样做的想法是,最终单击菜单应该触发事件-通知MyModule已被请求。 然后我希望MyModule负责进一步确定要做什么。

public MyModule(IEventAggregator aggregator, IApplicationMenuRegistry menu)
{
    _applicationMenu = menu;
    _aggregator = aggregator;
}

public void Initialize()
{
    var evnt = _aggregator.GetEvent<MyModuleRequestedEvent>();
    evnt.Subscribe(MyModuleRequested);
    _applicationMenu.RegisterMenuItem("MyModule", evnt);
}

public void MyModuleRequested(bool b)
{
    MessageBox.Show("MyModule requested");
}

现在,我的shell中有一个按钮,将会发布这个事件。当解决时,shell获取相同的(?)事件聚合器。

public Shell(IEventAggregator aggregator)
{
    InitializeComponent();
    var evnt = aggregator.GetEvent<MyModuleRequestedEvent>();
    EventTriggerButton.Click += (s, e) => evnt.Publish(true);
}

注:

  • 已验证事件已发布。在 Shell 中添加订阅者也将使该订阅者接收事件。
  • 再次强调,MyModule 中的订阅者没有被触发。然而,在某些情况下,它确实被触发了。
  • 我不使用事件的输入。似乎你需要有一些输入类型,所以我只是用了一个虚拟布尔值。我可以摆脱它吗?

2
听起来你可能得到了两个EventAggregators;使用调试器为它们创建一个对象ID,这样你就可以检查并让我们知道。 - GraemeF
1
谢谢你的提示!我已经做了,事实上使用的是相同的EventAggregator来订阅模块和发布。首先进入Shell构造函数。进行了“创建对象ID”。得到1#。然后在MyModule.Initialize中打断点,并验证聚合器的ID为1#。当点击按钮触发发布时,我也看到使用的聚合器是1#。 - stiank81
2个回答

33

Prism事件聚合器使用弱引用来链接事件。这是为了防止事件处理程序导致的内存泄漏。

一旦模块初始化程序被运行,它就会被处理掉,所以您的事件处理程序在事件触发之前已经被销毁了。您可以使用Subscribe的重载告诉Prism保留事件处理程序。

evnt.Subscribe(MyModuleRequested, true);

作为一种模式,我倾向于将任何事件订阅者放在一个单独的类中,并从模块的“Initialize”方法调用该类。这样事件仍然保持活动状态,但是在模块被销毁时它们是分离的。


太好了!你的模式很有意义 - 所以我会把一个最小的事件处理程序放在一个单独的类中。谢谢..! - stiank81
我也遇到了这个问题,将一个视图添加到Prism区域会阻止我的模块被处理,我在区域注册方面做错了什么吗?您是否知道有关推荐的模块生命周期和注册视图的任何文档? - BenCr

2

所以,我有一个理论,但现在没有时间测试它。明天会去测试。

问题:将模块添加到ModuleCatalogue中是否会使它们保持活动状态?我认为应该是这样的。因此,MyModule应该保持活动状态,然后在发布事件时被触发。

protected override IModuleCatalog GetModuleCatalog()
{
    var catalog = new ModuleCatalog();
    catalog.AddModule(typeof(MyModule));
    return catalog;
}

然而,如果这不能保持模块的活性,那么它很明显将难以响应事件。模块对象死亡,但它没有取消订阅 - 因此我会在EventAggregator列表中看到订阅者,但订阅者已经不在了。另外,我提到它确实偶尔会工作 - 如果垃圾收集器在事件触发之前没有时间清理垃圾的话,就会出现这种情况。
听起来像是这种情况吗?如果是的话 - 我还没有想到解决方案,所以欢迎您在另一个回复线程中提出建议。
那么,ModuleCatalog究竟是什么?只是为初始化而保留的列表,然后被丢弃吗?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接