问题描述
我正在开发一个应用程序(使用 C# 和 WPF),为 sql 数据库提供用户界面。应用程序的多个实例同时访问和修改数据库,因此应用程序定期从数据库中请求自上次请求以来修改过的记录,并使用 XML 反序列化将这些记录加载到复杂类的列表中。
通过将列表绑定到 WPF 的 ListView 控件的 ItemsSource 属性,将列表呈现给用户。这最初效果很好,但是当列表更新时(特别是添加或删除项目时),我收到此错误:
“这种类型的 CollectionView 不支持从不同于 dispatcher 线程的线程更改其 SourceCollection。”
我明白了这个问题,UI线程想通过ListView的调度器的安全性来控制绑定的数据集和添加或删除数据。但是,似乎必须有一种方法可以使数据的线程安全 UI 不是单个用户独有的。
我尝试了各种不同类型的集合,包括 ObservableCollection
和 ConcurrentBag
,但它们都存在相同的问题。
我可以通过绑定到列表的副本 (List.ToList<>()
) 来规避该问题,并在列表更新时使用事件触发重新绑定到新副本,但这看起来很笨拙ListViews 和绑定的目的,远非 C# 或 WPF 的优雅。
我是否遗漏了一些明显的东西?感谢您的帮助!
解决方法
有时 BindingOperations.EnableCollectionSynchronization
可以提供帮助。
它使 CollectionView 对象能够参与对在多个线程上使用的集合的同步访问。
BindingOperations.EnableCollectionSynchronization(collection,lockObject);
要在多个线程上使用集合,其中之一是拥有 ItemsControl 的 UI 线程,您必须在访问集合时实例化一个对象以锁定。
调用 EnableCollectionSynchronization(IEnumerable,Object) 以通知 WPF 您正在使用简单的锁定机制。调用必须发生在 UI 线程上。
调用必须在不同线程上使用集合之前或在将集合附加到 ItemsControl 之前发生,以较晚者为准。
通常,我在视图模型的构造函数中调用 BindingOperations.EnableCollectionSynchronization(collection,lockObject)
。