Consulte a versão C # 7 atualizada e otimizada . Como não queria remover a versão do VB.NET, publiquei-a em uma resposta separada.
Parece que não é suportado, eu implementei eu mesmo, para sua informação, espero que seja útil:
Atualizei a versão do VB e, a partir de agora, ele gera um evento antes de alterar a coleção, para que você possa se arrepender (útil ao usar com DataGrid
, ListView
e muito mais, que você possa mostrar uma confirmação "Você tem certeza" para o usuário), o VB atualizado versão está na parte inferior desta mensagem .
Aceite minhas desculpas pelo fato de a tela ser muito estreita para conter meu código. Também não gosto.
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) { }
}
Atualização - coleção de intervalos observáveis com notificação de alteração de coleção
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");
eOnPropertyChanged("Item[]");
nos métodos adicionar / remover / substituir o intervalo para imitar completamente o ObservableCollection padrão.Antes de tudo, vote e comente a solicitação da API no repositório .NET.
Aqui está a minha versão otimizada do
ObservableRangeCollection
(versão otimizada do James Montemagno um ).Seu desempenho é muito rápido e visa reutilizar elementos existentes quando possível e evitar eventos desnecessários ou agrupá-los em um, quando possível. O
ReplaceRange
método substitui / remove / adiciona os elementos necessários pelos índices apropriados e agrupa em lotes os possíveis eventos.Testado na interface do Xamarin.Forms com ótimos resultados para atualizações muito frequentes da coleção grande (de 5 a 7 atualizações por segundo).
Nota: Como o WPF não está acostumado a trabalhar com operações de intervalo, ele emitirá um
NotSupportedException
, ao usar oObservableRangeCollection
abaixo no trabalho relacionado à interface do usuário do WPF, como vinculá-lo a umListBox
etc. (você ainda pode usar oObservableRangeCollection<T>
se não estiver vinculado à interface do usuário) .No entanto, você pode usar a
WpfObservableRangeCollection<T>
solução alternativa.A solução real seria criar um
CollectionView
que saiba lidar com operações de alcance, mas ainda não tive tempo para implementar isso.Código RAW - abra como Raw e façaCtrl+Apara selecionar tudo eCtrl+Cpara copiar.
fonte
Eu acho que o AddRange é melhor implementado assim:
Isso economiza uma cópia da lista. Além disso, se você quiser otimizar otimizar, poderá adicionar até N itens e se mais de N itens forem adicionados, redefinir.
fonte
Você precisará ter cuidado ao vincular a interface do usuário à sua coleção personalizada - a classe Default CollectionView oferece suporte apenas à notificação única de itens.
fonte
Prova de necessidade
OnPropertyChanged("Count")
eOnPropertyChanged("Item[]")
chamadas para se comportar conformeObservableCollection
. Note que eu não sei quais são as consequências se você não se incomodar!Aqui está um método de teste que mostra que existem dois eventos PropertyChange para cada adição em uma coleção observável normal. Um para
"Count"
e um para"Item[]"
.@Shimmy, troque o padrão da sua coleção e mude para um intervalo adicional e você obterá zero PropertyChanges. Observe que a alteração da coleção funciona bem, mas não está fazendo exatamente o que ObservableCollection faz. Portanto, teste para coleção shimmy é assim:
Para sua informação, aqui está o código de InsertItem (também chamado por Add) de ObservableCollection:
fonte
O C # resumiu o descendente.
Mais informações: http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx
fonte
Sim, adicionar sua própria coleção observável personalizada seria bastante justo. Não se esqueça de gerar eventos apropriados, independentemente de serem usados pela interface do usuário no momento ou não;) Você precisará gerar uma notificação de alteração de propriedade para a propriedade "Item []" (exigida pelos controles de lado e vinculados do WPF), bem como para NotifyCollectionChangedEventArgs com um conjunto de itens adicionados (seu intervalo). Fiz essas coisas (além de classificar o suporte e outras coisas) e não tive problemas com as camadas Presentation e Code Behind.
fonte
Como pode haver várias operações a serem executadas em um ObservableCollection, por exemplo, Clear primeiro, AddRange e depois insira o item "All" para um ComboBox. Acabei com a seguinte solução:
E exemplo de como usá-lo:
A notificação Redefinir será chamada apenas uma vez após a conclusão da execução do processamento da lista subjacente.
fonte
Aqui está uma ajuda adicional para problemas de interface alterada e coleção:
fonte
ObservableRangeCollection deve passar em um teste como
caso contrário, temos
enquanto estiver usando com um controle.
Não vejo uma solução ideal, mas NotifyCollectionChangedAction.Reset em vez de Adicionar / remover resolve parcialmente o problema. Consulte http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx conforme mencionado por net_prog
fonte
Aqui está uma modificação da resposta aceita para fornecer mais funcionalidade.
RangeCollection.cs:
Eventos Classes:
Nota: Eu não levantei manualmente
OnCollectionChanged
os métodos base, porque parece apenas possível criar umCollectionChangedEventArgs
usando aReset
ação. Se você tentar aumentarOnCollectionChanged
usandoReset
uma única alteração de item, seu controle de itens parecerá piscar, o que é algo que você deseja evitar.fonte
Você também pode usar esse código para estender ObservableCollection:
Então você não precisa alterar a classe no código existente.
fonte