有效地向ObservableCollection添加一系列值

Gaz*_*yer 28 c# collections performance insert observablecollection

我有一个ObservableCollection项目绑定到我的视图中的列表控件.

我有一种情况需要在集合的开头添加一大块值. Collection<T>.Insertdocumentation将每个插入指定为O(n)操作,每个插入也生成一个CollectionChanged通知.

因此,我理想地希望在一次移动中插入整个范围的项目,这意味着只有一个基础列表的随机播放,并且希望有一个CollectionChanged通知(可能是"重置").

Collection<T>不公开任何执行此操作的方法.List<T>InsertRange(),但是IList<T>,这Collection<T>暴露了通过其Items属性不会.

有没有办法做到这一点?

dri*_*iis 52

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

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);  
}
Run Code Online (Sandbox Code Playgroud)

  • 另一个[答案](http://stackoverflow.com/questions/13302933/how-to-avoid-firing-observablecollection-collectionchanged-multiple-times-when-r)建议添加以下代码以通知对count和indexer属性的更改:`this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));``this.OnPropertyChanged(new PropertyChangedEventArgs("Item []"));` (6认同)

out*_*red 7

为了使上述答案有用,无需使用反射派生新的基类,这是一个例子:

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)});
}
Run Code Online (Sandbox Code Playgroud)

  • 当然,继承没有错.这只是一个替代方案.如果您有大量遗留代码并且序列化(二进制)依赖于ObservableCollection类型,那么它可能会很有用.在这种情况下,只需扩展ObservableCollection就更容易,更实用. (3认同)