ObservableCollection和BindingList的区别

261

我想了解ObservableCollectionBindingList之间的区别,因为我已经使用过两者来通知源中是否有添加/删除更改,但实际上我不知道何时应该优先选择其中一个。

为什么我会选择以下其中之一?

ObservableCollection<Employee> lstEmp = new ObservableCollection<Employee>();
或者
BindingList<Employee> lstEmp = new BindingList<Employee>();
5个回答

305
一个ObservableCollection可以像任何集合一样从UI更新。真正的区别非常简单: ObservableCollection<T>实现了INotifyCollectionChanged,当集合发生更改时提供通知(你猜对了 ^^)它允许绑定引擎在更新ObservableCollection时更新UI。
然而,BindingList<T>实现了IBindingListIBindingList提供有关集合更改的通知,但不仅如此,它还提供了一整套功能,这些功能可以被UI用来提供比UI更新更多的东西,例如:
- 排序 - 搜索 - 通过工厂添加(AddNew成员函数)。 - 只读列表(CanEdit属性)
所有这些功能在ObservableCollection<T>中都不可用。
另一个区别是,当BindingList的项实现INotifyPropertyChanged时,BindingList会转发项更改通知。如果某个项引发PropertyChanged事件,BindingList将接收到它,并引发具有ListChangedType.ItemChangedOldIndex=NewIndexListChangedEvent(如果替换了一个项目,则OldIndex=-1)。ObservableCollection不会转发项通知。
请注意,在Silverlight中,BindingList不可用作选项:但是您可以使用ObservableCollectionICollectionView(如果我没记错的话,还有IPagedCollectionView)。

8
需要考虑的另一件事是性能,参见:http://www.themissingdocs.net/wordpress/?p=465。 - Jarek Mazur
2
谢谢,我之前并不了解BindingList的实际实现。我倾向于使用ObservableCollection和ICollectionView。 - Eilistraee
9
虽然这个答案中的信息是正确的,但对于任何WPF用户来说,应该注意:BindingList没有实现INotifyCollectionChanged接口,如果绑定到控件的ItemsSource属性上会导致内存泄漏。ObservableCollection实现了这个接口,不会造成这样的问题。 - Brandon Hood
1
如果BindingList实现了排序,那么为什么不能对绑定到BindingList的网格进行排序呢? - Robert Harvey
BindingList已经过时了吗? - Shimmy Weitzhandler

29

实际上,BindingList是为WinForms设计的,而ObservableCollection是为WPF设计的。

从WPF的角度来看,BindingList没有得到很好的支持,在WPF项目中除非你确实必须使用它,否则你不会真正使用它。


1
有趣。作为一名Silverlight开发者,我不知道这个。谢谢。如果你想要排序和过滤,ICollectionView实现是你的好朋友^^ - Eilistraee
33
为什么会显示“不支持”?ViewManager(内部)位于PresentationFramework程序集中,因此支持它。例如将其绑定到ItemsControl,更改通知将得到尊重(即添加和删除项目)。如果它是WinForms特定的,它不应该放在Forms命名空间中吗? - David Kiff
7
同意David的说法,它位于System.Collections命名空间中,因此应该完全受WPF支持。 WPF只是一种不同的UI布局方式。 - Justin
15
我也同意David的看法,在WPF中我经常使用BindingList,因为ObservableCollection无法将其项的属性更改通知向上传递。 - amnesia
5
举个“不支持”的例子:我刚在我的 WPF 应用程序中发现了一个内存泄露问题,是由于某些 BindingLists 没有实现 INotifyCollectionChanged 接口引起的。 - Breeze
我发誓这个问题需要在官方文档中得到解决。我通过重构一个WinForms应用程序(几乎完全是.NET中的WPF / XAML开发人员)的方式学会了这一点,而且似乎每个第三方控件在绑定到ObservableCollection时都会出现问题,但没有任何解释。 - NimbusHex

14
已经有人提到了包含元素的特性和更改通知等一些最重要的区别,但还有其他一些区别也值得一提:
性能
当调用 AddNew 方法时,BindingList 会通过 IndexOf 查找添加的项。如果 T 实现了 INotifyPropertyChanged 接口,则还会通过 IndexOf 查找修改元素的索引(只要同一项反复更改,就不会进行新的查找)。如果在集合中存储了数千个元素,则使用 ObservableCollection (或具有 O(1) 查找成本的自定义 IBindingList 实现)可能更好。
完整性
IBindingList 接口非常庞大(可能不是最干净的设计),允许实现者仅实现其功能的子集。例如,AllowNew、SupportsSorting 和 SupportsSearching 属性分别指定是否可以使用 AddNew、ApplySort 和 Find 方法。这经常使人们感到惊讶,即 BindingList 本身不支持排序。实际上,它提供了一些虚拟方法,允许派生类添加缺失的功能。DataView 类是 IBindingList 的一个完整实现示例;但是,它首先不是针对类型化集合而设计的。WinForms 中的 BindingSource 类是一个混合示例:如果它包装另一个支持排序的 IBindingList 实现,则它支持排序。
ObservableCollection 已经是 INotifyCollectionChanged 接口的完整实现(它只有一个事件)。它也具有虚拟成员,但通常通过继承来派生 ObservableCollection,原因与其基类 Collection 类相同:用于自定义添加/删除项(例如在数据模型集合中),而不是调整绑定特性。
复制 vs. 包装
ObservableCollection 和 BindingList 都有一个构造函数,接受一个已经存在的列表。但当它们被另一个集合实例化时,它们的行为是不同的:
  • BindingList<T> 作为提供的列表的可观察包装器,对 BindingList<T> 所做的更改也将反映在底层集合上。
  • 另一方面,ObservableCollection<T> 将一个新的 List<T> 实例传递给基础的 Collection<T> 构造函数,并将原始集合的元素复制到这个新列表中。当然,如果 T 是引用类型,则元素上的更改将从原始集合中可见,但集合本身不会被更新。

好的、全面的回答。只是要注意:BindingList<T> 之所以有性能损失,是因为它在做一些 ObservableCollection<T> 不支持的额外操作。如果让后者实现内部项的更改通知并给出 changedItem 的索引,它也会产生同样的性能损失。我只是认为 BindingList<T> 在这种情况下返回索引是一个错误。他们本可以只返回更改的项,让客户端自己决定是否需要项索引(客户端可以自己执行 list.IndexOf)。 - nawfal
关于包装和复制行为的差异这一点说得好。我一直认为更好的命名方式应该是 ObservableList(像任何其他新的 List<T> 一样操作)和 BindingCollection(作为 Collection<T>、ReadOnlyCollection<T> 等其他集合的包装器进行操作)。 - nawfal
感谢您的想法。实际上,自从发布这个答案以来,我已经免费提供了我的库,其中包含一个FastBindingList,它没有性能损失,还有一个SortableBindingList和一个ObservableBindingList,将两个世界统一起来。GitHub: https://github.com/koszeggy/KGySoft.CoreLibraries - György Kőszeg
我看了一下,FastBindingCollection。所以你是预先构建索引映射。因此,权衡在其他地方 :) - nawfal
1
如果你把字典初始化看作是一种权衡,那么是的。 :) 它有一个一次性的 O(n) 成本(当你将一个已经存在的集合传递给构造函数时),并且在添加/删除项时具有实际上的 O(1) 维护成本。 - György Kőszeg

3

ObservableCollectionBindingList 之间还有一个非常大的区别,这对于技术选型来说是一个重要的决策因素:

BindingList 列表变化处理程序:

BindingList List Change

ObservableCollection 集合变化:

ObervableCollection Collection Changed

上述简要概述:如果在 BindingList 中更改某个项的属性,则ListChanged事件将完整提供该属性(在PropertyDescriptor中)的详细信息,并且 ObservableCollection 不会。实际上,ObservableCollection不会为更改项中的属性引发更改事件。

上述结论涉及模型类中实现的INotifyPropertyChanged 。默认情况下,如果在项目中更改属性,则没有任何内容会引发更改事件。


我认为这个(PropertyDescriptor)可能是内存泄漏的源头。 - Abdulkarim Kanaan

0

两者都有优点和缺点,需要一些时间来发现。

我在使用BindingList时遇到了一些麻烦,因为更改通知事件仅在删除项目后才会发生,并且仅提供索引(这意味着如果您实现了某些后删除机制,则必须跟踪哪个对象位于哪个位置)。另一方面,ObservableCollection为您提供了已删除项目的列表。

BindingList具有方便的AddNew()方法,允许派生类实现工厂模式,例如根据父集合初始化新项的值(例如,如果集合包含子项,则为父项的外键)。

还要注意,使用(Entity Framework中的ToBindingList扩展),非常容易从ObservableCollection获取BindingList,并且返回的(派生)BindingList实现了排序等功能,而原始的BindingList则不具备。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接