Фильтр CollectionViewSource не обновляется при изменении источника

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

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

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

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

Ваше здоровье


person Steve    schedule 19.03.2009    source источник
comment
На случай, если кто-то найдет это, сейчас он немного устарел. В WPF 4.5 были добавлены новые функции, позволяющие выполнять сортировку, фильтрацию и группировку в реальном времени. См. jonathanantoine.com/2011/10/05/   -  person Mat Fergusson    schedule 05.09.2014


Ответы (4)


Обновление 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, который полностью обновляет элемент.

person Wim Wouters    schedule 01.04.2011
comment
В настоящее время я застрял на .NET 4.0, и решение IEditableObject работает как шарм. - person Golvellius; 07.03.2016
comment
Я не мог заставить IEditableObject работать. Но FilteredObservableCollection отлично работает. Спасибо за решение - person Maya S; 10.10.2016

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

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

Изменить:

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

person Robert Macnee    schedule 19.03.2009
comment
Да, я меняю коллекцию, и элементы в списке обновляются, отражая новую коллекцию. Однако фильтр не переоценивается. Выполнение этого вручную не помогло: ((CollectionViewSource) this.Resources [logEntryViewSource]). Source = _application.CurrentLog.Entries.ObservableCollection - person Steve; 20.03.2009

Я нашел конкретное решение для расширения класса 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);

        }
    }
}
person Stackoverflower    schedule 17.02.2012

Может, немного опоздал на вечеринку, но на всякий случай

Вы также можете использовать CollectionViewSource.LiveSortingProperties Я нашел его через это сообщение блога.

public class Message : INotifyPropertyChanged
{
    public string Text { get; set; }
    public bool Read { get; set; }

    /* for simplicity left out implementation of INotifyPropertyChanged */
}
public ObservableCollection<Message> Messages {get; set}
ListCollectionView listColectionView = (ListCollectionView)CollectionViewSource.GetDefaultView(Messages);
listColectionView.IsLiveSorting = true;
listColectionView.LiveSortingProperties.Add(nameof(Message.Read));
listColectionView.SortDescriptions.Add(new SortDescription(nameof(Message.Read), ListSortDirection.Ascending));
person Blubb    schedule 08.07.2021