Подтвердить что ты не робот

Эффективное добавление диапазона значений в ObservableCollection

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

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

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

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

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

4b9b3361

Ответ 1

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);  
}

Ответ 2

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

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

Этот ответ не показывал мне новые записи в 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[]"));
    }
}

Ответ 4

Пример: Желаемые этапы 0,10,20,30,40,50,60,70,80,90,100 - > min = 0, max = 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())
    );