【问题标题】:When to remove event handlers from an object?何时从对象中删除事件处理程序?
【发布时间】:2013-08-23 22:44:29
【问题描述】:

所以我在对象的加载事件中注册了一些事件处理程序。

tv.PreviewMouseDown += new MouseButtonEventHandler(SignalScrollViewer_PreviewMouseDown);

但我有两个问题。

  1. 如果加载了两次,再次尝试添加事件处理程序会不会有问题?
  2. 我应该如何处理注销事件?它会在销毁时自动处理注销,还是我需要在某些事件(如卸载或其他情况下)处理它?

【问题讨论】:

  • 如果在事件触发时仍需要它做某事,则无需取消注册。 event 只是一个 special delegate ,它包含一个调用列表,它只是对象的成员以及其他类型的成员(属性、字段、方法……)。因此,当您的对象被释放时,它将与对象一起消失。

标签: c# wpf xaml


【解决方案1】:
  1. 是的,这将导致另一个订阅导致处理程序执行两次。您可以在已加载的处理程序中删除已加载的处理程序

    MSDN:

    由于用户启动的系统主题更改,控件上可能会同时引发 Loaded 和 Unloaded。主题更改会导致控件模板和包含的可视化树失效,进而导致整个控件卸载和重新加载。因此,不能假定仅在通过导航到页面首次加载页面时才发生 Loaded。

  2. 如果对象消失了,它就无法引发任何事件,因此无需对此做任何事情。并且处理程序不会使对象保持活动状态(反之亦然)。

【讨论】:

  • 提供指向该 MSDN 页面的链接可能会很好。
  • 如何检查事件是否已经订阅了处理程序以避免添加两次?当它不存在时,我可以只使用 ParentTreeView.PreviewMouseDown -= new MouseButtonEventHandler(SignalScrollViewer_PreviewMouseDown) 吗?还是我需要进行检查?
  • @JamesJoshuaStreet:你不需要先检查,你可以预防性地取消订阅。但是,如前所述,我会在处理程序本身中取消订阅,这实际上是一次处理订阅。
  • @JamesJoshuaStreet: Here's one.
【解决方案2】:

如果加载了两次,再次尝试添加事件处理程序会不会有问题?

该事件将被多次订阅。您可能需要处理这种情况。

我应该如何处理取消注册事件?它会在销毁时自动处理注销,还是我需要在某些事件(如卸载或其他情况下)处理它?

您可以在 Unloaded 或类似中取消注册它。通常,您只需要在您订阅的事件的对象超出您的生命周期时执行此操作。如果它是您自己内部的对象(即:从用户控件订阅用户控件内按钮上的事件),则不需要取消订阅。

【讨论】:

  • HB 为Loaded 多次触发提供了一个非常有说服力的案例。
  • @BenVoigt 虽然它会在再次加载之前卸载 -
  • 是的,但您的回答建议不要这样做。 (最后一句,指出多次调用Loaded时未能更新)
【解决方案3】:

通常从构造函数内部订阅事件,即在 tv 对象创建后立即订阅。如果将它们放在一起,就不可能多次订阅同一个tv 对象。

【讨论】:

  • 如果您订阅另一个对象的事件怎么办?这种情况不常见吗?我想让我的滚动查看器订阅树视图的 mousedown 事件,以便它可以处理它们来代替树视图。
  • @James:这就是我所说的场景。包含用户控件或窗口的构造函数创建滚动查看器和树视图,并将它们附加在一起。订阅事件不是(根据我的 WinForms 经验——WPF 是否完全不同?)需要等待布局首先发生。
  • 我认为在我的情况下,用于查找要订阅的父级的可视化树连接在布局完成加载之前不可用。我知道我一开始尝试将它放在构造函数中,但我无法让 Visualtreehelper 找到我想要的父对象。
【解决方案4】:

想要在 Loaded 中注册事件处理程序并在 Unloaded 中删除它们,因此处理事件的任何对象都将具有定义的生命周期。因此,人们希望每个 Loaded 事件在控件显示时只发生一次,然后在控件从视线中移开时发生 Unloaded 事件。

但是,根据您的布局,Loaded 可能会被提升多次:Expander 会导致 Loaded 被提升两次,但在 Expander 内的控件上仅卸载一次,并且 TabControl 可能会为不同的 TabItems(不同的数据)重用相同的控件,而无需在两者之间提高 Loaded 或 Unloaded。

我找到了解决这个问题的两种方法:使用 DataTemplates(这至少在 TabControl 情况下有帮助),或者使用 DataContextChanged 事件来注册/取消注册事件,这是确保接收到的对象的好方法事件始终是当前的 DataContext(即 ViewModel)。

您可能还想查看以下链接,了解有关附加和删除事件处理程序和其他行为的更多信息:http://wpfglue.wordpress.com/2009/12/11/the-sticky-component-framework/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-03
    • 1970-01-01
    • 2021-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多