Veuillez vous référer à la version C # 7 mise à jour et optimisée . Je ne voulais pas supprimer la version VB.NET, je l'ai donc simplement publiée dans une réponse séparée.
Semble que ce n'est pas pris en charge, j'ai mis en œuvre par moi-même, FYI, j'espère que cela sera utile:
J'ai mis à jour la version VB et à partir de maintenant, cela déclenche un événement avant de changer la collection afin que vous puissiez regretter (utile lors de l'utilisation avec DataGrid
, ListView
et bien d'autres, que vous puissiez montrer une confirmation "Êtes-vous sûr" à l'utilisateur), le VB mis à jour la version est au bas de ce message .
Veuillez accepter mes excuses car l'écran est trop étroit pour contenir mon code, je ne l'aime pas non plus.
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) { }
}
Mise à jour - Collection de plages observables avec notification de modification de collection
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");
etOnPropertyChanged("Item[]");
dans les méthodes de plage d'ajout / suppression / remplacement pour imiter complètement la ObservableCollection standard.Tout d'abord, veuillez voter et commenter la demande d'API sur le repo .NET.
Voici ma version optimisée de la
ObservableRangeCollection
(version optimisée de James Montemagno l'un ).Il fonctionne très rapidement et est destiné à réutiliser les éléments existants lorsque cela est possible et à éviter les événements inutiles, ou à les regrouper en un seul, lorsque cela est possible. le
ReplaceRange
méthode remplace / supprime / ajoute les éléments requis par les indices appropriés et regroupe les événements possibles.Testé sur l'interface utilisateur Xamarin.Forms avec d'excellents résultats pour les mises à jour très fréquentes de la grande collection (5 à 7 mises à jour par seconde).
Remarque: Étant donné que WPF n'est pas habitué à travailler avec des opérations de plage, il lèvera un
NotSupportedException
, lors de l'utilisationObservableRangeCollection
de ci-dessous dans le travail lié à l'interface utilisateur WPF, comme la liaison à unListBox
etc. (vous pouvez toujours utiliser leObservableRangeCollection<T>
si non lié à l'interface utilisateur) .Cependant, vous pouvez utiliser la
WpfObservableRangeCollection<T>
solution de contournement.La vraie solution serait de créer un
CollectionView
qui sait comment gérer les opérations de portée, mais je n'ai toujours pas eu le temps de l'implémenter.Code RAW - ouvrir en tant que Raw, puis faireCtrl+Apour tout sélectionner, puisCtrl+Cpour copier.
la source
Je pense qu'AddRange est mieux implémenté comme ceci:
Cela vous enregistre une copie de la liste. De plus, si vous souhaitez micro-optimiser, vous pouvez ajouter jusqu'à N éléments et si plus de N éléments ont été ajoutés, effectuez une réinitialisation.
la source
Vous devrez faire attention à lier l'interface utilisateur à votre collection personnalisée - la classe Default CollectionView ne prend en charge qu'une seule notification d'éléments.
la source
Preuve de la nécessité
OnPropertyChanged("Count")
et desOnPropertyChanged("Item[]")
appels afin de se comporter selonObservableCollection
. Notez que je ne sais pas quelles sont les conséquences si vous ne vous embêtez pas!Voici une méthode de test qui montre qu'il existe deux événements PropertyChange pour chaque ajout dans une collection observable normale. Un pour
"Count"
et un pour"Item[]"
.@Shimmy, échangez le standard de votre collection et passez à une plage d'ajout et vous n'obtiendrez aucun changement de propriété. Notez que la modification de collection fonctionne correctement, mais ne fait pas exactement ce que fait ObservableCollection. Donc, le test de la collection shimmy ressemble à ceci:
Pour info, voici le code de InsertItem (également appelé par Add) de ObservableCollection:
la source
Le descendant résumé en C #.
Plus de lecture: http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx
la source
Oui, ajouter votre propre collection d'observables personnalisés serait assez juste. N'oubliez pas de déclencher les événements appropriés, qu'il soit utilisé par l'interface utilisateur pour le moment ou non;) Vous devrez déclencher une notification de modification de propriété pour la propriété "Item []" (requise par le côté WPF et les contrôles liés) ainsi que NotifyCollectionChangedEventArgs avec un ensemble d'articles ajoutés (votre gamme). J'ai fait de telles choses (ainsi que le tri du support et d'autres choses) et je n'ai eu aucun problème avec les couches Presentation et Code Behind.
la source
Comme il peut y avoir un certain nombre d'opérations à faire sur une ObservableCollection, par exemple Effacer d'abord puis AddRange, puis insérer l'élément "All" pour une ComboBox, j'ai fini avec la solution suivante:
Et par exemple comment l'utiliser:
La notification de réinitialisation ne sera appelée qu'une fois après l'exécution du traitement de la liste sous-jacente.
la source
Voici une aide supplémentaire pour la collection modifiée et les problèmes d'interface utilisateur:
la source
ObservableRangeCollection devrait passer un test comme
sinon nous obtenons
lors de l'utilisation avec un contrôle.
Je ne vois pas de solution idéale, mais NotifyCollectionChangedAction.Reset au lieu de Add / Remove résout partiellement le problème. Voir http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx comme cela a été mentionné par net_prog
la source
Voici une modification de la réponse acceptée pour fournir plus de fonctionnalités.
RangeCollection.cs:
Classes d'événements:
Remarque: je n'ai pas soulevé manuellement
OnCollectionChanged
les méthodes de base car il semble uniquement possible de créer unCollectionChangedEventArgs
à l'aide de l'Reset
action. Si vous essayez d'augmenter l'OnCollectionChanged
utilisationReset
pour un seul changement d'élément, votre contrôle d'éléments semblera scintiller, ce que vous souhaitez éviter.la source
Vous pouvez également utiliser ce code pour étendre ObservableCollection:
Ensuite, vous n'avez pas besoin de changer de classe dans le code existant.
la source