Эффективное добавление диапазона значений в ObservableCollection - PullRequest
28 голосов
/ 22 декабря 2011

У меня есть ObservableCollection элементов, привязанных к элементу управления списком, на мой взгляд.

У меня есть ситуация, когда мне нужно добавить порцию значений в начало коллекции.Документация Collection<T>.Insert определяет каждую вставку как операцию O (n), и каждая вставка также генерирует уведомление CollectionChanged.

Поэтому в идеале я хотел бы вставить весь диапазон элементов за один ход, означая толькоодна случайная последовательность из базового списка и, возможно, одно CollectionChanged уведомление (предположительно, «сброс»).

Collection<T> не предоставляет никакого способа сделать это.List<T> имеет InsertRange(), но IList<T>, который Collection<T> предоставляет через свое свойство Items, не делает.

Есть ли вообще какой-либо способ сделать это?

Ответы [ 4 ]

52 голосов
/ 22 декабря 2011

ObservableCollection предоставляет защищенное свойство Items, которое является базовой коллекцией без семантики уведомлений. Это означает, что вы можете создать коллекцию, которая делает то, что вы хотите, наследуя ObservableCollection:

class RangeEnabledObservableCollection<T> : ObservableCollection<T>
{
    public void InsertRange(IEnumerable<T> items) 
    {
        this.CheckReentrancy();
        foreach(var item in items)
            this.Items.Add(item);
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}

Использование:

void Main()
{
    var collection = new RangeEnabledObservableCollection<int>();
    collection.CollectionChanged += (s,e) => Console.WriteLine("Collection changed");
    collection.InsertRange(Enumerable.Range(0,100));
    Console.WriteLine("Collection contains {0} items.", collection.Count);  
}
7 голосов
/ 02 мая 2014

Чтобы сделать приведенный выше ответ полезным без получения нового базового класса с помощью отражения, вот пример:

public static void InsertRange<T>(this ObservableCollection<T> collection, IEnumerable<T> items)
{
  var enumerable = items as List<T> ?? items.ToList();
  if (collection == null || items == null || !enumerable.Any())
  {
    return;
  }

  Type type = collection.GetType();

  type.InvokeMember("CheckReentrancy", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, collection, null);
  var itemsProp = type.BaseType.GetProperty("Items", BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance);
  var privateItems = itemsProp.GetValue(collection) as IList<T>;
  foreach (var item in enumerable)
  {
    privateItems.Add(item);
  }

  type.InvokeMember("OnPropertyChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null,
    collection, new object[] { new PropertyChangedEventArgs("Count") });

  type.InvokeMember("OnPropertyChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null,
    collection, new object[] { new PropertyChangedEventArgs("Item[]") });

  type.InvokeMember("OnCollectionChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, 
    collection, new object[]{ new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)});
}
3 голосов
/ 26 марта 2017

Этот ответ не показывал мне новые записи в DataGrid. Это OnCollectionChanged работает для меня:

public class SilentObservableCollection<T> : ObservableCollection<T>
{
    public void AddRange(IEnumerable<T> enumerable)
    {
        CheckReentrancy();

        int startIndex = Count;

        foreach (var item in enumerable)
            Items.Add(item);

        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(enumerable), startIndex));
        OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
    }
}
0 голосов
/ 14 июля 2016

Пример: желаемые шаги 0,10,20,30,40,50,60,70,80,90,100 -> мин = 0, макс = 100, шаги = 11

    static int min = 0;
    static int max = 100;
    static int steps = 11; 

    private ObservableCollection<string> restartDelayTimeList = new ObservableCollection<string> (
        Enumerable.Range(0, steps).Select(l1 => (min + (max - min) * ((double)l1 / (steps - 1))).ToString())
    );
...