Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在何处取消订阅附加属性中的活动?_C#_Wpf_Datagridview_Attached Properties - Fatal编程技术网

C# 在何处取消订阅附加属性中的活动?

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

我有一个附加属性要在datagrid中使用,以便可以在我的视图模型中使用SelectedItems。代码如下:

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对象

您可以使用下一个链接下载演示此行为的测试项目。它还演示了如何通过处理事件来复制内存泄漏问题


我也遇到了同样的问题,但我得出的结论是,没有必要取消本次活动的订阅