Consulte la versión actualizada y optimizada de C # 7 . No quería eliminar la versión VB.NET, así que la publiqué en una respuesta separada.
Parece que no es compatible, lo implementé yo mismo, para su información, espero que sea útil:
Actualicé la versión de VB y de ahora en adelante genera un evento antes de cambiar la colección para que pueda arrepentirse (útil cuando se usa con DataGrid
, ListView
y muchos más, que puede mostrar una confirmación de "¿Está seguro" al usuario), el VB actualizado La versión está en la parte inferior de este mensaje .
Acepte mis disculpas de que la pantalla es demasiado estrecha para contener mi código, tampoco me gusta.
VB.NET:
Imports System.Collections.Specialized
Namespace System.Collections.ObjectModel
''' <summary>
''' Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
''' </summary>
''' <typeparam name="T"></typeparam>
Public Class ObservableRangeCollection(Of T) : Inherits System.Collections.ObjectModel.ObservableCollection(Of T)
''' <summary>
''' Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
''' </summary>
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
''' <summary>
''' Removes the first occurence of each item in the specified collection from ObservableCollection(Of T).
''' </summary>
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
''' <summary>
''' Clears the current collection and replaces it with the specified item.
''' </summary>
Public Sub Replace(ByVal item As T)
ReplaceRange(New T() {item})
End Sub
''' <summary>
''' Clears the current collection and replaces it with the specified collection.
''' </summary>
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
''' <summary>
''' Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
''' </summary>
''' <remarks></remarks>
Public Sub New()
MyBase.New()
End Sub
''' <summary>
''' Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
''' </summary>
''' <param name="collection">collection: The collection from which the elements are copied.</param>
''' <exception cref="System.ArgumentNullException">The collection parameter cannot be null.</exception>
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;
/// <summary>
/// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObservableRangeCollection<T> : ObservableCollection<T>
{
/// <summary>
/// Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
/// </summary>
public void AddRange(IEnumerable<T> collection)
{
if (collection == null) throw new ArgumentNullException("collection");
foreach (var i in collection) Items.Add(i);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
/// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T).
/// </summary>
public void RemoveRange(IEnumerable<T> collection)
{
if (collection == null) throw new ArgumentNullException("collection");
foreach (var i in collection) Items.Remove(i);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
/// Clears the current collection and replaces it with the specified item.
/// </summary>
public void Replace(T item)
{
ReplaceRange(new T[] { item });
}
/// <summary>
/// Clears the current collection and replaces it with the specified collection.
/// </summary>
public void ReplaceRange(IEnumerable<T> collection)
{
if (collection == null) throw new ArgumentNullException("collection");
Items.Clear();
foreach (var i in collection) Items.Add(i);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
/// </summary>
public ObservableRangeCollection()
: base() { }
/// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
/// </summary>
/// <param name="collection">collection: The collection from which the elements are copied.</param>
/// <exception cref="System.ArgumentNullException">The collection parameter cannot be null.</exception>
public ObservableRangeCollection(IEnumerable<T> collection)
: base(collection) { }
}
Actualización: colección de rango observable con notificación de cambio de colección
Imports System.Collections.Specialized
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Public Class ObservableRangeCollection(Of T) : Inherits ObservableCollection(Of T) : Implements INotifyCollectionChanging(Of T)
''' <summary>
''' Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
''' </summary>
''' <remarks></remarks>
Public Sub New()
MyBase.New()
End Sub
''' <summary>
''' Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
''' </summary>
''' <param name="collection">collection: The collection from which the elements are copied.</param>
''' <exception cref="System.ArgumentNullException">The collection parameter cannot be null.</exception>
Public Sub New(ByVal collection As IEnumerable(Of T))
MyBase.New(collection)
End Sub
''' <summary>
''' Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
''' </summary>
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
''' <summary>
''' Inserts the collection at specified index.
''' </summary>
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
''' <summary>
''' Removes the first occurence of each item in the specified collection from ObservableCollection(Of T).
''' </summary>
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
''' <summary>
''' Clears the current collection and replaces it with the specified item.
''' </summary>
Public Sub Replace(ByVal item As T)
ReplaceRange(New T() {item})
End Sub
''' <summary>
''' Clears the current collection and replaces it with the specified collection.
''' </summary>
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
OnPropertyChanged("Count");
yOnPropertyChanged("Item[]");
agregar / quitar / reemplazar métodos de rango para imitar completamente la ObservableCollection estándar.En primer lugar, vote y comente la solicitud de API en el repositorio de .NET.
Aquí está mi versión optimizada del
ObservableRangeCollection
(versión optimizada de James Montemagno uno ).Funciona muy rápido y está destinado a reutilizar elementos existentes cuando sea posible y evitar eventos innecesarios, o agruparlos en uno, cuando sea posible. El
ReplaceRange
método reemplaza / elimina / agrega los elementos requeridos por los índices apropiados y agrupa los posibles eventos.Probado en Xamarin.Forms UI con excelentes resultados para actualizaciones muy frecuentes de la colección grande (5-7 actualizaciones por segundo).
Nota: Dado que WPF no está acostumbrado a trabajar con operaciones de rango, arrojará un
NotSupportedException
, cuando se usaObservableRangeCollection
desde abajo en el trabajo relacionado con la interfaz de usuario de WPF, como vincularlo a unListBox
etc. (aún puede usar elObservableRangeCollection<T>
si no está vinculado a la interfaz de usuario) .Sin embargo, puede utilizar la
WpfObservableRangeCollection<T>
solución alternativa.La solución real sería crear una
CollectionView
que sepa cómo lidiar con las operaciones de rango, pero aún no tuve tiempo para implementar esto.Código RAW : ábralo como Raw, luego hagaCtrl+Apara seleccionar todo, luegoCtrl+Cpara copiar.
fuente
Creo que AddRange se implementa mejor así:
Le ahorra una copia de la lista. Además, si desea micro-optimizar, puede hacer adiciones para hasta N elementos y, si se agregan más de N elementos, realice un reinicio.
fuente
Deberá tener cuidado al vincular la IU a su colección personalizada: la clase Default CollectionView solo admite notificaciones únicas de elementos.
fuente
Prueba de necesidad
OnPropertyChanged("Count")
yOnPropertyChanged("Item[]")
llamadas para comportarse como porObservableCollection
. ¡Tenga en cuenta que no sé cuáles son las consecuencias si no se molesta!Aquí hay un método de prueba que muestra que hay dos eventos PropertyChange para cada agregado en una colección observable normal. Uno para
"Count"
y otro para"Item[]"
.@Shimmy, cambie el estándar de su colección y cambie a un rango adicional y obtendrá cero PropertyChanges. Tenga en cuenta que el cambio de colección funciona bien, pero no hace exactamente lo que hace ObservableCollection. Entonces Test for shimmy collection se ve así:
FYI aquí es el código de InsertItem (también llamado por Agregar) de ObservableCollection:
fuente
El descendiente resumido en C #.
Más lectura: http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx
fuente
Sí, agregar su propia Colección Observable Personalizada sería lo suficientemente justo. No olvide generar eventos apropiados independientemente de si la UI la utiliza por el momento o no;) Deberá generar una notificación de cambio de propiedad para la propiedad "Elemento []" (requerido por el lado WPF y los controles vinculados) así como NotifyCollectionChangedEventArgs con un conjunto de elementos agregados (su rango). Hice tales cosas (además de ordenar el soporte y algunas otras cosas) y no tuve problemas con las capas Presentation y Code Behind.
fuente
Como podría haber una serie de operaciones para hacer en una Colección Observable, por ejemplo, Borrar primero, luego AgregarRango y luego insertar el elemento "Todos" para un ComboBox, terminé con la siguiente solución:
Y un ejemplo de cómo usarlo:
La notificación de reinicio se llamará solo una vez después de que Ejecutar haya terminado de procesar la lista subyacente.
fuente
Aquí hay alguna ayuda adicional para la colección modificada y los problemas de IU:
fuente
ObservableRangeCollection debería pasar una prueba como
de lo contrario obtenemos
mientras se usa con un control.
No veo una solución ideal, pero NotifyCollectionChangedAction.Reset en lugar de Add / Remove resuelve parcialmente el problema. Ver http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx como fue mencionado por net_prog
fuente
Aquí hay una modificación de la respuesta aceptada para proporcionar más funcionalidad.
RangeCollection.cs:
Clases de eventos:
Nota: No subí manualmente
OnCollectionChanged
en los métodos base porque parece que solo es posible crear unCollectionChangedEventArgs
uso de laReset
acción. Si intentas aumentarOnCollectionChanged
usandoReset
un solo cambio de elemento, el control de tus elementos parecerá parpadear, algo que debes evitar.fuente
También puede usar este código para extender ObservableCollection:
Entonces no necesita cambiar la clase en el código existente.
fuente