何处取消订阅附属财产中的事件?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了何处取消订阅附属财产中的事件?相关的知识,希望对你有一定的参考价值。

我有一个在数据网格中使用的附加属性,可以在我的视图模型中使用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
    }

问题是此数据网格在选项卡控件中触发事件卸载,因此该事件未取消,然后SelectedItems不再通知视图模型。

所以我想知道如何解决这个问题,或许取消订阅其他地方的事件而不是卸载事件?

谢谢。

答案

当我关闭用户控件时,附加属性将被重新选择,因为没有对象引用它。

这是错误的。如果删除取消注册事件的代码,则使用附加属性的任何控件都将永久存在。为什么?因为您注册的事件处理程序是静态的。这意味着控件将包含对静态的引用,以防止垃圾收集器收集它。

此问题的第一个可能解决方案是在注册事件时使用弱事件模式。出于上述原因,我在为自己的附加属性注册事件时总是使用弱事件模式。

这个解决方案令人讨厌的是它需要相当大量的样板代码。您必须为每种新类型的事件创建一个新的WeakEventManager实现。然后,要接收弱事件,您必须实现一个接口(编辑:除非您使用的是.NET 4.5或更高版本),这意味着您不能拥有静态处理程序。那么你需要实现IWeakEventListner接口的类,并在附加的属性事件中创建和管理该类的实例。

因此,我建议您使用的解决方案实际上是对DataGrid类进行子类化,并将此功能添加为普通依赖项属性。如果你这样做,你根本不必注册事件(你可以覆盖受保护的方法),并且不用担心潜在的内存泄漏。我推荐这个解决方案的原因是因为根据我的经验,我需要覆盖DataGrid类有很多其他原因,其中许多可以通过附加属性实现,但其中一些不能。

真正的问题是WPF DataGrid实现是相当半生不熟的(我的个人意见)。存在我不喜欢的错误,默认行为,以及不完整或未实现的功能(例如支持复制,但不支持粘贴;或者我认为您尝试解决的特定问题:可绑定的SelectedItems)。通过简单地继承DataGrid,可以最轻松地解决所有这些问题。

以上是关于何处取消订阅附属财产中的事件?的主要内容,如果未能解决你的问题,请参考以下文章

如何取消订阅RxKotlin / RxJava中的Flowable?

取消/订阅 Xamarin 内容视图中的事件

如何取消订阅使用 lambda 表达式的事件?

如何取消订阅使用 lambda 表达式的事件?

订阅/取消订阅列表中的事件[重复]

EventBus事件通信框架 ( 取消注册 | 获取事件参数类型 | 根据事件类型获取订阅者 | 移除相关订阅者 )