WPF,如何在观看TypeDescriptor时正确解开处理程序

时间:2010-11-09 20:42:31

标签: wpf mvvm typedescriptor xceed-datagrid

我正在使用WPF,并尝试遵循MVVM模式。我们的团队决定使用Xceed DataGrid控件,我在使其适应MVVM模式时遇到了一些困难。

我必须满足的一个要求是我需要知道用户何时更改网格上的列过滤器。我知道最新版本的DataGrid控件有一个为此引发的事件,但不幸的是,我必须使用旧版本的控件。

在搜索了一段时间之后,我找到了this帖子。它说我需要将一个INotifyCollectionChanged处理程序挂钩到每个可能的过滤器列表。这有效,但它也说我需要在网格的行源发生变化时解开处理程序。

当我在页面的代码隐藏中显式设置行源时,我能够使它工作(并且在我第一次尝试使用直接引用视图 gasp!时在ModelView中)

我遇到的第一个问题是如何在没有代码中的逻辑或ViewModel中的逻辑的情况下执行此操作。我的解决方案是扩展DataGridControl类并添加以下代码:

    private IDictionary<string, IList> _GridFilters = null;
    public MyDataGridControl() : base()
    {
        TypeDescriptor.GetProperties(typeof(MyDataGridControl))["ItemsSource"].AddValueChanged(this, new EventHandler(ItemsSourceChanged));
    }

    void ItemsSourceChanged(object sender, EventArgs e)
    {
        UnsetGridFilterChangedEvent();
        SetGridFilterChangedEvent();
    }

    public void SetGridFilterChangedEvent()
    {
        if (this.ItemsSource == null)
            return;

        DataGridCollectionView dataGridCollectionView = (DataGridCollectionView)this.ItemsSource;

        _GridFilters = dataGridCollectionView.AutoFilterValues;

        foreach (IList autofilterValues in _GridFilters.Values)
        {
            ((INotifyCollectionChanged)autofilterValues).CollectionChanged += FilterChanged;
        }
    }

    /*TODO: Possible memory leak*/
    public void UnsetGridFilterChangedEvent()
    {
        if (_GridFilters == null)
            return;

        foreach (IList autofilterValues in _GridFilters.Values)
        {
            INotifyCollectionChanged notifyCollectionChanged = autofilterValues as INotifyCollectionChanged;

            notifyCollectionChanged.CollectionChanged -= FilterChanged;
        }

        _GridFilters = null;
    }

这引出了我的下一个问题;我很确定在调用ItemsSourceChanged方法时,AutoFilterValues的集合已经改变了,所以我无法有效地取消处理程序。

我是否正确地假设这个?有没有人能想到更好的管理这些处理程序的方法,同时仍然允许我将这些功能封装在我的扩展类中?

对帖子的长度感到抱歉,并提前感谢您的帮助!

-Funger

1 个答案:

答案 0 :(得分:0)

你知道AutoFilterValues在那时已经改变了,所以你将解除错误的处理程序,导致内存泄漏。

解决方案非常简单。完全按照您的要求进行,但使用List<IList>而不是仅仅引用AutoFilterValues:

private List<IList> _GridFilters;

并使用ToList()复制您设置处理程序的过滤器:

_GridFilters = dataGridCollectionView.AutoFilterValues.Values.ToList();

由于_GridFilters现在是List<IList>,您还必须更改循环:

foreach(IList autofilterValues in _GridFilters) 
  ...

这样做的原因是旧的过滤器列表的实际列表被复制到_GridFilters中,而不是简单地引用AutoFilterValues属性。

这是一种很好的通用技术,适用于许多情况。