CollectionChanged和IList of Items - 为何遇到困难

时间:2014-02-04 16:04:39

标签: c# .net collections observablecollection inotifycollectionchanged

我正在调查使用IList参数调用CollectionChanged时ObservableCollection/ListCollectionView/CollectionView引发 NotSuportedException 的主题。

//Throws an exception
private void collectionChanged_Removed(IList items)
{
    if (CollectionChanged != null)
        CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items));
}

我找到了几个网页,讨论了这个主题,他们建议使用Reset能力强制完全重绘用户界面,或者只是简单地调用每个项目CollectionChanged或更多创造性的方式:http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx

我只是找不到为什么? 对我来说,为什么会出现这种情况毫无意义。

我们全部在我们的开发周期的某个时刻出现这种缺乏功能的可能性,因为当您想要快速添加多个项目时,Add方法只需要大量的开销,将随时实施(.Net 5,C#6 ......)。

修改

在我的具体案例中,我写了自己的课程:

public class ObservableList<T> : IList<T>, IList, IEnumerable<T>,
    INotifyCollectionChanged
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    //other stuff...
}

仍然会抛出上述 NotSupportedException

4 个答案:

答案 0 :(得分:4)

受VirtualBlackFox的回答启发,我在ILSpy的CollectionView类的引擎盖下看了一眼。似乎为什么缺乏对Range操作的支持的主要原因是CollectionView内部使用更改日志来集中管理所有类型的挂起更改并在每个/项目上调度消息基础。

出于其目的,CollectionView可以存储1000个记录,这些记录与表示其基础数据的多个UI控件同时使用。因此,添加或删除记录必须在原子基础上完成,以维护访问视图信息的UI控件的完整性。您无法使用批量更改事件与多个UI订阅者同步增量更改,而无需将CollectionView的分组,排序和过滤功能传递到使用它的UI控件上。

CollectionView也来自System.Windows.Threading.Dispatcher,因此问题可能与它如何管理其线程上的工作项有关。调用路径包括一个受保护的ProcessCollectionChanged方法,该方法专门处理UI线程上的各个更改。因此,更新范围可能会干扰它用于与使用它的UI元素交互的整个线程模型。

我完全同意让CollectionView传递给IListNotifyCollectionChangedEventArgs的消费者是愚蠢的。它特别拒绝任何长度为!= 1的内容和内部args.NewItems[0]的硬代码。

答案 1 :(得分:2)

正如@nmclean在评论中所说,问题不在发送CollectionChanged的集合中,而是在接收端。

如果您查看ListCollectionView的代码(例如,使用DotPeek或新版本的visual studio,您可以访问参考源代码),您会注意到每次附加的集合更改它调用在有多个元素发生更改时抛出的方法ValidateCollectionChangedEventArgs

private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs e)
{
  switch (e.Action)
  {
    case NotifyCollectionChangedAction.Add:
      if (e.NewItems.Count == 1)
        break;
      else
        throw new NotSupportedException(System.Windows.SR.Get("RangeActionsNotSupported"));
...

该类的其余部分及其CollectionView基类已经是大型动物(参考源代码中发布的源代码中有2710和2027行),因此Microsoft可能希望避免支持该集合的复杂案例他们建议不要创建。

(处理集合更改的方法在参考源代码中已经是141行,并且添加多元素支持将使其增长更多或需要仔细拆分并可能破坏其他内容......)

我没有找到任何与在Microsoft Connect中添加对范围事件的支持相关联的建议,但如果对您来说很重要,则应提交自己的建议。

答案 2 :(得分:0)

我想这主要是出于性能原因。当我看到CollectionView也不接受NewStartingIndexOldStartingIndex的-1值时,我也感到震惊。所以基本上,CollectionView总是想要从项目到它们的索引的映射。但是,它并不要求这种映射是准确的(从我的观点来看这很奇怪),允许NewStartingIndex小于正确的索引(除非它是-1)。

我认为问题的根源在于微软内部的大部分仍然认为列表是实现集合的唯一方式,当然这是不正确的。在这里,NotifyCollectionChangedEventArgs的创建者考虑了替代方案(例如链接列表或散列集合),但UI人员没有。或者至少他们不想支持这些集合,因为它们很少出现。

答案 3 :(得分:0)

临时解决方案毫无用处。它只隐藏了问题。 解决方案可能在于为您提供向观察者提供全新列表的事件。这样,Microsoft就不必为每种类型的观察者实现有效的范围处理程序。