CollectionViewSource Filter not refreshed when Source is changed

吃可爱长大的小学妹 提交于 2020-01-20 21:45:08

问题


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


回答1:


A bit late perhaps, but this may help other users so I'll post anyway...

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.




回答2:


Are you changing the actual collection instance assigned to the CollectionViewSource.Source, or are you just firing PropertyChanged on the property that it's bound to?

If the Source property is set, the filter should be recalled for every item in the new source collection, so I'm thinking something else is happening. Have you tried setting Source manually instead of using a binding and seeing if you still get your behavior?

Edit:

Are you using CollectionViewSource.View.Filter property, or the CollectionViewSource.Filter event? The CollectionView will get blown away when you set a new Source, so if you had a Filter set on the CollectionView it won't be there anymore.




回答3:


I found a specific solution for extending the ObservableCollection class to one that monitors changes in the properties of the objects it contains here.

Here's that code with a few modifications by me:

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

        }
    }
}


来源:https://stackoverflow.com/questions/661630/collectionviewsource-filter-not-refreshed-when-source-is-changed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!