Какая коллекция .Net для одновременного добавления нескольких объектов и получения уведомлений?

Рассматривал System.Collections.ObjectModel ObservableCollection<T> класс. Это странно, потому что

  • у него есть метод добавления, который принимает только один элемент. Нет AddRange или аналога.
  • аргументы события уведомления имеют свойство NewItems, которое является IList (объектов .. не T)

Мне здесь нужно добавить пакет объектов в коллекцию, и слушатель также получит пакет как часть уведомления. Мне что-то не хватает с ObservableCollection? Есть ли другой класс, который соответствует моей спецификации?

Обновление: не хочу выкатывать собственные, насколько это возможно. Мне пришлось бы добавить / удалить / изменить и т. Д. Много чего.


Связанный вопрос:
https://stackoverflow.com/questions/670577/observablecollection-doesnt-support-addrange-method-so-i-get-notified-for-each/670579#670579


person Gishu    schedule 11.09.2008    source источник
comment
Гишу, осторожно, если вы привяжете к списку, большинство реализаций здесь взорвутся.   -  person Sam Saffron    schedule 12.05.2009


Ответы (10)


Кажется, что интерфейс 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);
    }
}
person fryguybob    schedule 11.09.2008
comment
Фрагмент кода нуждается в некоторых исправлениях ... в IEnumerable нет ToList (), а AddRange должен принимать ICollection ‹T›, чтобы быть последовательным ... Поскольку мне пришлось пройти через эту временную неудачу к моим грандиозным планам встречи с моими еженедельниками target, разместив мой образец кода .. немного короче. - person Gishu; 14.09.2008
comment
Гишу, метод ToList () - это метод расширения LINQ, доступный в IEnumerable. - person Brad Wilson; 14.09.2008
comment
Понятно ... Вам нужно установить параметры проекта для использования .NET 3.5 и добавить ссылку на сборку LINQ и директиву using для ее получения. - person Gishu; 28.09.2008
comment
я пытаюсь сделать что-то почти идентичное ... даже изменил его, чтобы использовать ваш метод AddRange для тестирования, но я все еще получаю, что элемент не существует в коллекции tiny.cc/iim24 - person Sonic Soul; 21.07.2010
comment
Опубликованный код не работает, если вы используете AddRange с более чем одним элементом. Событие CollectionChanged не может обрабатываться вручную с более чем одним элементом в измененном списке - вы получаете Range actions are not supported исключение - person Rachel; 16.09.2011

Что ж, идея такая же, как у 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);
        }

    }
}
person Gishu    schedule 14.09.2008
comment
Я пробовал этот подход раньше. К сожалению, это не сработает для привязок WPF, потому что уведомления для некоторых элементов не поддерживаются. См. эту ошибку на MS Connect - person Thomas Levesque; 30.09.2010

Не только System.Collections.ObjectModel.Collection<T> хороший выбор, но и в справочной документации есть пример о том, как переопределить его различные защищенные методы, чтобы получить уведомление. (Прокрутите вниз до примера 2.)

person Ryan Lundy    schedule 12.09.2008

Если вы используете любую из вышеперечисленных реализаций, которые отправляют команду добавления диапазона и связывают наблюдаемую коллекцию со списком, вы получите эту неприятную ошибку.

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));
    }
person Sam Saffron    schedule 12.05.2009
comment
вы можете добавить добавление для каждого элемента, но ваш пользовательский интерфейс будет зависать - person Sam Saffron; 12.05.2009
comment
На самом деле, нет - пользовательский интерфейс WPF обрабатывается партиями, как пост-сообщение. Эта функция завершится до того, как пользовательский интерфейс будет проверен на предмет обновления. - person cunningdave; 10.06.2011
comment
То же исключение произошло здесь, кто-нибудь поможет нам решить эту проблему - person Hemalatha M.R.; 12.06.2020

Я много раз сталкивался с подобными вопросами, и мне интересно, почему даже Microsoft продвигает ObservableCollection везде, где еще есть лучшая коллекция, уже доступная, а именно ...

BindingList<T>

Это позволяет отключать уведомления и выполнять массовые операции, а затем включать уведомления.

person Akash Kava    schedule 30.09.2010
comment
Очень хороший момент о BindingList<T>, но, к сожалению, он реализует IBindingList<T> вместо _3 _... Последний необходим для приложений WPF, использующих mvvm. - person RonnBlack; 02.06.2016

Если вы хотите наследовать от какой-либо коллекции, вам, вероятно, лучше наследовать от System.Collections.ObjectModel.Collection, потому что он предоставляет виртуальные методы для переопределения. Если вы пойдете по этому пути, вам придется скрыть методы из списка.

Мне не известны какие-либо встроенные коллекции, которые предоставляют эту функциональность, хотя я бы хотел, чтобы меня поправили :)

person David Mohundro    schedule 11.09.2008

Другое решение, похожее на шаблон 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
    }
}
person Mo0gles    schedule 12.01.2012

Наследовать от List ‹T> и переопределить методы Add () и AddRange () для создания события?

person Joel Coehoorn    schedule 11.09.2008

Взгляните на Наблюдаемую коллекцию с помощью методов AddRange, RemoveRange и Replace range как в C #, так и в VB.

В VB: реализация INotifyCollectionChanging.

person Shimmy Weitzhandler    schedule 14.07.2009

Для быстрого добавления вы можете использовать:

((List<Person>)this.Items).AddRange(NewItems);
person Boris    schedule 24.11.2009