ObservableCollection : calling OnCollectionChanged with multiple new items

前端 未结 4 1269
[愿得一人]
[愿得一人] 2020-12-06 02:33

please note that I am trying to use NotifyCollectionChangedAction.Add action instead of .Reset. the latter does work, but it is not very efficient with large collections.

4条回答
  •  旧时难觅i
    2020-12-06 03:05

    After many iterations, we ended up with this version of ObservableRangeCollection and ReadOnlyObservableRangeCollection which is based on the code from the accepted answer, and which we didn't have to modify in the last 6 months:

    public class ObservableRangeCollection : ObservableCollection
    {
        private bool suppressNotification;
    
        public ObservableRangeCollection() { }
    
        public ObservableRangeCollection(IEnumerable items)
            : base(items)
        {
        }
    
        public override event NotifyCollectionChangedEventHandler CollectionChanged;
    
        protected virtual void OnCollectionChangedMultiItem(
            NotifyCollectionChangedEventArgs e)
        {
            var handlers = CollectionChanged;
            if (handlers == null) return;
    
            foreach (NotifyCollectionChangedEventHandler handler in handlers.GetInvocationList())
            {
                if (handler.Target is ReadOnlyObservableCollection
                    && !(handler.Target is ReadOnlyObservableRangeCollection))
                {
                    throw new NotSupportedException(
                        "ObservableRangeCollection is wrapped in ReadOnlyObservableCollection which might be bound to ItemsControl " +
                        "which is internally using ListCollectionView which does not support range actions.\n" +
                        "Instead of ReadOnlyObservableCollection, use ReadOnlyObservableRangeCollection");
                }
                var collectionView = handler.Target as ICollectionView;
                if (collectionView != null)
                {
                    collectionView.Refresh();
                }
                else
                {
                    handler(this, e);
                }
            }
        }
    
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (suppressNotification) return;
    
            base.OnCollectionChanged(e);
            if (CollectionChanged != null)
            {
                CollectionChanged.Invoke(this, e);
            }
        }
    
        public void AddRange(IEnumerable items)
        {
            if (items == null) return;
    
            suppressNotification = true;
    
            var itemList = items.ToList();
    
            foreach (var item in itemList)
            {
                Add(item);
            }
            suppressNotification = false;
    
            if (itemList.Any())
            {
                OnCollectionChangedMultiItem(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, itemList));
            }
        }
    
        public void AddRange(params T[] items)
        {
            AddRange((IEnumerable)items);
        }
    
        public void ReplaceWithRange(IEnumerable items)
        {
            Items.Clear();
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            AddRange(items);
        }
    
        public void RemoveRange(IEnumerable items)
        {
            suppressNotification = true;
    
            var removableItems = items.Where(x => Items.Contains(x)).ToList();
    
            foreach (var item in removableItems)
            {
                Remove(item);
            }
    
            suppressNotification = false;
    
            if (removableItems.Any())
            {
                OnCollectionChangedMultiItem(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removableItems));
            }
        }
    }
    
    public class ReadOnlyObservableRangeCollection : ReadOnlyObservableCollection
    {
        public ReadOnlyObservableRangeCollection(ObservableCollection list)
            : base(list)
        {            
        }
    
        protected override event NotifyCollectionChangedEventHandler CollectionChanged;
    
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            var handlers = CollectionChanged;
            if (handlers == null) return;
    
            foreach (NotifyCollectionChangedEventHandler handler in handlers.GetInvocationList())
            {
                var collectionView = handler.Target as ICollectionView;
                if (collectionView != null)
                {
                    collectionView.Refresh();
                }
                else
                {
                    handler(this, e);
                }
            }
        }
    }
    

    We basically replaced all usages of ObservableCollection in our app by ObservableRangeCollection, and it works like a charm.

提交回复
热议问题