Zapoznaj się ze zaktualizowaną i zoptymalizowaną wersją C # 7 . Nie chciałem usuwać wersji VB.NET, więc po prostu opublikowałem to w osobnej odpowiedzi.
Wygląda na to, że nie jest obsługiwany, wdrożyłem samodzielnie, do Twojej wiadomości, mam nadzieję, że będzie pomocny:
Po zaktualizowaniu wersji VB i odtąd to wywołuje zdarzenie przed zmianą kolekcji, dzięki czemu można żałować (przydatne podczas korzystania z DataGrid
, ListView
i wiele innych, które można pokazać „Czy na pewno” potwierdzenie dla użytkownika), zaktualizowaną VB wersja znajduje się na dole tej wiadomości .
Proszę przyjąć moje przeprosiny, że ekran jest zbyt wąski, aby pomieścić mój kod, też mi się to nie podoba.
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
DO#:
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) { }
}
Aktualizacja - obserwowalny zbiór z powiadomieniem o zmianie kolekcji
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");
iOnPropertyChanged("Item[]");
w metodach dodaj / usuń / zamień zakres, aby całkowicie naśladować standardowe ObservableCollection.Przede wszystkim proszę głos i komentarz na żądanie API na repo .NET.
Oto mój zoptymalizowana wersja
ObservableRangeCollection
(wersja zoptymalizowana Jamesa Montemagno za jednego ).Działa bardzo szybko i ma na celu ponowne wykorzystanie istniejących elementów, gdy jest to możliwe, i uniknięcie niepotrzebnych zdarzeń lub łączenie ich w jeden, jeśli to możliwe. Plik
ReplaceRange
zastępuje metoda / usuwanie / dodaje wymagane elementy przez odpowiednie indeksy i partiami możliwych zdarzeń.Testowane w interfejsie użytkownika platformy Xamarin.Forms z doskonałymi wynikami w przypadku bardzo częstych aktualizacji dużej kolekcji (5-7 aktualizacji na sekundę).
Uwaga: ponieważ WPF nie jest przyzwyczajony do pracy z operacjami zakresu, wyrzuci a
NotSupportedException
, gdy używaszObservableRangeCollection
od dołu w pracy związanej z interfejsem WPF, na przykład wiążąc go z aListBox
itp. (Nadal możesz użyć,ObservableRangeCollection<T>
jeśli nie jest powiązany z interfejsem użytkownika) .Możesz jednak zastosować
WpfObservableRangeCollection<T>
obejście.Prawdziwym rozwiązaniem byłoby stworzenie narzędzia,
CollectionView
które wie, jak radzić sobie z operacjami na zasięgu, ale nadal nie miałem czasu, aby to zaimplementować.Kod RAW - otwórz jako Raw, następnie zróbCtrl+,Aaby zaznaczyć wszystko, a następnieCtrl+,Caby skopiować.
źródło
Myślę, że AddRange jest lepiej zaimplementowany w ten sposób:
Zapisuje kopię listy. Również jeśli chcesz przeprowadzić mikro-optymalizację, możesz dodać do N elementów, a jeśli dodano więcej niż N elementów, zresetuj.
źródło
Należy zachować ostrożność, wiążąc interfejs użytkownika z kolekcją niestandardową - klasa Default CollectionView obsługuje tylko pojedyncze powiadomienia o elementach.
źródło
Dowód potrzeby
OnPropertyChanged("Count")
iOnPropertyChanged("Item[]")
wzywa, aby zachowywać się zgodnie zObservableCollection
. Zauważ, że nie wiem, jakie są konsekwencje, jeśli nie zawracasz sobie głowy!Oto metoda testowa, która pokazuje, że istnieją dwa zdarzenia PropertyChange dla każdego dodatku w normalnej obserwowalnej kolekcji. Jeden za
"Count"
i jeden za"Item[]"
.@Shimmy, zamień standard dla swojej kolekcji i zmień na zakres dodawania, a otrzymasz zero zmian PropertyChanges. Zauważ, że zmiana kolekcji działa dobrze, ale nie robi dokładnie tego, co robi ObservableCollection. Tak więc kolekcja Test for shimmy wygląda następująco:
FYI tutaj jest kod z InsertItem (wywoływany również przez Add) z ObservableCollection:
źródło
C # podsumowany potomek.
Więcej do czytania: http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx
źródło
Tak, dodanie własnej kolekcji, którą można obserwować, byłoby w porządku. Nie zapomnij wywołać odpowiednich zdarzeń, niezależnie od tego, czy jest używany przez interfejs użytkownika w danym momencie, czy nie;) Będziesz musiał zgłosić powiadomienie o zmianie właściwości dla właściwości „Item []” (wymagane przez kontrolki po stronie WPF i powiązane), a także NotifyCollectionChangedEventArgs z zestawem przedmiotów dodanych (Twój zakres). Robiłem takie rzeczy (a także obsługę sortowania i kilka innych rzeczy) i nie miałem problemów z warstwami prezentacji i kodu.
źródło
Ponieważ może istnieć wiele operacji do wykonania na ObservableCollection, na przykład najpierw Wyczyść, a następnie AddRange, a następnie wstaw element „All” dla ComboBox, otrzymałem następujące rozwiązanie:
I przykład, jak go używać:
Powiadomienie o zresetowaniu zostanie wywołane tylko raz po zakończeniu przetwarzania listy źródłowej przez wykonanie.
źródło
Oto dodatkowa pomoc dotycząca zmian w kolekcji i problemów z interfejsem użytkownika:
źródło
ObservableRangeCollection powinien przejść test podobny do
inaczej otrzymamy
podczas używania z kontrolką.
Nie widzę idealnego rozwiązania, ale NotifyCollectionChangedAction.Reset zamiast Dodaj / Usuń częściowo rozwiązuje problem. Zobacz http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx, o czym wspomniał net_prog
źródło
Oto modyfikacja zaakceptowanej odpowiedzi, aby zapewnić większą funkcjonalność.
RangeCollection.cs:
Klasy wydarzeń:
Uwaga: nie podbiłem ręcznie
OnCollectionChanged
w metodach podstawowych, ponieważ wydaje się, że możliwe jest tylko utworzenie zaCollectionChangedEventArgs
pomocąReset
akcji. Jeśli spróbujesz podbićOnCollectionChanged
zaReset
pomocą jednej zmiany przedmiotu, kontrolka przedmiotów będzie migotać, czego chcesz uniknąć.źródło
Możesz również użyć tego kodu, aby rozszerzyć ObservableCollection:
Wtedy nie musisz zmieniać klasy w istniejącym kodzie.
źródło