ObservableCollection Doesn't support AddRange method, so I get notified for each item added, besides what about INotifyCollectionChanging?

前端 未结 12 1410
走了就别回头了
走了就别回头了 2020-11-22 16:19

I want to be able to add a range and get updated for the entire bulk.

I also want to be able to cancel the action before it\'s done (i.e. collection changing besides

12条回答
  •  情深已故
    2020-11-22 16:39

    Here's a modification of the accepted answer to provide more functionality.

    RangeCollection.cs:

    public class RangeCollection : ObservableCollection
    {
        #region Members
    
        /// 
        /// Occurs when a single item is added.
        /// 
        public event EventHandler> ItemAdded;
    
        /// 
        /// Occurs when a single item is inserted.
        /// 
        public event EventHandler> ItemInserted;
    
        /// 
        /// Occurs when a single item is removed.
        /// 
        public event EventHandler> ItemRemoved;
    
        /// 
        /// Occurs when a single item is replaced.
        /// 
        public event EventHandler> ItemReplaced;
    
        /// 
        /// Occurs when items are added to this.
        /// 
        public event EventHandler> ItemsAdded;
    
        /// 
        /// Occurs when items are removed from this.
        /// 
        public event EventHandler> ItemsRemoved;
    
        /// 
        /// Occurs when items are replaced within this.
        /// 
        public event EventHandler> ItemsReplaced;
    
        /// 
        /// Occurs when entire collection is cleared.
        /// 
        public event EventHandler> ItemsCleared;
    
        /// 
        /// Occurs when entire collection is replaced.
        /// 
        public event EventHandler> CollectionReplaced;
    
        #endregion
    
        #region Helper Methods
    
        /// 
        /// Throws exception if any of the specified objects are null.
        /// 
        private void Check(params T[] Items)
        {
            foreach (T Item in Items)
            {
                if (Item == null)
                {
                    throw new ArgumentNullException("Item cannot be null.");
                }
            }
        }
    
        private void Check(IEnumerable Items)
        {
            if (Items == null) throw new ArgumentNullException("Items cannot be null.");
        }
    
        private void Check(IEnumerable> Items)
        {
            if (Items == null) throw new ArgumentNullException("Items cannot be null.");
        }
    
        private void RaiseChanged(NotifyCollectionChangedAction Action)
        {
            this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
            this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    
        #endregion
    
        #region Bulk Methods
    
        ///  
        /// Adds the elements of the specified collection to the end of this.
        ///  
        public void AddRange(IEnumerable NewItems)
        {
            this.Check(NewItems);
            foreach (var i in NewItems) this.Items.Add(i);
            this.RaiseChanged(NotifyCollectionChangedAction.Reset);
            this.OnItemsAdded(new ItemsAddedEventArgs(NewItems));
        }
    
        /// 
        /// Adds variable IEnumerable to this.
        /// 
        /// 
        public void AddRange(params IEnumerable[] NewItems)
        {
            this.Check(NewItems);
            foreach (IEnumerable Items in NewItems) foreach (T Item in Items) this.Items.Add(Item);
            this.RaiseChanged(NotifyCollectionChangedAction.Reset);
            //TO-DO: Raise OnItemsAdded with combined IEnumerable.
        }
    
        ///  
        /// Removes the first occurence of each item in the specified collection. 
        ///  
        public void Remove(IEnumerable OldItems)
        {
            this.Check(OldItems);
            foreach (var i in OldItems) Items.Remove(i);
            this.RaiseChanged(NotifyCollectionChangedAction.Reset);
            OnItemsRemoved(new ItemsRemovedEventArgs(OldItems));
        }
    
        /// 
        /// Removes all occurences of each item in the specified collection.
        /// 
        /// 
        public void RemoveAll(IEnumerable OldItems)
        {
            this.Check(OldItems);
            var set = new HashSet(OldItems);
            var list = this as List;
            int i = 0;
            while (i < this.Count) if (set.Contains(this[i])) this.RemoveAt(i); else i++;
            this.RaiseChanged(NotifyCollectionChangedAction.Reset);
            OnItemsRemoved(new ItemsRemovedEventArgs(OldItems));
        }
    
        ///  
        /// Replaces all occurences of a single item with specified item.
        ///  
        public void ReplaceAll(T Old, T New)
        {
            this.Check(Old, New);
            this.Replace(Old, New, false);
            this.RaiseChanged(NotifyCollectionChangedAction.Reset);
            this.OnItemReplaced(new ItemReplacedEventArgs(Old, New));
        }
    
        ///  
        /// Clears this and adds specified collection. 
        ///  
        public void ReplaceCollection(IEnumerable NewItems, bool SupressEvent = false)
        {
            this.Check(NewItems);
            IEnumerable OldItems = new List(this.Items);
            this.Items.Clear();
            foreach (T Item in NewItems) this.Items.Add(Item);
            this.RaiseChanged(NotifyCollectionChangedAction.Reset);
            this.OnReplaced(new CollectionReplacedEventArgs(OldItems, NewItems));
        }
    
        private void Replace(T Old, T New, bool BreakFirst)
        {
            List Cloned = new List(this.Items);
            int i = 0;
            foreach (T Item in Cloned)
            {
                if (Item.Equals(Old))
                {
                    this.Items.Remove(Item);
                    this.Items.Insert(i, New);
                    if (BreakFirst) break;
                }
                i++;
            }
        }
    
        ///  
        /// Replaces the first occurence of a single item with specified item.
        ///  
        public void Replace(T Old, T New)
        {
            this.Check(Old, New);
            this.Replace(Old, New, true);
            this.RaiseChanged(NotifyCollectionChangedAction.Reset);
            this.OnItemReplaced(new ItemReplacedEventArgs(Old, New));
        }
    
        #endregion
    
        #region  New Methods
    
        /// 
        /// Removes a single item.
        /// 
        /// 
        public new void Remove(T Item)
        {
            this.Check(Item);
            base.Remove(Item);
            OnItemRemoved(new ItemRemovedEventArgs(Item));
        }
    
        /// 
        /// Removes a single item at specified index.
        /// 
        /// 
        public new void RemoveAt(int i)
        {
            T OldItem = this.Items[i]; //This will throw first if null
            base.RemoveAt(i);
            OnItemRemoved(new ItemRemovedEventArgs(OldItem));
        }
    
        /// 
        /// Clears this.
        /// 
        public new void Clear()
        {
            IEnumerable OldItems = new List(this.Items);
            this.Items.Clear();
            this.RaiseChanged(NotifyCollectionChangedAction.Reset);
            this.OnCleared(new ItemsClearedEventArgs(OldItems));
        }
    
        /// 
        /// Adds a single item to end of this.
        /// 
        /// 
        public new void Add(T Item)
        {
            this.Check(Item);
            base.Add(Item);
            this.OnItemAdded(new ItemAddedEventArgs(Item));
        }
    
        /// 
        /// Inserts a single item at specified index.
        /// 
        /// 
        /// 
        public new void Insert(int i, T Item)
        {
            this.Check(Item);
            base.Insert(i, Item);
            this.OnItemInserted(new ItemInsertedEventArgs(Item, i));
        }
    
        /// 
        /// Returns list of T.ToString().
        /// 
        /// 
        public new IEnumerable ToString()
        {
            foreach (T Item in this) yield return Item.ToString();
        }
    
        #endregion
    
        #region Event Methods
    
        private void OnItemAdded(ItemAddedEventArgs i)
        {
            if (this.ItemAdded != null) this.ItemAdded(this, new ItemAddedEventArgs(i.NewItem));
        }
    
        private void OnItemInserted(ItemInsertedEventArgs i)
        {
            if (this.ItemInserted != null) this.ItemInserted(this, new ItemInsertedEventArgs(i.NewItem, i.Index));
        }
    
        private void OnItemRemoved(ItemRemovedEventArgs i)
        {
            if (this.ItemRemoved != null) this.ItemRemoved(this, new ItemRemovedEventArgs(i.OldItem));
        }
    
        private void OnItemReplaced(ItemReplacedEventArgs i)
        {
            if (this.ItemReplaced != null) this.ItemReplaced(this, new ItemReplacedEventArgs(i.OldItem, i.NewItem));
        }
    
        private void OnItemsAdded(ItemsAddedEventArgs i)
        {
            if (this.ItemsAdded != null) this.ItemsAdded(this, new ItemsAddedEventArgs(i.NewItems));
        }
    
        private void OnItemsRemoved(ItemsRemovedEventArgs i)
        {
            if (this.ItemsRemoved != null) this.ItemsRemoved(this, new ItemsRemovedEventArgs(i.OldItems));
        }
    
        private void OnItemsReplaced(ItemsReplacedEventArgs i)
        {
            if (this.ItemsReplaced != null) this.ItemsReplaced(this, new ItemsReplacedEventArgs(i.OldItems, i.NewItems));
        }
    
        private void OnCleared(ItemsClearedEventArgs i)
        {
            if (this.ItemsCleared != null) this.ItemsCleared(this, new ItemsClearedEventArgs(i.OldItems));
        }
    
        private void OnReplaced(CollectionReplacedEventArgs i)
        {
            if (this.CollectionReplaced != null) this.CollectionReplaced(this, new CollectionReplacedEventArgs(i.OldItems, i.NewItems));
        }
    
        #endregion
    
        #region RangeCollection
    
        ///  
        /// Initializes a new instance. 
        ///  
        public RangeCollection() : base() { }
    
        ///  
        /// Initializes a new instance from specified enumerable. 
        ///  
        public RangeCollection(IEnumerable Collection) : base(Collection) { }
    
        ///  
        /// Initializes a new instance from specified list.
        ///  
        public RangeCollection(List List) : base(List) { }
    
        /// 
        /// Initializes a new instance with variable T.
        /// 
        public RangeCollection(params T[] Items) : base()
        {
            this.AddRange(Items);
        }
    
        /// 
        /// Initializes a new instance with variable enumerable.
        /// 
        public RangeCollection(params IEnumerable[] Items) : base()
        {
            this.AddRange(Items);
        }
    
        #endregion
    }
    

    Events Classes:

    public class CollectionReplacedEventArgs : ReplacedEventArgs
    {
        public CollectionReplacedEventArgs(IEnumerable Old, IEnumerable New) : base(Old, New) { }
    }
    
    public class ItemAddedEventArgs : EventArgs
    {
        public T NewItem;
        public ItemAddedEventArgs(T t)
        {
            this.NewItem = t;
        }
    }
    
    public class ItemInsertedEventArgs : EventArgs
    {
        public int Index;
        public T NewItem;
        public ItemInsertedEventArgs(T t, int i)
        {
            this.NewItem = t;
            this.Index = i;
        }
    }
    
    public class ItemRemovedEventArgs : EventArgs
    {
        public T OldItem;
        public ItemRemovedEventArgs(T t)
        {
            this.OldItem = t;
        }
    }
    
    public class ItemReplacedEventArgs : EventArgs
    {
        public T OldItem;
        public T NewItem;
        public ItemReplacedEventArgs(T Old, T New)
        {
            this.OldItem = Old;
            this.NewItem = New;
        }
    }
    
    public class ItemsAddedEventArgs : EventArgs
    {
        public IEnumerable NewItems;
        public ItemsAddedEventArgs(IEnumerable t)
        {
            this.NewItems = t;
        }
    }
    
    public class ItemsClearedEventArgs : RemovedEventArgs
    {
        public ItemsClearedEventArgs(IEnumerable Old) : base(Old) { }
    }
    
    public class ItemsRemovedEventArgs : RemovedEventArgs
    {
        public ItemsRemovedEventArgs(IEnumerable Old) : base(Old) { }
    }
    
    public class ItemsReplacedEventArgs : ReplacedEventArgs
    {
        public ItemsReplacedEventArgs(IEnumerable Old, IEnumerable New) : base(Old, New) { }
    }
    
    public class RemovedEventArgs : EventArgs
    {
        public IEnumerable OldItems;
        public RemovedEventArgs(IEnumerable Old)
        {
            this.OldItems = Old;
        }
    }
    
    public class ReplacedEventArgs : EventArgs
    {
        public IEnumerable OldItems;
        public IEnumerable NewItems;
        public ReplacedEventArgs(IEnumerable Old, IEnumerable New)
        {
            this.OldItems = Old;
            this.NewItems = New;
        }
    }
    

    Note: I did not manually raise OnCollectionChanged in the base methods because it appears only to be possible to create a CollectionChangedEventArgs using the Reset action. If you try to raise OnCollectionChanged using Reset for a single item change, your items control will appear to flicker, which is something you want to avoid.

提交回复
热议问题