WPF, Как правильно отключить обработчики при просмотре TypeDescriptor

StackOverflow https://stackoverflow.com/questions/4138399

Вопрос

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

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

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

Мне удалось заставить его работать, когда я явно установил источник строки в коде страницы (и в моей первой попытке в 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 ​​уже изменилась, поэтому я не могу эффективно отцепить обработчики.

Прав ли я, предполагая это?И может ли кто-нибудь придумать лучший способ управления этими обработчиками, позволяющий мне сохранить эту функциональность, инкапсулированную в моем расширенном классе?

Извините за длину поста и заранее спасибо за помощь!

-Фунгер

Это было полезно?

Решение

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

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

private List<IList> _GridFilters;

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

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

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

foreach(IList autofilterValues in _GridFilters) 
  ...

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top