2010-09-02 113 views
9

好吧,我最近实现了一个后台工作来执行保存和加载数据。用MVVM中的后台工作人员更新ObservableCollection

然而,得到这个在保存命令的工作已经证明很难。

基本上,我的save命令生成一个事件,通知集合视图模型,项目已被添加和该项目应被添加到了自己的ObservableCollection。

在这一点上,我得到的通常的例外是说我不能更新在不同的线程的ICollection的。我试图创建一个新的列表类型,调用Dispatcher.Invoke,但是这仍然会产生相同的异常。

我不知道其他人是否对如何最好地解决这个有什么建议?

所以目前我有一个类,从的ObservableCollection继承:

public class ThreadSafeObservableCollection<T> : ObservableCollection<T> 
{ 
    public ThreadSafeObservableCollection(List<T> 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<cdAdministrators> 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); 
    } 

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

回答

7

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

无可否认,这意味着视图模型需要知道调度程序,然后变得尴尬测试......幸运的是,介绍自己的接口并且以正常方式使用依赖注入并不难。

+0

乔恩您好,感谢您的回复,我已经有一个集合对象,从ObserveableCollection继承上InsertItem这确实Dispatcher.CheckAccess如果为假,则不会Dispather.BeginInvoke,但是这仍然没有工作? – jpgooner 2010-09-02 15:25:18

+0

@jpgooner:你确定代码实际上被使用了吗?你能想出一个简短但完整的例子来证明问题吗? – 2010-09-02 16:09:09

+0

我已经加入上面更详细,让我知道如果你需要了,我还是新来的StackOverflow – jpgooner 2010-09-02 16:32:17

3

这个怎么样?

public class ThreadSafeObservableCollection<T> : ObservableCollection<T> 
{ 
    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); 
    } 
} 
2

我找到了一个blog post,它使用Dispatcher来管理所有ObeservableCollection的方法。这里是代码片段,请参阅整个班级的post

public class DispatchingObservableCollection<T> : ObservableCollection<T> 
{ 
    /// <summary> 
    /// The default constructor of the ObservableCollection 
    /// </summary> 
    public DispatchingObservableCollection() 
    { 
     //Assign the current Dispatcher (owner of the collection) 
     _currentDispatcher = Dispatcher.CurrentDispatcher; 
    } 

    private readonly Dispatcher _currentDispatcher; 

    /// <summary> 
    /// Executes this action in the right thread 
    /// </summary> 
    ///<param name="action">The action which should be executed</param> 
    private void DoDispatchedAction(Action action) 
    { 
     if (_currentDispatcher.CheckAccess()) 
      action(); 
     else 
      _currentDispatcher.Invoke(DispatcherPriority.DataBind, action); 
    } 

    /// <summary> 
    /// Clears all items 
    /// </summary> 
    protected override void ClearItems() 
    { 
     DoDispatchedAction(() => base.ClearItems()); 
    } 

    /// <summary> 
    /// Inserts a item at the specified index 
    /// </summary> 
    ///<param name="index">The index where the item should be inserted</param> 
    ///<param name="item">The item which should be inserted</param> 
    protected override void InsertItem(int index, T item) 
    { 
     DoDispatchedAction(() => base.InsertItem(index, item)); 
    } 
+0

很好的例子!只是一个小的更正:保护覆盖无效InsertItem(INT索引,T项目){DoDispatchedAction(()=> BaseInsertItem(索引,项目)); }实际上是保护覆盖无效InsertItem(INT索引,T项目){DoDispatchedAction(()=> base.InsertItem(索引,项目)); } – 2011-10-03 20:09:19

+0

谢谢。我不确定为什么我明确创建了一个方法,并且在第二个示例中使用了匿名方法。我把它们清理干净了。 – chilltemp 2011-10-17 15:15:04