Wpf 从自定义控件中删除事件处理程序';s模板部件

Wpf 从自定义控件中删除事件处理程序';s模板部件,wpf,custom-controls,Wpf,Custom Controls,当我第一次开始编写WPF自定义控件时,如果我想添加一个事件处理程序,我会在获取模板部分后,在控件的OnApplyTemplate覆盖中这样做: public void override OnApplyTemplate() { if ( addMenu != null ) { addMenu.Click -= addMenu_Click; addMenu = null; } addMenu = (MenuItem)Template.FindName("PART_AddM

当我第一次开始编写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;
  }
}

这种方法似乎奏效了。大家都同意这是在自定义控件中连接和删除事件处理程序的更好方法吗?如果没有,那么原因是什么?

此方法很好,但您必须了解,在您可能不希望事件处理程序取消挂钩的情况下,您会获得卸载的事件。例如,假设您有一个选项卡控件。切换TabItems时,会卸载上一个TabItem all的内容,然后在再次选中该TabItem时重新加载。这适用于按钮。单击按钮,因为您无法在非活动选项卡上执行此类操作,但即使项目仍然存在,也会断开不需要将项目加载到可视化树中的任何事件的连接

为什么您觉得需要清理所有事件处理程序?我意识到,在某些情况下,它们可以挂起另一个对象的引用,但这是一个不寻常的情况,通常最好在以这种方式使用时将它们清理干净。这里有一些更好的细节:

WPF控件(如ComboBox)使用
OnTemplateChangedInternal()
方法取消注册在
OnApplyTemplate()中注册的事件。您可以重写该方法,因为它是PresentationFramework dll的内部方法,但是您可以重写受保护的
OnTemplateChanged()
方法来执行相同的操作-它由控件基类中的
OnTemplateChangedInternal()
调用

以下是可以进入自定义控件的示例代码:

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            EditableTextBoxSite = GetTemplateChild("PART_EditableTextBox") as TextBox;
            EditableTextBoxSite.TextChanged += new TextChangedEventHandler(this.OnEditableTextBoxTextChanged);
            this.EditableTextBoxSite.PreviewTextInput -= new TextCompositionEventHandler(this.OnEditableTextBoxPreviewTextInput);
        }

        protected override void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate)
        {
            base.OnTemplateChanged(oldTemplate, newTemplate);
            if (this.EditableTextBoxSite == null)
                return;
            this.EditableTextBoxSite.TextChanged -= new TextChangedEventHandler(this.OnEditableTextBoxTextChanged);
            this.EditableTextBoxSite.PreviewTextInput -= new TextCompositionEventHandler(this.OnEditableTextBoxPreviewTextInput);
        }

我不确定这样做的所有含义,但这似乎是模仿WPF控件的最接近的方法。

我想避免挂起引用,这是对的,因为我注意到它在性能调整期间发生。我不确定具体的原因,所以我宁愿使用新技术来保证安全。关于您的TabItem示例,我知道会发生这种情况,但我想不出有问题的情况。只有当父对象将其事件解析到子对象上(从而保留对子对象的第二个引用)时,才会发生泄漏。如果我没有听错,那么在上面的示例中,“父对象”是自定义控件和“子对象”是addMenu(模板部分)。由于自定义控件将addMenu存储在私有字段中,因此旧技术(在ApplyTemplate上使用的)可能会发生泄漏.No。在您的示例中,自定义控件必须执行类似custom.event+=addMenu.Method的操作,而不是通常的addMenu.event+=custom.Method。这是非常不寻常的。