从另一个线程更新ObservableCollection的最佳方法是什么?

时间:2011-03-29 23:56:35

标签: c# .net multithreading parallel-processing observablecollection

我正在使用BackgroundWorker更新ObservableCollection,但它会出现此错误:

  

“这种CollectionView会这样做   不支持改变它   来自一个帖子的SourceCollection   与Dispatcher线程不同。“

用最少的工作量来解决这个问题的最佳和最优雅的方法是什么。我不想写低级别的基于锁的多线程代码。

我在网上看到了一些解决方案,但它们已有几年的历史了,所以不确定解决这个问题的最新共识是什么。

5 个答案:

答案 0 :(得分:36)

如果在构造函数中初始化集合,它将位于默认的Application线程上。

要调用主线程,您可以执行以下操作:

Application.Current.Dispatcher.Invoke((Action)(() =>
    {
       //Do something here.
    }));

你必须将Anonymous委托强制转换为动作,否则会混淆¯\ O_o /¯

如果您使用的是Async CTP,那么您可以这样做

Application.Current.Dispatcher.InvokeAsync(()=>
    {
       //Do something here.
    });

答案 1 :(得分:12)

如果MVVM

public class MainWindowViewModel : ViewModel {

    private ICommand loadcommand;
    public ICommand LoadCommand { get { return loadcommand ?? (loadcommand = new RelayCommand(param => Load())); } }

    private ObservableCollection<ViewModel> items;
    public ObservableCollection<ViewModel> Items {
        get {
            if (items == null) {
                items = new ObservableCollection<ViewModel>();
            }
            return items;
        }
    }

    public void Load() {
        BackgroundWorker bgworker = new BackgroundWorker();
        bgworker.WorkerReportsProgress = true;
        bgworker.DoWork += (s, e) => {
            for(int i=0; i<10; i++) {
                System.Threading.Thread.Sleep(1000);
                bgworker.ReportProgress(i, new List<ViewModel>());
            }
            e.Result = null;
        };
        bgworker.ProgressChanged += (s, e) => {
            List<ViewModel> partialresult = (List<ViewModel>)e.UserState;
            partialresult.ForEach(i => {
                Items.Add(i);
            });
        };
        bgworker.RunWorkerCompleted += (s, e) => {
            //do anything here
        };
        bgworker.RunWorkerAsync();
    }
}

答案 2 :(得分:4)

您正在使用BGW,它旨在解决您的问题。但是您必须正确使用它,在ProgressChanged或RunWorkerCompleted事件处理程序中更新集合。如果这就是你正在做的事情,那么你就在错误的线程上创建了BGW实例。它必须在UI线程上完成。

答案 3 :(得分:3)

试试这个:

this.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
() =>
{

 //Code

}));

答案 4 :(得分:1)

从串行端口类引发的事件(在DataReceived上)重新加载observableCollection时遇到了同样的问题。 我用过MVVM;我尝试使用BackgroundWorker更新集合,但它提出“这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection。”。我在网上尝试了很多其他的解决方案,但唯一一个解决了我的问题(立即!)是使用多线程可观察集合(我使用了这篇文章中的那个:Where do I get a thread-safe CollectionView?