Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CollectionViewSource Filter not refreshed when Source is changed

I have a WPF ListView bound to a CollectionViewSource. The source of that is bound to a property, which can change if the user selects an option.

When the list view source is updated due to a property changed event, everything updates correctly, but the view is not refreshed to take into account any changes in the CollectionViewSource filter.

If I attach a handler to the Changed event that the Source property is bound to I can refresh the view, but this is still the old view, as the binding has not updated the list yet.

Is there a decent way to make the view refresh and re-evaluate the filters when the source changes?

Cheers

like image 978
Steve Avatar asked Mar 19 '09 10:03

Steve


2 Answers

Maybe a bit late to the party but just in case

You can also use CollectionViewSource.LiveSortingProperties I found it through this blog post.

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));
like image 186
Blubb Avatar answered Sep 30 '22 17:09

Blubb


Updating the CollectionView.Filter based on a PropertyChanged event is not supported by the framework. There are a number of solutions around this.

1) Implementing the IEditableObject interface on the objects inside your collection, and calling BeginEdit and EndEdit when changing the property on which the filter is based. You can read more about this on the Dr.WPF's excellent blog here : Editable Collections by Dr.WPF

2) Creating the following class and using the RefreshFilter function on the changed object.

public class FilteredObservableCollection<T> : ObservableCollection<T>
{
    public void RefreshFilter(T changedobject)
    {
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, changedobject, changedobject));
    }        
}

Example:

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;
    }
}

Subscribe to the PropertyChanged event of the TestClass object when you create it, but don't forget to unhook the eventhandler when the object gets removed, otherwise this may lead to memory leaks

OR

Inject the TestCollection into the TestClass and use the RefreshFilter function inside the TestProp setter. Anyhow, the magic here is worked by the NotifyCollectionChangedAction.Replace which updates the item entirely.

like image 36
Wim Wouters Avatar answered Sep 30 '22 17:09

Wim Wouters