WPF, Как правильно отключить обработчики при просмотре TypeDescriptor
-
30-09-2019 - |
Вопрос
Я использую 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.
Это хороший общий метод, который применим во многих ситуациях.