I am using the BackgroundWorker
to update an ObservableCollection
but it gives this error:
我使用BackgroundWorker更新ObservableCollection但它给出了这个错误:
"This type of
CollectionView
does not support changes to itsSourceCollection
from a thread different from the Dispatcher thread."“这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection。”
What's the best and most elegant way to solve this, with the least amount of work. I don't want to write low level lock-based multi-threading code.
用最少的工作量来解决这个问题的最佳和最优雅的方法是什么。我不想写低级别的基于锁的多线程代码。
I have seen some solutions online but they are several years old, so not sure what the latest consensus is for the solution of this problem.
我在网上看到了一些解决方案,但它们已有几年的历史了,所以不确定解决这个问题的最新共识是什么。
5 个解决方案
#1
12
If MVVM
如果是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
36
If you initialize the collection in the constructor it will be on the default Application thread.
如果在构造函数中初始化集合,它将位于默认的Application线程上。
To invoke the main thread you can do this:
要调用主线程,您可以执行以下操作:
Application.Current.Dispatcher.Invoke((Action)(() =>
{
//Do something here.
}));
You have to cast the Anonymous delegate as an action otherwise it gets confused ¯\O_o/¯
你必须将匿名委托作为一个动作,否则会混淆¯\ _O_o /¯
If you are using the Async CTP then you can do this
如果您使用的是Async CTP,则可以执行此操作
Application.Current.Dispatcher.InvokeAsync(()=>
{
//Do something here.
});
#3
4
You are using BGW, it was designed to solve your problem. But you'll have to use it properly, update the collection in a ProgressChanged or RunWorkerCompleted event handler. If that's what you are doing then you created the BGW instance on the wrong thread. It has to be done on the UI thread.
您正在使用BGW,它旨在解决您的问题。但是您必须正确使用它,在ProgressChanged或RunWorkerCompleted事件处理程序中更新集合。如果这就是你正在做的事情,那么你就在错误的线程上创建了BGW实例。它必须在UI线程上完成。
#4
3
Try This:
尝试这个:
this.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
() =>
{
//Code
}));
#5
1
I had the same problem while reloading an observableCollection from an event (on DataReceived) raised by the serial port class. I used MVVM; I tried to update the collection with the BackgroundWorker, but it raised the "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.". I tried many others solutions found online, but the only one that solved my problem (immediately!) was to use a multi-threading observable collection (I used the one in this post: Where do I get a thread-safe CollectionView?)
从串行端口类引发的事件(在DataReceived上)重新加载observableCollection时,我遇到了同样的问题。我用过MVVM;我尝试使用BackgroundWorker更新集合,但它提出“这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection。”。我在网上尝试了很多其他的解决方案,但唯一一个解决了我的问题(立即!)是使用多线程可观察集合(我在这篇文章中使用了一个:我在哪里获得一个线程安全的CollectionView?)
#1
12
If MVVM
如果是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
36
If you initialize the collection in the constructor it will be on the default Application thread.
如果在构造函数中初始化集合,它将位于默认的Application线程上。
To invoke the main thread you can do this:
要调用主线程,您可以执行以下操作:
Application.Current.Dispatcher.Invoke((Action)(() =>
{
//Do something here.
}));
You have to cast the Anonymous delegate as an action otherwise it gets confused ¯\O_o/¯
你必须将匿名委托作为一个动作,否则会混淆¯\ _O_o /¯
If you are using the Async CTP then you can do this
如果您使用的是Async CTP,则可以执行此操作
Application.Current.Dispatcher.InvokeAsync(()=>
{
//Do something here.
});
#3
4
You are using BGW, it was designed to solve your problem. But you'll have to use it properly, update the collection in a ProgressChanged or RunWorkerCompleted event handler. If that's what you are doing then you created the BGW instance on the wrong thread. It has to be done on the UI thread.
您正在使用BGW,它旨在解决您的问题。但是您必须正确使用它,在ProgressChanged或RunWorkerCompleted事件处理程序中更新集合。如果这就是你正在做的事情,那么你就在错误的线程上创建了BGW实例。它必须在UI线程上完成。
#4
3
Try This:
尝试这个:
this.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
() =>
{
//Code
}));
#5
1
I had the same problem while reloading an observableCollection from an event (on DataReceived) raised by the serial port class. I used MVVM; I tried to update the collection with the BackgroundWorker, but it raised the "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.". I tried many others solutions found online, but the only one that solved my problem (immediately!) was to use a multi-threading observable collection (I used the one in this post: Where do I get a thread-safe CollectionView?)
从串行端口类引发的事件(在DataReceived上)重新加载observableCollection时,我遇到了同样的问题。我用过MVVM;我尝试使用BackgroundWorker更新集合,但它提出“这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection。”。我在网上尝试了很多其他的解决方案,但唯一一个解决了我的问题(立即!)是使用多线程可观察集合(我在这篇文章中使用了一个:我在哪里获得一个线程安全的CollectionView?)