使用MVVM中的后台worker更新ObservableCollection

好的,我最近实现了一个后台工作程序来执行数据的保存和加载。

但是,让它在save命令上工作已经certificate是困难的。

基本上,我的save命令生成一个事件,该事件通知集合视图模型,已添加Item,并且该项应添加到其自己的ObservableCollection中。

此时,我得到通常的exception,说我不能在不同的线程上更新ICollection。 我已经尝试创建一个调用Dispatcher.Invoke的新列表类型,但是这仍然会生成相同的exception。

我想知道是否有其他人对如何最好地解决这个问题有任何建议?

所以目前我有一个inheritance自ObservableCollection的类:

 public class ThreadSafeObservableCollection : ObservableCollection { public ThreadSafeObservableCollection(List collection) : base(collection) { dispatcher = Dispatcher.CurrentDispatcher; rwLock = new ReaderWriterLock(); } protected override void InsertItem(int index, T item) { if (dispatcher.CheckAccess()) { if (index > this.Count) return; LockCookie c = rwLock.UpgradeToWriterLock(-1); base.InsertItem(index, item); rwLock.DowngradeFromWriterLock(ref c); } else { object[] obj = new object[] { index, item }; dispatcher.Invoke( DispatcherPriority.Send, (SendOrPostCallback)delegate { InsertItemImpl(obj); }, obj); } } 

然后我有一个视图模型类,它有一个后台工作程序来执行保存。

保存完成后,会向另一个视图模型触发事件以更新其列表。

  protected override void OnObjectAddedToRepository(object sender, ObjectEventArgs e) { Dispatcher x = Dispatcher.CurrentDispatcher; var viewModel = new AdministratorViewModel(e.EventObject, DataAccess); viewModel.RecentlyAdded = true; viewModel.ItemSelected += this.OnItemSelected; this.AllViewModels.Add(viewModel); RecentlyAddedViewModel = viewModel; OnPropertyChanged(null); } 

两个列表都由单独的后台工作线程创建。

如果您有代码将项添加到可观察集合(可能在视图模型中),请在Dispatcher.BeginInvoke调用中包装Add调用。

不可否认,这意味着视图模型需要了解调度程序,然后测试变得笨拙…幸运的是,引入自己的IDispatcher接口并以正常方式使用依赖项注入并不困难。

这个怎么样?

 public class ThreadSafeObservableCollection : ObservableCollection { private SynchronizationContext SynchronizationContext; public ThreadSafeObservableCollection() { SynchronizationContext = SynchronizationContext.Current; // current synchronization context will be null if we're not in UI Thread if (SynchronizationContext == null) throw new InvalidOperationException("This collection must be instantiated from UI Thread, if not, you have to pass SynchronizationContext to con structor."); } public ThreadSafeObservableCollection(SynchronizationContext synchronizationContext) { if (synchronizationContext == null) throw new ArgumentNullException("synchronizationContext"); this.SynchronizationContext = synchronizationContext; } protected override void ClearItems() { this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.ClearItems()), null); } protected override void InsertItem(int index, T item) { this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.InsertItem(index, item)), null); } protected override void RemoveItem(int index) { this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.RemoveItem(index)), null); } protected override void SetItem(int index, T item) { this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.SetItem(index, item)), null); } protected override void MoveItem(int oldIndex, int newIndex) { this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.MoveItem(oldIndex, newIndex)), null); } } 

我发现了一篇博客文章 ,它使用Dispatcher来管理所有ObeservableCollection的方法。 这是代码的片段,请参阅整个课程的post 。

 public class DispatchingObservableCollection : ObservableCollection { ///  /// The default constructor of the ObservableCollection ///  public DispatchingObservableCollection() { //Assign the current Dispatcher (owner of the collection) _currentDispatcher = Dispatcher.CurrentDispatcher; } private readonly Dispatcher _currentDispatcher; ///  /// Executes this action in the right thread ///  ///The action which should be executed private void DoDispatchedAction(Action action) { if (_currentDispatcher.CheckAccess()) action(); else _currentDispatcher.Invoke(DispatcherPriority.DataBind, action); } ///  /// Clears all items ///  protected override void ClearItems() { DoDispatchedAction(() => base.ClearItems()); } ///  /// Inserts a item at the specified index ///  ///The index where the item should be inserted ///The item which should be inserted protected override void InsertItem(int index, T item) { DoDispatchedAction(() => base.InsertItem(index, item)); }