Пожалуйста, обратитесь к обновленной и оптимизированной версии C # 7 . Я не хотел удалять версию VB.NET, поэтому я просто разместил ее в отдельном ответе.
Кажется, это не поддерживается, я реализовал сам, к вашему сведению, надеюсь, это будет полезно:
Я обновил версию VB, и теперь она вызывает событие перед изменением коллекции, так что вы можете сожалеть (полезно при использовании с DataGrid
, ListView
и многое другое, что вы можете показать подтверждение «Вы уверены» пользователю), обновленный VB версия находится внизу этого сообщения .
Пожалуйста, примите мои извинения, что экран слишком узок, чтобы содержать мой код, мне это тоже не нравится.
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) { }
}
Обновление - Наблюдаемый диапазон коллекции с уведомлением об изменении коллекции
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");
иOnPropertyChanged("Item[]");
в диапазоне добавления / удаления / замены методы полностью имитировать стандартный ObservableCollection.Прежде всего, пожалуйста , голосовать и комментировать по запросу API на .NET репо.
Вот моя оптимизированная версия
ObservableRangeCollection
(оптимизированная версия Джеймса Montemagno в одном ).Он работает очень быстро и предназначен для повторного использования существующих элементов, когда это возможно, и позволяет избежать ненужных событий или объединить их в один, когда это возможно.
ReplaceRange
методе заменяет / удаляет / добавляет необходимые элементы от соответствующих показателей и партий возможных событий.Протестировано на пользовательском интерфейсе Xamarin.Forms с отличными результатами для очень частых обновлений большой коллекции (5-7 обновлений в секунду).
Примечание. Поскольку WPF не привык работать с операциями с диапазонами, он будет использовать a
NotSupportedException
при использовании приведенногоObservableRangeCollection
ниже в работе, связанной с пользовательским интерфейсом WPF, такой как привязка к объектуListBox
и т. Д. (Вы все равно можете использовать операторObservableRangeCollection<T>
if, если он не привязан к пользовательскому интерфейсу) ,Однако вы можете использовать
WpfObservableRangeCollection<T>
обходной путь.Реальное решение было бы создать,
CollectionView
который знает, как справляться с дальномерными операциями, но у меня все еще не было времени, чтобы реализовать это.RAW Code - откройте как Raw, затем сделайтеCtrl+,Aчтобы выбрать все, затемCtrl+,Cчтобы скопировать.
источник
Я думаю, что AddRange лучше реализован так:
Это экономит вам копию списка. Также, если вы хотите микрооптимизировать, вы можете добавить до N элементов, а если больше чем N элементов, добавленных, сделать сброс.
источник
Вы должны быть осторожны, связывая пользовательский интерфейс с вашей пользовательской коллекцией - класс Default CollectionView поддерживает только одно уведомление об элементах.
источник
Доказательство необходимости
OnPropertyChanged("Count")
иOnPropertyChanged("Item[]")
звонки для того, чтобы вести себя согласноObservableCollection
. Обратите внимание, что я не знаю, каковы будут последствия, если вы не будете беспокоиться!Вот тестовый метод, который показывает, что есть два события PropertyChange для каждого добавления в обычной наблюдаемой коллекции. Один за
"Count"
и один за"Item[]"
.@Shimmy, поменяйте стандарт для своей коллекции и измените диапазон добавления, и вы получите ноль PropertyChanges. Обратите внимание, что изменение коллекции работает нормально, но не совсем так, как ObservableCollection. Итак, тест на коллекцию шимми выглядит так:
К вашему сведению, это код из InsertItem (также вызываемый Add) из ObservableCollection:
источник
C # обобщенный потомок.
Больше чтения: http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx
источник
Да, добавление вашей собственной пользовательской наблюдаемой коллекции будет достаточно справедливым. Не забывайте вызывать соответствующие события независимо от того, используется ли он в данный момент пользовательским интерфейсом или нет;) Вам нужно будет подать уведомление об изменении свойства для свойства «Item []» (требуется для сторонних и связанных элементов управления WPF), а также для NotifyCollectionChangedEventArgs с добавленным набором предметов (ваш ассортимент). Я делал такие вещи (а также поддержку сортировки и некоторые другие вещи), и у меня не было проблем со слоями Presentation и Code Behind.
источник
Так как в ObservableCollection может быть несколько операций, например, сначала очистить, затем добавить AddRange, а затем вставить элемент «Все» для ComboBox, и я получил следующее решение:
И пример, как его использовать:
Уведомление сброса будет вызвано только один раз после завершения выполнения базового списка.
источник
Вот некоторая дополнительная помощь для изменения коллекции и проблем пользовательского интерфейса:
источник
ObservableRangeCollection должен пройти тест как
в противном случае мы получаем
при использовании с контролем.
Я не вижу идеального решения, но NotifyCollectionChangedAction.Reset вместо Add / Remove частично решают проблему. См http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx , как было упомянуто net_prog
источник
Вот модификация принятого ответа для обеспечения большей функциональности.
RangeCollection.cs:
Классы мероприятий:
Примечание: я не вызывал вручную
OnCollectionChanged
в базовых методах, потому что представляется возможным только создать действие сCollectionChangedEventArgs
использованиемReset
. Если вы попытаетесь поднять,OnCollectionChanged
используяReset
для изменения одного предмета, ваш элемент управления будет мерцать, чего вы хотите избежать.источник
Вы также можете использовать этот код для расширения ObservableCollection:
Тогда вам не нужно менять класс в существующем коде.
источник