C# 在何处取消订阅附加属性中的活动?
我有一个附加属性要在datagrid中使用,以便可以在我的视图模型中使用SelectedItems。代码如下:C# 在何处取消订阅附加属性中的活动?,c#,wpf,datagridview,attached-properties,C#,Wpf,Datagridview,Attached Properties,我有一个附加属性要在datagrid中使用,以便可以在我的视图模型中使用SelectedItems。代码如下: public class DataGridSelectedItemsAttachedProperty { #region SelectedItems /// /// SelectedItems Attached Dependency Property /// public static readon
public class DataGridSelectedItemsAttachedProperty
{
#region SelectedItems
///
/// SelectedItems Attached Dependency Property
///
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.RegisterAttached("SelectedItems", typeof(IList),
typeof(DataGridSelectedItemsAttachedProperty),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnSelectedItemsChanged)));
public static IList GetSelectedItems(DependencyObject d)
{
return (IList)d.GetValue(SelectedItemsProperty);
}
public static void SetSelectedItems(DependencyObject d, IList value)
{
d.SetValue(SelectedItemsProperty, value);
}
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid miDg = (DataGrid)d;
miDg.SelectionChanged += dataGrid_SelectionChanged;
miDg.Unloaded += dataGrid_Unloaded;
}
private static void dataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DataGrid miDg = (DataGrid)sender;
//Get list box's selected items.
IEnumerable miDgSelectedItems = miDg.SelectedItems;
//Get list from model
IList ModelSelectedItems = GetSelectedItems(miDg);
//Update the model
ModelSelectedItems.Clear();
if (miDg.SelectedItems != null)
{
foreach (var item in miDg.SelectedItems)
ModelSelectedItems.Add(item);
}
SetSelectedItems(miDg, ModelSelectedItems);
}
private static void dataGrid_Unloaded(object sender, RoutedEventArgs e)
{
DataGrid miDg = sender as DataGrid;
miDg.SelectionChanged -= dataGrid_SelectionChanged;
miDg.Unloaded -= dataGrid_Unloaded;
}
#endregion
}
问题在于,此datagrid位于触发事件卸载的选项卡控件中,因此事件被取消订阅,然后SelectedItems不再通知视图模型
所以我想知道如何解决这个问题,也许在另一个地方取消订阅事件而不是卸载事件
谢谢
当我关闭用户控件时,由于没有对象引用它,因此将收回附加的属性
这是错误的。如果删除注销事件的代码,则任何使用附加属性的控件都将永远有效。为什么?因为您注册的事件处理程序是静态的。这意味着该控件将包含对某个静态对象的引用,从而阻止垃圾收集器收集它
此问题的第一个潜在解决方案是在注册事件时使用弱事件模式。出于上述原因,我在为自己的附加属性注册事件时总是使用弱事件模式
这个解决方案令人恼火的地方在于它需要大量的样板代码。您必须为每一种新类型的事件创建一个新的WeakEventManager
实现。然后,为了接收弱事件,您必须实现一个接口(编辑:除非您使用的是.NET 4.5或更高版本),这意味着您不能使用静态处理程序。因此,您需要实现IWeakEventListner
接口的类,并在附加的属性事件中创建和管理该类的实例
因此,我向您推荐的解决方案实际上是将DataGrid
类划分为子类,并将此功能添加为普通的依赖属性。如果这样做,就根本不需要注册事件(有一些受保护的方法可以重写),也不必担心潜在的内存泄漏。我推荐此解决方案的原因是,根据我的经验,由于许多其他原因,我需要重写DataGrid
类,其中许多可以通过附加属性来实现,但其中一些无法实现
真正的问题是WPF DataGrid实现相当不成熟(我个人的观点)。有一些bug,我不喜欢的默认行为,以及不完整或未实现的特性(例如支持复制,但不支持粘贴;或者我认为您正试图解决的特定问题:可绑定的SelectedItems)。通过简单地将DataGrid子类化,可以最容易地解决所有这些问题
当我关闭用户控件时,由于没有对象引用它,因此将收回附加的属性
这是错误的。如果删除注销事件的代码,则任何使用附加属性的控件都将永远有效。为什么?因为您注册的事件处理程序是静态的。这意味着该控件将包含对某个静态对象的引用,从而阻止垃圾收集器收集它
此问题的第一个潜在解决方案是在注册事件时使用弱事件模式。出于上述原因,我在为自己的附加属性注册事件时总是使用弱事件模式
这个解决方案令人恼火的地方在于它需要大量的样板代码。您必须为每一种新类型的事件创建一个新的WeakEventManager
实现。然后,为了接收弱事件,您必须实现一个接口(编辑:除非您使用的是.NET 4.5或更高版本),这意味着您不能使用静态处理程序。因此,您需要实现IWeakEventListner
接口的类,并在附加的属性事件中创建和管理该类的实例
因此,我向您推荐的解决方案实际上是将DataGrid
类划分为子类,并将此功能添加为普通的依赖属性。如果这样做,就根本不需要注册事件(有一些受保护的方法可以重写),也不必担心潜在的内存泄漏。我推荐此解决方案的原因是,根据我的经验,由于许多其他原因,我需要重写DataGrid
类,其中许多可以通过附加属性来实现,但其中一些无法实现
真正的问题是WPF DataGrid实现相当不成熟(我个人的观点)。有一些bug,我不喜欢的默认行为,以及不完整或未实现的特性(例如支持复制,但不支持粘贴;或者我认为您正试图解决的特定问题:可绑定的SelectedItems)。只需将DataGrid子类化,就可以最轻松地解决所有这些问题。我也遇到了同样的问题,但得出的结论是,在这种情况下,没有必要取消订阅活动(感谢阿尔瓦罗·加西亚和布莱克多斯的评论,他们为我指明了这一方向) 实际上,事件处理程序导致的内存泄漏是一个单向问题。此问题的原因如下所述:。使用此代码
miDg.SelectionChanged+=dataGrid\u SelectionChanged代码>添加指向将dataGrid_SelectionChanged方法存储到miDg对象中的对象的链接。因此,当miDg对象处于活动状态时,GC无法删除存储dataGrid_SelectionChanged方法的对象
但是,静态对象对miDg对象一无所知,即使处理了事件,GC也可以删除miDg对象
您可以使用下一个链接下载演示此行为的测试项目。它还演示了如何通过处理事件来复制内存泄漏问题
我也遇到了同样的问题,但我得出的结论是,没有必要取消本次活动的订阅