WPF, как правильно отцепить обработчики при просмотре TypeDescriptor - PullRequest
1 голос
/ 09 ноября 2010

Я использую WPF и пытаюсь следовать шаблону MVVM. Наша команда решила использовать элемент управления Xceed DataGrid, и у меня возникли некоторые трудности при его установке в шаблон MVVM.

Одно требование, которое я должен выполнить, это то, что мне нужно знать, когда пользователь меняет фильтр столбцов в сетке. Мне известно, что в последней версии элемента управления DataGrid есть событие, которое вызывается для этого, но, к сожалению, мне приходится использовать более старую версию элемента управления.

После поиска некоторое время, я нашел эту запись. Это говорит о том, что мне нужно подключить обработчик INotifyCollectionChanged к каждому из возможных списков фильтров. Это работает, но также говорит, что мне нужно отсоединять обработчики всякий раз, когда изменяется источник строки сетки.

Мне удалось заставить его работать, когда я явно установил источник строки в коде позади страницы (и в моей первой попытке в ModelView с помощью прямой ссылки на представление gasp! )

Первая проблема, с которой я столкнулся, заключается в том, как сделать это, не имея логики в коде или в 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 голосов
/ 10 ноября 2010

Вы правы, что AutoFilterValues ​​уже изменился на этом этапе, поэтому вы будете отцеплять неправильные обработчики, что приведет к утечке памяти.

Решение очень простое.Делайте то же, что и вы, но используйте List<IList> вместо простой ссылки на AutoFilterValues:

private List<IList> _GridFilters;

и используйте ToList(), чтобы сделать копию фильтров, для которых вы устанавливаете обработчики:

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

Поскольку _GridFilters теперь List<IList>, вам также придется изменить циклы:

foreach(IList autofilterValues in _GridFilters) 
  ...

Причина, по которой это работает, заключается в том, что фактический список старых списков фильтров копируется в _GridFilters, а нечем просто ссылка на свойство AutoFilterValues.

Это хороший общий метод, который применим во многих ситуациях.

...