2010-09-02 31 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