【问题标题】:Adding a range of values to an ObservableCollection efficiently有效地将一系列值添加到 ObservableCollection
【发布时间】:2011-12-22 16:38:47
【问题描述】:

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

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

因此,理想情况下,我希望一次插入整个项目范围,这意味着只对基础列表进行一次随机播放,并希望有一个 CollectionChanged 通知(可能是“重置”)。

Collection<T> 没有公开任何这样做的方法。 List<T> 具有 InsertRange(),但 IList<T>Collection<T> 通过其 Items 属性公开却没有。

有没有办法做到这一点?

【问题讨论】:

标签: c# performance collections insert observablecollection


【解决方案1】:

为了使上述答案在不使用反射派生新基类的情况下有用,这里有一个示例:

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

【讨论】:

  • 继承有什么问题?为什么要使用反射而不是派生新类?
  • 继承当然没有错。这只是一个替代方案。如果您有大量遗留代码并且您的序列化(二进制)取决于 ObservableCollection 类型,它可能会很有用。在这种情况下,扩展 ObservableCollection 是一种更简单、更实用的选择。
  • 嗨@outbred。抱歉,您如何“使用”您的示例。谢谢
  • @NevilleDastur - 像这样使用它: var coll = new ObservableCollection(); coll.InsertRange();这是一个扩展方法,所以只要确保你使用的是声明扩展方法的命名空间,你应该一切顺利。
  • 我投了赞成票,因为扩展方法允许我使用而无需将我的集合更改为新类。
【解决方案2】:

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

【讨论】:

  • 这个其他answer 建议添加以下代码以通知计数和索引器属性的更改:this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
【解决方案3】:

这个answer 没有显示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 --> 最小=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())
    );

【讨论】:

    猜你喜欢
    • 2019-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-10
    • 1970-01-01
    • 2021-02-12
    • 2015-08-18
    • 1970-01-01
    相关资源
    最近更新 更多