Фильтр 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);

        }
    }
}