How to automatically update filter and/or sort order on CollectionViewSource, when an individual item's property changes?

前端 未结 2 600
礼貌的吻别
礼貌的吻别 2021-02-14 02:24

Ok, so this question is related to Windows Phone 7/Silverlight (updated WP7 Tools, Sept 2010), specifically filtering an underlying ObservableCollection.

相关标签:
2条回答
  • 2021-02-14 03:06

    Don't you just hate it when that happens, not 5 minutes gone since I posted the question, and I've figured out what the problem is - and it was something quite basic. On the CollectionViewSource object, there is a View property, which has a Refresh() function. Calling this function after a property on an underlying item contained in the ObservableCollection<T> changes, seems to have done it.

    Basically, all I had to do was change the CollectionViewSource object into a member variable, and then save it when LoadData() is called:

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        if (!App.ViewModel.IsDataLoaded)
        {
            App.ViewModel.LoadData();
            m_isSelectedListView = this.Resources["IsSelectedCollectionView"] as CollectionViewSource;
            if (m_isSelectedListView != null)
            {
                m_isSelectedListView.Source = App.ViewModel.Items;
            }
        }
    }
    

    Then, call Refresh() on the view, after any of the items in the underlying ObservableCollection<T> changes. So in MainPage.xaml.cs, just after changing the last item, add the call to refresh:

    private void ApplicationBarIconButton_Click(object sender, EventArgs e)
    {
        ItemViewModel item = App.ViewModel.Items[App.ViewModel.Items.Count - 1];
        item.IsSelected = !item.IsSelected;
        m_isSelectedListView.View.Refresh();
    }
    

    ... and the second Pivot's ListBox is updated instantly. Such a short line of code, a whole world of difference!

    In the time it took me to write up that question, there are a hundred things I could've done :-( Ah well, better late than never I guess - thought to post the answer here, if only to save someone else tearing out their hair like I did.

    0 讨论(0)
  • 2021-02-14 03:11

    I had to handle this problem and although the 'Refresh()' solution works well, it is quite long to execute because its refreshes the whole list just for one item property changed event. Not very good. And in a scenario of real time data entering the collection every 1 seconds, I let you imagine the result in user experience if you use this approach :)

    I came up with a solution which base is : when adding an item to collection wrapped in a collectionview, then the item is evaluated by the filter predicate and, based on this result, displayed or not in the view.

    So instead of calling refresh(), I came up simulating an insert of the object that got its property updated. By simulating the insert of the object, it is going to be automatically evaluated by the filter predicate without need to refresh the whole list with a refresh.

    Here is the code in order to do that :

    The derived observable collection :

    namespace dotnetexplorer.blog.com
    {
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    
    /// <summary>
    /// Derived class used to be able to manage filter application when a collection item property changed
    ///   whithout having to do a refresh
    /// </summary>
    internal sealed class CustomObservableCollection : ObservableCollection<object>
    {
        /// <summary>
        ///   Initializes a new instance of the <see cref = "CustomObservableCollection " /> class.
        /// </summary>
        public CustomObservableCollection ()
        {
        }
    
        /// <summary>
        /// Initializes a new instance of the <see cref="CustomObservableCollection "/> class.
        /// </summary>
        /// <param name="source">
        /// The source.
        /// </param>
        public CustomObservableCollection (IEnumerable<object> source)
            : base(source)
        {
        }
    
        /// <summary>
        /// Custom Raise collection changed
        /// </summary>
        /// <param name="e">
        /// The notification action
        /// </param>
        public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            OnCollectionChanged(e);
        }
    }
    }
    

    And there is the code to use when receiveing item property changed event where substitute source is a CustomObservableCollection :

            private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
    
                    // To avoid doing a refresh on a property change which would end in a very hawful user experience
                    // we simulate a replace to the collection because the filter is automatically applied in this case
                    int index = _substituteSource.IndexOf(sender);
    
                    var argsReplace = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,
                                                                           new List<object> { sender },
                                                                           new List<object> { sender }, index);
                    _substituteSource.RaiseCollectionChanged(argsReplace);
                }
    
            }
        }
    

    Hope this will help !

    0 讨论(0)
提交回复
热议问题