Фильтр CollectionViewSource не обновляется при изменении источника - PullRequest
12 голосов
/ 19 марта 2009

У меня есть WPF ListView, связанный с CollectionViewSource. Источник этого связан со свойством, которое может измениться, если пользователь выберет опцию.

Когда источник представления списка обновляется из-за события изменения свойства, все обновляется правильно, но представление не обновляется, чтобы учесть любые изменения в фильтре CollectionViewSource.

Если я присоединяю обработчик к событию Changed, с которым связано свойство Source, я могу обновить представление, но это все еще старое представление, поскольку привязка еще не обновила список.

Есть ли достойный способ обновления представления и переоценки фильтров при изменении источника?

Приветствия

Ответы [ 3 ]

12 голосов
/ 01 апреля 2011

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

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

OR

Вставьте TestCollection в TestClass и используйте функцию RefreshFilter внутри установщика TestProp. В любом случае, магия здесь работает с NotifyCollectionChangedAction.Replace, которая полностью обновляет элемент.

2 голосов
/ 17 февраля 2012

Я нашел конкретное решение для расширения класса ObservableCollection до класса, который отслеживает изменения свойств объектов, которые он содержит здесь .

Вот этот код с некоторыми моими изменениями:

namespace Solution
{
public class ObservableCollectionEx<T> : ObservableCollection<T> where T : INotifyPropertyChanged
    {
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (e != null)  // There's 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);

        }
    }
}
2 голосов
/ 19 марта 2009

Вы изменяете фактический экземпляр коллекции, назначенный для CollectionViewSource.Source, или вы просто запускаете PropertyChanged для свойства, к которому он привязан?

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

Edit:

Используете ли вы свойство CollectionViewSource.View.Filter или событие CollectionViewSource.Filter? CollectionView взорвется, когда вы установите новый Source, поэтому, если у вас есть Filter, установленный на CollectionView, его больше не будет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...