13

我正在研究为什么在使用 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强制重新绘制 UI 的能力,或者只是简单地调用每个项目CollectionChanged或更创造性的方式: http: //geekswithblogs.net/NewThingsILearned/存档/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

4 回答 4

4

受 VirtualBlackFox 回答的启发,我查看了CollectionViewILSpy 中类的底层。似乎缺乏对 Range 操作的支持的主要原因是因为在内部使用CollectionView更改日志来集中管理各种挂起的更改并基于每个项目发送消息。

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

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

我完全同意让CollectionView通行证的消费者IList进入NotifyCollectionChangedEventArgs是愚蠢的。它专门拒绝任何长度为 != 1 并在args.NewItems[0]内部进行硬编码的内容。

于 2014-02-07T17:41:52.857 回答
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 中添加对范围事件的支持相关的建议,但如果这对您很重要,您应该提交自己的建议。

于 2014-02-08T19:33:57.243 回答
0

临时解决方案是没有用的。它只会隐藏问题。解决方案可能在于制作事件,您可以在其中向观察者提供一个全新的列表。这样,微软就不必为每种类型的观察者实现有效的范围处理程序。

于 2014-02-12T15:14:10.853 回答
0

我想这主要是出于性能原因。当我看到CollectionView也不接受NewStartingIndexor的 -1 值时,我也很震惊OldStartingIndex。所以基本上,CollectionView 总是想要从项目到它们的索引的映射。但是,它不需要这个映射是精确的(从我的角度来看这很奇怪),允许 NewStartingIndex 小于正确的索引(除非它是-1)。

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

于 2014-02-10T17:16:43.747 回答