Я использую BackgroundWorker
для обновления ObservableCollection
, но он дает эту ошибку:
"Этот тип
CollectionView
делает не поддерживают изменения в своихSourceCollection
из потока отличной от потока Диспетчер."
Какой лучший и самый элегантный способ решить эту проблему, с наименьшим количеством работы. Я не хочу писать низкоуровневый многопоточный код на основе блокировки.
Я видел некоторые решения в Интернете, но им уже несколько лет, поэтому не уверен, какой последний консенсус касается решения этой проблемы.
Если 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();
}
}
Если вы инициализируете коллекцию в конструкторе, она будет включена в поток приложений по умолчанию.
Чтобы вызвать основной поток, вы можете сделать это:
Application.Current.Dispatcher.Invoke((Action)(() =>
{
//Do something here.
}));
Вы должны отправить анонимного делегата в качестве действия, иначе он запутается ¯\O_o/¯
Если вы используете Async CTP, вы можете сделать это
Application.Current.Dispatcher.InvokeAsync(()=>
{
//Do something here.
});
Вы используете BGW, он был разработан для решения вашей проблемы. Но вы должны будете использовать его правильно, обновите коллекцию в обработчике событий ProgressChanged или RunWorkerCompleted. Если это то, что вы делаете, вы создали экземпляр BGW в неправильном потоке. Это должно быть сделано в потоке пользовательского интерфейса.
Попробуйте следующее:
this.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
() =>
{
//Code
}));
У меня была такая же проблема при перезагрузке наблюдаемого набора из события (в DataReceived), поднятого классом последовательного порта. Я использовал MVVM; Я попытался обновить коллекцию BackgroundWorker, но он поднял "Этот тип CollectionView не поддерживает изменения в SourceCollection из потока, отличного от потока Dispatcher". Я попробовал много других решений, найденных в Интернете, но единственный, кто решил мою проблему (сразу!), Состоял в использовании многопоточной наблюдаемой коллекции (я использовал ту, что была в этом сообщении: Где Я получаю потокобезопасный CollectionView?)