Какая коллекция .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
    }
}

Ответ 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);
    }