.NET Framework下WPF DataTemplate内事件未自动退订导致重复触发问题
问题根本原因
这不是WPF的缺陷,是绑定和引用残留导致的问题:
- 你的ListBox直接绑定了全局静态数据源,WPF绑定系统会对绑定到静态源的控件实例持有强引用,即便你将UserControl从界面移除触发了Unloaded事件,控件实例也没有被GC回收,仍然存活在内存中。
- CheckBox的
IsChecked属性为双向绑定,绑定到全局数据对象的IsEnabled属性。当你操作当前界面上的CheckBox时,会修改全局数据的IsEnabled值,所有曾经绑定过该全局数据源的CheckBox(包括已被移除但未被回收的UserControl内的CheckBox),都会收到属性变更通知,更新自身IsChecked状态,进而触发Checked/Unchecked事件,就出现了一次操作触发多次日志的现象。
可行解决方案
方案1:Unloaded时手动切断绑定
在UserControl的Unloaded事件中手动清空内部ListBox的ItemsSource,切断和全局数据源的绑定关联,移除引用:
private void UserControl1_OnUnloaded(object sender, RoutedEventArgs e) { Log("Unloaded"); // 给内部ListBox起个名字比如InnerListBox,清空ItemsSource InnerListBox.ItemsSource = null; }
方案2:改用MVVM命令绑定替代直接事件订阅
不要在XAML里直接绑定Checked/Unchecked事件到后台代码,改为绑定到ViewModel的命令,同时在Unloaded时置空DataContext:
private void UserControl1_OnUnloaded(object sender, RoutedEventArgs e) { Log("Unloaded"); this.DataContext = null; InnerListBox.ClearValue(ItemsControl.ItemsSourceProperty); }
方案3:改用局部数据源
不要直接绑定全局静态数据源,将全局数据拷贝一份作为UserControl的局部数据源使用,切断控件和全局源的强引用关联,控件移除后就不会再收到全局数据的变更通知。
方案4:使用弱事件订阅
改用WPF弱事件模式来订阅Checked/Unchecked事件,避免事件订阅的强引用导致控件无法被GC回收。
内容的提问来源于stack exchange,提问作者wpfwannabe
相关产品推荐
相关产品推荐

