2011-06-14 322 views
2

当我第一次开始写WPF的自定义控件,如果我想添加事件处理程序,我将在控件的OnApplyTemplate重写这样做,得到了模板部件后:删除事件处理程序

public void override OnApplyTemplate() { 
    if (addMenu != null) { 
    addMenu.Click -= addMenu_Click; 
    addMenu = null; 
    } 
    addMenu = (MenuItem)Template.FindName("PART_AddMenu", this); 
    addMenu.Click += addMenu_Click; 
} 

但是后来有一天我注意到OnApplyTemplate()并不总是在我期望的时候被调用,也就是说,当控件从视觉树中断开时。也就是说,使用上述技术,事件处理程序不会总是被删除。所以我想出了一个不同的方式:

public MyCustomControl() 
{ 
    Loaded += this_Loaded; 
} 

void this_Loaded(object sender, RoutedEventArgs e) 
{ 
    Unloaded += this_Unloaded; 

    addMenu = (MenuItem)Template.FindName("PART_AddMenu", this); 
    addMenu.Click += addMenu_Click; 
} 

void this_Unloaded(object sender, RoutedEventArgs e) 
{ 
    Unloaded -= this_Unloaded; 

    if (addMenu != null) 
    { 
    addMenu.Click -= addMenu_Click; 
    addMenu = null; 
    } 
} 

这种方式似乎有伎俩。是否每个人都认为这是在自定义控件中连接和删除事件处理程序的更好方法?如果不是,那为什么?

回答

2

这种方法没问题,但您必须明白,您有时会得到卸载事件,您可能不希望事件处理程序解除挂钩。例如,假设您有一个选项卡控件。当切换TabItem时,前面的TabItem的内容全部获得Unloaded,然后在再次选择TabItem时重新加载。对于像Button.Click这样的事物来说,这很好,因为您无法在非活动选项卡上执行此类操作,但即使项目仍然存在,任何不需要将项目加载到可视化树中的事件都将被断开连接。

为什么你觉得你需要清理所有事件处理程序?我意识到有些情况下,他们可以挂在另一个对象的引用上,但这是一种不寻常的情况,通常最好通过清理它们来处理。这里有一些更好的细节:How built-in WPF controls manage their event handlers to an attached event?

+0

你是对的,我想防止挂在引用,因为我已经注意到它发生在性能调整过程中。我不确定具体的原因,所以我宁愿使用新技术来安全地玩。关于你的TabItem例子,我知道发生了什么,但我想不出一个问题。 – HappyNomad 2011-06-15 13:18:54

+0

只有父对象将其事件解析为子对象时才会发生泄漏(从而保留对子对象的第二次引用)。 – 2011-06-15 14:40:54

+0

如果我正确地关注你,那么在上面的例子中,“父对象”是自定义控件,“子对象”是addMenu(模板部分)。由于自定义控件将addMenu存储在专用字段中,因此旧技术(使用OnApplyTemplate)会发生泄漏。 – HappyNomad 2011-06-15 20:32:33