Какая коллекция .Net для одновременного добавления нескольких объектов и получения уведомления?
Рассматривал класс System.Collections.ObjectModel ObservableCollection<T>
. Это странно, потому что
- у него есть метод добавления, который принимает только один. Нет AddRange или эквивалент.
- Аргументы события уведомления имеют свойство NewItems, которое является IList (объектов.. не T)
Моя потребность в том, чтобы добавить партию объектов в коллекцию, а слушатель также получает пакет как часть уведомления. Я что-то пропустил с ObservableCollection? Есть ли другой класс, который соответствует моей спецификации?
Обновление: не нужно откатывать свои собственные, насколько это возможно. Мне нужно будет добавить/удалить/изменить и т.д. Много вещей.
Связанный Q:
qaru.site/info/47118/...
Ответы
Ответ 1
Кажется, что интерфейс INotifyCollectionChanged
позволяет обновлять, когда было добавлено несколько элементов, поэтому я не уверен, почему ObservableCollection<T>
не имеет AddRange
. Вы можете создать метод расширения для AddRange
, но это вызовет событие для каждого добавленного элемента. Если это неприемлемо, вы должны унаследовать от ObservableCollection<T>
следующим образом:
public class MyObservableCollection<T> : ObservableCollection<T>
{
// matching constructors ...
bool isInAddRange = false;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
// intercept this when it gets called inside the AddRange method.
if (!isInAddRange)
base.OnCollectionChanged(e);
}
public void AddRange(IEnumerable<T> items)
{
isInAddRange = true;
foreach (T item in items)
Add(item);
isInAddRange = false;
var e = new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add,
items.ToList());
base.OnCollectionChanged(e);
}
}
Ответ 2
Ну, идея такая же, как у fryguybob - что странно, что ObservableCollection немного наполовину сделано. Событие утверждает, что эта вещь даже не использует Generics.. заставляя меня использовать IList (что так.. вчера:)
Протестированный сниппет следует за...
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace MyNamespace
{
public class ObservableCollectionWithBatchUpdates<T> : ObservableCollection<T>
{
public void AddRange(ICollection<T> obNewItems)
{
IList<T> obAddedItems = new List<T>();
foreach (T obItem in obNewItems)
{
Items.Add(obItem);
obAddedItems.Add(obItem);
}
NotifyCollectionChangedEventArgs obEvtArgs = new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add,
obAddedItems as System.Collections.IList);
base.OnCollectionChanged(obEvtArgs);
}
}
}
Ответ 3
Не только System.Collections.ObjectModel.Collection<T>
хорошая ставка, но и в справочной документации пример о том, как переопределить различные защищенные методы в чтобы получить уведомление. (Прокрутите вниз до примера 2.)
Ответ 4
Если вы используете любую из вышеперечисленных реализаций, которые отправляют команду добавления диапазона и связывают наблюдаемое копирование с списком, вы получите эту неприятную ошибку.
NotSupportedException
at System.Windows.Data.ListCollectionView.ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs e)
at System.Windows.Data.ListCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
В реализации, которую я использовал, используется событие Reset, которое более равномерно реализовано вокруг рамки WPF:
public void AddRange(IEnumerable<T> collection)
{
foreach (var i in collection) Items.Add(i);
OnPropertyChanged("Count");
OnPropertyChanged("Item[]");
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
Ответ 5
Я много раз встречал этот вопрос, и мне интересно, почему даже Microsoft продвигает ObservableCollection везде, где еще есть более эффективная коллекция, которая уже доступна.
BindingList<T>
Позволяет отключать уведомления и выполнять массовые операции, а затем включать уведомления.
Ответ 6
Если вы хотите наследовать из какой-либо коллекции, вам, вероятно, лучше наследовать от System.Collections.ObjectModel.Collection, потому что она предоставляет виртуальные методы для переопределения. Вам нужно будет отключить методы из списка, если вы идете по этому маршруту.
Я не знаю каких-либо встроенных коллекций, которые предоставляют эту функциональность, хотя я бы рад, что исправил:)
Ответ 7
Наследовать из списка <T> и переопределить методы Add() и AddRange(), чтобы поднять событие?
Ответ 8
Другое решение, подобное шаблону CollectionView:
public class DeferableObservableCollection<T> : ObservableCollection<T>
{
private int deferLevel;
private class DeferHelper<T> : IDisposable
{
private DeferableObservableCollection<T> owningCollection;
public DeferHelper(DeferableObservableCollection<T> owningCollection)
{
this.owningCollection = owningCollection;
}
public void Dispose()
{
owningCollection.EndDefer();
}
}
private void EndDefer()
{
if (--deferLevel <= 0)
{
deferLevel = 0;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
public IDisposable DeferNotifications()
{
deferLevel++;
return new DeferHelper<T>(this);
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (deferLevel == 0) // Not in a defer just send events as normally
{
base.OnCollectionChanged(e);
} // Else notify on EndDefer
}
}
Ответ 9
Взгляните на Наблюдаемую коллекцию с помощью методов AddRange, RemoveRange и Replace range как на С#, так и на VB.
В VB: реализация INotifyCollectionChanging.
Ответ 10
Для быстрого добавления вы можете использовать:
((List<Person>)this.Items).AddRange(NewItems);
Ответ 11
Метод расширения Человек на помощь!
/// <summary>
/// Adds all given items to the collection
/// </summary>
/// <param name="collection">The collection.</param>
/// <param name="toAdd">Objects to add.</param>
public static void AddAll<T>(this IList<T> collection, params T[] toAdd)
{
foreach (var o in toAdd)
collection.Add(o);
}