一尘不染

如何通过辅助线程更新ObservableCollection?

c#

我有一个ObservableCollection<A> a_collection;集合包含“ n”个项目。每个项目A如下所示:

public class A : INotifyPropertyChanged
{

    public ObservableCollection<B> b_subcollection;
    Thread m_worker;
}

基本上,所有这些都连接到WPF列表视图+一个详细信息视图控件,该控件b_subcollection在单独的列表视图中显示选定项目的内容(2向绑定,对propertychanged进行更新等)。

当我开始实施线程时,问题就出现了。整个想法是让a_collection工作线程全部使用它来“完成工作”,然后更新各自的工作,b_subcollections并让gui实时显示结果。

当我尝试它时,我得到一个例外,说只有Dispatcher线程可以修改ObservableCollection,并且工作停止了。

谁能解释这个问题,以及如何解决这个问题?


阅读 396

收藏
2020-05-19

共1个答案

一尘不染

从技术上讲,问题不是您正在从后台线程更新ObservableCollection。问题在于,这样做时,集合将在引起更改的同一线程上引发其CollectionChanged事件-
这意味着控件正在从后台线程更新。

为了在控件绑定到背景线程时从后台线程中填充一个集合,您可能必须从头开始创建自己的集合类型以解决此问题。不过,有一个更简单的选项可能会为您解决。

将Add调用发布到UI线程上。

public static void AddOnUI<T>(this ICollection<T> collection, T item) {
    Action<T> addMethod = collection.Add;
    Application.Current.Dispatcher.BeginInvoke( addMethod, item );
}

...

b_subcollection.AddOnUI(new B());

此方法将立即返回(在将项目实际添加到集合之前),然后在UI线程上,将项目添加到集合,每个人都应该高兴。

但是,现实情况是,由于所有跨线程活动,该解决方案很可能在高负载下陷入困境。一种更有效的解决方案是批量处理一堆项目,并将它们定期发布到UI线程中,这样您就不会在每个项目的线程间进行调用。

BackgroundWorker的类实现的模式,使您可以通过汇报其进展ReportProgress后台操作过程中的方法。通过ProgressChanged事件在UI线程上报告进度。这可能是您的另一选择。

2020-05-19