弱订阅通过反射获得的事件

本文关键字:事件 反射 | 更新日期: 2023-09-27 18:02:52

我正在使用MVVM模式在WPF中制作MessageBox控件。这个MessageBox将在具有不同外观的多个应用程序中使用,所以理想情况下,我希望保持代码远离代码隐藏。

我试图使MessageBox出现在事件引发时,在MessageBox的声明中指定。

例如,这将在MessageBox应该出现的窗口的XAML中指定。
<dialog:MessageBox
    ShowOnEvent="EventRaised"
    EventContext="{Binding}"
    Message="I am a message box"
    IconType="Warning"
    ButtonsType="YesNo" />

目前的工作方式:在MessageBox ViewModel中,我使用反射来获取事件的EventInfo,然后直接订阅:

if (eventContext != null && showOnEvent != string.Empty)
{
    EventInfo eventInfo = eventContext.GetType ().GetEvent (showOnEvent);
    if (eventInfo != null)
    {
        eventInfo.AddEventHandler (eventContext, eventHandler);
    }
    else
    {
        Debug.WriteLine (string.Format ("Dialog: Couldn't find event {0} on {1}, check event name.", showOnEvent, eventContext));
    }
}

显示事件触发时的MessageBox,如预期的那样。

然而,事件处理程序意味着MessageBox ViewModel在主窗口的View被处置时不会被GC。这意味着,如果为主窗口创建了另一个视图,则会创建另一个MessageBox,因此,如果引发事件,则会显示两个MessageBox

我试图通过使用WeakEventManager来解决这个问题,但是弱事件模式文档指定WeakEventManager的实现应该只处理一个事件-这意味着我不能使用事件名称作为字符串属性并使用它订阅ShowOnEventEventManager

谁有什么最好的方法来做这件事?

弱订阅通过反射获得的事件

使用弱事件并不能解决您的问题,因为在GC决定运行之前您不会取消订阅(除非您显式调用GC.Collect())。正如Will在评论中建议的那样,你可以尝试在适当的时候取消订阅,或者更容易的是在显示自己之前让你的MessageBox检查它是否为IsLoaded

我不会担心你的MessageBox中的代码滞后,除非你知道为什么它会损害它的可重用性。让MessageBox代码直接引用它的视图是可以的,只要MessageBox的消费者有一个对mvc友好的API。

PRISM EventAggregator默认使用弱引用实现事件。你需要能够修改发布事件的代码来在你的应用程序中实现这一点。

在链接页面上有适当的代码示例以及必要的流程图。事件聚合器使用起来相当简单:您可以在任意位置使用具有强类型有效负载的PublishSubscribe。(免费下载)