Search code examples
windows-runtimemicrosoft-metrowinrt-xamlobservablecollectioncollectionviewsource

Metro App CollectionViewSource ObservableCollection Filter


It appears that filtering an ObservableCollection with CollectionViewSource is not possible in WinRT:

See here!

I can filter using LINQ, but how do I get the UI to update if changes that affect the filtered data are made?


Solution

  • I ended up writing my own class to achieve the desired effect:

    public class ObservableCollectionView<T> : ObservableCollection<T>
    {
        private ObservableCollection<T> _view;
        private Predicate<T> _filter;
    
        public ObservableCollectionView(IComparer<T> comparer)
            : base(comparer)
        {
    
        }
    
        public ObservableCollectionView(IComparer<T> comparer, IEnumerable<T> collection)
            : base(comparer, collection)
        {
    
        }
    
        public ObservableCollectionView(IComparer<T> comparer, IEnumerable<T> collection, Predicate<T> filter)
            : base(comparer, collection == null ? new T[] { } : collection)
        {
            if (filter != null)
            {
                _filter = filter;
    
                if (collection == null)
                    _view = new ObservableCollection<T>(comparer);
                else
                    _view = new ObservableCollection<T>(comparer, collection);
            }
        }
    
        public ObservableCollection<T> View
        {
            get
            { 
                return (_filter == null ? this : _view);
            }
        }
    
        public Predicate<T> Filter
        {
            get
            {
                return _filter;
            }
            set
            {
                if (value == null)
                {
                    _filter = null;
                    _view = new ObservableCollection<T>(Comparer);
                }
                else
                {
                    _filter = value;
                    Fill();
                }
            }
        }
    
        private void Fill()
        {
            _view = new ObservableCollection<T>(Comparer);
            foreach (T item in this)
            {
                if (Filter(item))
                    View.Add(item);
            }
        }
    
        private int this[T item]
        {
            get
            {
                int foundIndex = -1;
                for (int index = 0; index < View.Count; index++)
                {
                    if (View[index].Equals(item))
                    {
                        foundIndex = index;
                        break;
                    }
                }
                return foundIndex;
            }
        }
    
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnCollectionChanged(e);
    
            if (_filter != null)
            {
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        foreach (T item in e.NewItems)
                            if (Filter(item))
                                View.Add(item);
                        break;
    
                    case NotifyCollectionChangedAction.Move:
    
                        break;
    
                    case NotifyCollectionChangedAction.Remove:
                        foreach (T item in e.OldItems)
                            if (Filter(item))
                                View.Remove(item);
                        break;
    
                    case NotifyCollectionChangedAction.Replace:
                        for (int index = 0; index < e.OldItems.Count; index++)
                        {
                            T item = (T)e.OldItems[index];
                            if (Filter(item))
                            {
                                int foundIndex = this[item];
                                if (foundIndex != -1)
                                    View[foundIndex] = (T)e.NewItems[index];
                            }
                        }
                        break;
    
                    case NotifyCollectionChangedAction.Reset:
                        Fill();
                        break;
                }
            }
        }
    
        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
    
            if (_filter != null)
            {
        // TODO: Implement code for property changes
            }
        }
    }
    

    Not yet perfect. So improvements/suggestions welcomed.

    You can now bind this object, directly to a control using the View property.