从自定义控件的模板部件中删除事件处理程序

时间:2011-06-14 20:27:29

标签: wpf custom-controls

当我第一次开始编写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 个答案:

答案 0 :(得分:2)

这种方法很好,但你必须明白,你可能不希望事件处理程序脱钩,从而得到卸载事件。例如,假设您有一个制表符控件。当您切换TabItems时,前一个TabItem的内容都将被取消,然后在再次选中TabItem时重新加载。这对于Button.Click这样的事情很好,因为您无法在非活动选项卡上执行此类操作,但是即使项目仍然存在,任何不需要将项目加载到可视树中的事件也将断开连接。 / p>

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

答案 1 :(得分:0)

WPF控件(例如ComboBox)使用OnTemplateChangedInternal()方法注销在OnApplyTemplate()中注册的事件。您可以覆盖该方法,因为它位于PresentationFramework dll的内部,但是您可以覆盖受保护的OnTemplateChanged()方法以执行相同的操作-在Control基类中由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控件功能的最接近方法。