Фильтр CollectionViewSource не обновляется при изменении источника
У меня есть ListView WPF, связанный с CollectionViewSource. Источник этого привязан к свойству, которое может измениться, если пользователь выбирает параметр.
Когда источник просмотра списка обновляется из-за события с изменением свойства, все обновляется корректно, но представление не обновляется, чтобы принимать во внимание любые изменения в фильтре CollectionViewSource.
Если я присоединяю обработчик к событию Changed, к которому привязано свойство Source, я могу обновить представление, но это все еще старое представление, поскольку привязка еще не обновила список.
Есть ли достойный способ обновить представление и переоценить фильтры при изменении источника?
Приветствия
Ответы
Ответ 1
Немного поздно, но это может помочь другим пользователям, поэтому я все равно отправлю...
Обновление CollectionView.Filter на основе события PropertyChanged не поддерживается каркасом.
Существует несколько решений.
1) Реализация интерфейса IEditableObject для объектов внутри вашей коллекции и вызов BeginEdit и EndEdit при изменении свойства, на котором основан фильтр.
Подробнее об этом читайте в отличном блоге Dr.WPF: Редактируемые коллекции Dr.WPF
2) Создание следующего класса и использование функции RefreshFilter для измененного объекта.
public class FilteredObservableCollection<T> : ObservableCollection<T>
{
public void RefreshFilter(T changedobject)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, changedobject, changedobject));
}
}
Пример:
public class TestClass : INotifyPropertyChanged
{
private string _TestProp;
public string TestProp
{
get{ return _TestProp; }
set
{
_TestProp = value;
RaisePropertyChanged("TestProp");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
FilteredObservableCollection<TestClass> TestCollection = new FilteredObservableCollection<TestClass>();
void TestClass_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "TestProp":
TestCollection.RefreshFilter(sender as TestClass);
break;
}
}
Подпишитесь на событие PropertyChanged объекта TestClass при его создании, но не забудьте отцепить обработчик событий, когда объект будет удален, иначе это может привести к утечке памяти.
ИЛИ
Внесите TestCollection в TestClass и используйте функцию RefreshFilter внутри установщика TestProp.
Во всяком случае, магия здесь обрабатывается NotifyCollectionChangedAction.Replace, которая полностью обновляет элемент.
Ответ 2
Вы меняете фактический экземпляр коллекции, назначенный CollectionViewSource.Source
, или вы просто стреляете PropertyChanged
в свойство, которому оно связано?
Если свойство Source
установлено, фильтр должен быть вызван для каждого элемента в новой исходной коллекции, поэтому я думаю, что происходит что-то еще. Попробовали ли вы установить Source
вручную вместо использования привязки и посмотреть, все ли вы по-прежнему получаете свое поведение?
Edit:
Используете ли вы CollectionViewSource.View.Filter
или CollectionViewSource.Filter
событие? CollectionView
будет сбрасываться, когда вы установите новый Source
, поэтому, если у вас есть Filter
, установленный на CollectionView
, он больше не будет там.
Ответ 3
Я нашел конкретное решение для расширения класса ObservableCollection до того, которое отслеживает изменения в свойствах объектов, которые он содержит здесь.
Вот этот код с несколькими модификациями от меня:
namespace Solution
{
public class ObservableCollectionEx<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e != null) // There been an addition or removal of items from the Collection
{
Unsubscribe(e.OldItems);
Subscribe(e.NewItems);
base.OnCollectionChanged(e);
}
else
{
// Just a property has changed, so reset the Collection.
base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
protected override void ClearItems()
{
foreach (T element in this)
element.PropertyChanged -= ContainedElementChanged;
base.ClearItems();
}
private void Subscribe(IList iList)
{
if (iList != null)
{
foreach (T element in iList)
element.PropertyChanged += ContainedElementChanged;
}
}
private void Unsubscribe(IList iList)
{
if (iList != null)
{
foreach (T element in iList)
element.PropertyChanged -= ContainedElementChanged;
}
}
private void ContainedElementChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged(e);
// Tell the Collection that the property has changed
this.OnCollectionChanged(null);
}
}
}