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

前端 未结 12 1406
走了就别回头了
走了就别回头了 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:41

    Please refer to the updated and optimized C# 7 version. I didn't want to remove the VB.NET version so I just posted it in a separate answer.

    Go to updated version

    Seems it's not supported, I implemented by myself, FYI, hope it to be helpful:

    I updated the VB version and from now on it raises an event before changing the collection so you can regret (useful when using with DataGrid, ListView and many more, that you can show an "Are you sure" confirmation to the user), the updated VB version is in the bottom of this message.

    Please accept my apology that the screen is too narrow to contain my code, I don't like it either.

    VB.NET:

    Imports System.Collections.Specialized
    
    Namespace System.Collections.ObjectModel
        ''' 
        ''' Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
        ''' 
        ''' 
        Public Class ObservableRangeCollection(Of T) : Inherits System.Collections.ObjectModel.ObservableCollection(Of T)
    
            ''' 
            ''' Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
            ''' 
            Public Sub AddRange(ByVal collection As IEnumerable(Of T))
                For Each i In collection
                    Items.Add(i)
                Next
                OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
            End Sub
    
            ''' 
            ''' Removes the first occurence of each item in the specified collection from ObservableCollection(Of T).
            ''' 
            Public Sub RemoveRange(ByVal collection As IEnumerable(Of T))
                For Each i In collection
                    Items.Remove(i)
                Next
    
                OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
            End Sub
    
            ''' 
            ''' Clears the current collection and replaces it with the specified item.
            ''' 
            Public Sub Replace(ByVal item As T)
                ReplaceRange(New T() {item})
            End Sub
            ''' 
            ''' Clears the current collection and replaces it with the specified collection.
            ''' 
            Public Sub ReplaceRange(ByVal collection As IEnumerable(Of T))
                Dim old = Items.ToList
                Items.Clear()
                For Each i In collection
                    Items.Add(i)
                Next
                OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
            End Sub
    
            ''' 
            ''' Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
            ''' 
            ''' 
            Public Sub New()
                MyBase.New()
            End Sub
            ''' 
            ''' Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
            ''' 
            ''' collection: The collection from which the elements are copied.
            ''' The collection parameter cannot be null.
            Public Sub New(ByVal collection As IEnumerable(Of T))
                MyBase.New(collection)
            End Sub
        End Class   
    
    End Namespace
    

    C#:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.Linq;
    
    ///  
    /// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed. 
    ///  
    ///  
    public class ObservableRangeCollection : ObservableCollection
    {
        ///  
        /// Adds the elements of the specified collection to the end of the ObservableCollection(Of T). 
        ///  
        public void AddRange(IEnumerable collection)
        {
            if (collection == null) throw new ArgumentNullException("collection");
    
            foreach (var i in collection) Items.Add(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    
        ///  
        /// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T). 
        ///  
        public void RemoveRange(IEnumerable collection)
        {
            if (collection == null) throw new ArgumentNullException("collection");
    
            foreach (var i in collection) Items.Remove(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    
        ///  
        /// Clears the current collection and replaces it with the specified item. 
        ///  
        public void Replace(T item)
        {
            ReplaceRange(new T[] { item });
        }
    
        ///  
        /// Clears the current collection and replaces it with the specified collection. 
        ///  
        public void ReplaceRange(IEnumerable collection)
        {
            if (collection == null) throw new ArgumentNullException("collection");
    
            Items.Clear();
            foreach (var i in collection) Items.Add(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    
        ///  
        /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class. 
        ///  
        public ObservableRangeCollection()
            : base() { }
    
        ///  
        /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection. 
        ///  
        /// collection: The collection from which the elements are copied. 
        /// The collection parameter cannot be null. 
        public ObservableRangeCollection(IEnumerable collection)
            : base(collection) { }
    }
    

    Update - Observable range collection with collection changing notification

    Imports System.Collections.Specialized
    Imports System.ComponentModel
    Imports System.Collections.ObjectModel
    
    Public Class ObservableRangeCollection(Of T) : Inherits ObservableCollection(Of T) : Implements INotifyCollectionChanging(Of T)
        ''' 
        ''' Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
        ''' 
        ''' 
        Public Sub New()
            MyBase.New()
        End Sub
    
        ''' 
        ''' Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
        ''' 
        ''' collection: The collection from which the elements are copied.
        ''' The collection parameter cannot be null.
        Public Sub New(ByVal collection As IEnumerable(Of T))
            MyBase.New(collection)
        End Sub
    
        ''' 
        ''' Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
        ''' 
        Public Sub AddRange(ByVal collection As IEnumerable(Of T))
            Dim ce As New NotifyCollectionChangingEventArgs(Of T)(NotifyCollectionChangedAction.Add, collection)
            OnCollectionChanging(ce)
            If ce.Cancel Then Exit Sub
    
            Dim index = Items.Count - 1
            For Each i In collection
                Items.Add(i)
            Next
    
            OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection, index))
        End Sub
    
    
        ''' 
        ''' Inserts the collection at specified index.
        ''' 
        Public Sub InsertRange(ByVal index As Integer, ByVal Collection As IEnumerable(Of T))
            Dim ce As New NotifyCollectionChangingEventArgs(Of T)(NotifyCollectionChangedAction.Add, Collection)
            OnCollectionChanging(ce)
            If ce.Cancel Then Exit Sub
    
            For Each i In Collection
                Items.Insert(index, i)
            Next
    
            OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
        End Sub
    
    
        ''' 
        ''' Removes the first occurence of each item in the specified collection from ObservableCollection(Of T).
        ''' 
        Public Sub RemoveRange(ByVal collection As IEnumerable(Of T))
            Dim ce As New NotifyCollectionChangingEventArgs(Of T)(NotifyCollectionChangedAction.Remove, collection)
            OnCollectionChanging(ce)
            If ce.Cancel Then Exit Sub
    
            For Each i In collection
                Items.Remove(i)
            Next
    
            OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
        End Sub
    
    
    
        ''' 
        ''' Clears the current collection and replaces it with the specified item.
        ''' 
        Public Sub Replace(ByVal item As T)
            ReplaceRange(New T() {item})
        End Sub
    
        ''' 
        ''' Clears the current collection and replaces it with the specified collection.
        ''' 
        Public Sub ReplaceRange(ByVal collection As IEnumerable(Of T))
            Dim ce As New NotifyCollectionChangingEventArgs(Of T)(NotifyCollectionChangedAction.Replace, Items)
            OnCollectionChanging(ce)
            If ce.Cancel Then Exit Sub
    
            Items.Clear()
            For Each i In collection
                Items.Add(i)
            Next
            OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
        End Sub
    
        Protected Overrides Sub ClearItems()
            Dim e As New NotifyCollectionChangingEventArgs(Of T)(NotifyCollectionChangedAction.Reset, Items)
            OnCollectionChanging(e)
    
            If e.Cancel Then Exit Sub
    
            MyBase.ClearItems()
        End Sub
    
        Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As T)
            Dim ce As New NotifyCollectionChangingEventArgs(Of T)(NotifyCollectionChangedAction.Add, item)
            OnCollectionChanging(ce)
            If ce.Cancel Then Exit Sub
    
            MyBase.InsertItem(index, item)
        End Sub
    
        Protected Overrides Sub MoveItem(ByVal oldIndex As Integer, ByVal newIndex As Integer)
            Dim ce As New NotifyCollectionChangingEventArgs(Of T)()
            OnCollectionChanging(ce)
            If ce.Cancel Then Exit Sub
    
            MyBase.MoveItem(oldIndex, newIndex)
        End Sub
    
        Protected Overrides Sub RemoveItem(ByVal index As Integer)
            Dim ce As New NotifyCollectionChangingEventArgs(Of T)(NotifyCollectionChangedAction.Remove, Items(index))
            OnCollectionChanging(ce)
            If ce.Cancel Then Exit Sub
    
            MyBase.RemoveItem(index)
        End Sub
    
        Protected Overrides Sub SetItem(ByVal index As Integer, ByVal item As T)
            Dim ce As New NotifyCollectionChangingEventArgs(Of T)(NotifyCollectionChangedAction.Replace, Items(index))
            OnCollectionChanging(ce)
            If ce.Cancel Then Exit Sub
    
            MyBase.SetItem(index, item)
        End Sub
    
        Protected Overrides Sub OnCollectionChanged(ByVal e As Specialized.NotifyCollectionChangedEventArgs)
            If e.NewItems IsNot Nothing Then
                For Each i As T In e.NewItems
                    If TypeOf i Is INotifyPropertyChanged Then AddHandler DirectCast(i, INotifyPropertyChanged).PropertyChanged, AddressOf Item_PropertyChanged
                Next
            End If
            MyBase.OnCollectionChanged(e)
        End Sub
    
        Private Sub Item_PropertyChanged(ByVal sender As T, ByVal e As ComponentModel.PropertyChangedEventArgs)
            OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, sender, IndexOf(sender)))
        End Sub
    
        Public Event CollectionChanging(ByVal sender As Object, ByVal e As NotifyCollectionChangingEventArgs(Of T)) Implements INotifyCollectionChanging(Of T).CollectionChanging
        Protected Overridable Sub OnCollectionChanging(ByVal e As NotifyCollectionChangingEventArgs(Of T))
            RaiseEvent CollectionChanging(Me, e)
        End Sub
    End Class
    
    
    Public Interface INotifyCollectionChanging(Of T)
        Event CollectionChanging(ByVal sender As Object, ByVal e As NotifyCollectionChangingEventArgs(Of T))
    End Interface
    
    Public Class NotifyCollectionChangingEventArgs(Of T) : Inherits CancelEventArgs
    
        Public Sub New()
            m_Action = NotifyCollectionChangedAction.Move
            m_Items = New T() {}
        End Sub
    
        Public Sub New(ByVal action As NotifyCollectionChangedAction, ByVal item As T)
            m_Action = action
            m_Items = New T() {item}
        End Sub
    
        Public Sub New(ByVal action As NotifyCollectionChangedAction, ByVal items As IEnumerable(Of T))
            m_Action = action
            m_Items = items
        End Sub
    
        Private m_Action As NotifyCollectionChangedAction
        Public ReadOnly Property Action() As NotifyCollectionChangedAction
            Get
                Return m_Action
            End Get
        End Property
    
        Private m_Items As IList
        Public ReadOnly Property Items() As IEnumerable(Of T)
            Get
                Return m_Items
            End Get
        End Property
    End Class
    

提交回复
热议问题