2010-10-07 123 views
0

我見過類似於我的其他問題,但我還沒有看到任何可以應用於我的代碼的工作。WPF MVVM多線程問題

所以我是新來的MVVM,我試圖得到一些在後臺線程執行的東西來更新我的用戶界面。我注意到,第一次調出用戶界面,後臺線程第一次執行,如果集合是IEnumerable <> UI不是完全根據備份數據進行了更新。如果集合是ObservableCollection <>,則會引發錯誤。

從我讀到的,集合的更改需要在調度程序線程上執行,但OnPropertyChanged()調用不會。所以,一個人,請告訴我如何這可能發生:

我改變我的_Printers觀察到的集合:

foreach (PrinterViewModel pv in _Printers) 
      { 
       DispatcherExec(() => 
       { 
       var abilities = from x in _ServerData.Types 
           select new PrinterAbility(
            new PrintableType() { ID = x.ID, Name = x.Name, NumInProcUnit = x.NumInProcUnit, PrintersMappedTo = x.PrintersMappedTo, SysName = x.SysName }, 
            x.PrintersMappedTo.Contains(pv.Printer.ID) 
            ); 


        pv.Printer.SetAbilities(abilities); 
       }); 

我DispatcherExec看起來像這樣:

private void DispatcherExec(Action action) 
    { 
     //Dispatcher.Invoke((Action)delegate 
     //{ 
     // action.BeginInvoke(null, null); 
     //}, null); 
     Dispatcher.CurrentDispatcher.Invoke((Action)delegate 
     { 
      action.Invoke(); 
     }, null); 
    } 

而這裏的SetAbilities碼即失敗:

public void SetAbilities(IEnumerable<PrinterAbility> abilities) 
    { 
     if (log.IsInfoEnabled) 
      log.Info("SetAbilities(IEnumerable<PrinterAbility> abilities): called on printer "+Name); 

     List<PrinterAbility> l = new List<PrinterAbility>(); 
     abilities.ForEach(i => 
      { 
       i.PrinterAbilityChanged += new PrinterAbilityChangedEventHandler(OnPrinterAbilityChanged); 
       l.Add(i); 
      } 
      ); 
     lock (_Abilities) 
     { 
      foreach (PrinterAbility pa in l) 
       _Abilities.Add(pa); 
     } 
     if (log.IsDebugEnabled) 
      log.Debug("SetAbilities(IEnumerable<PrinterAbility> abilities): leaving"); 
    } 

關於_Abilities.Add(pa)observable collecti上添加它說:「這種類型的CollectionView不支持從與分派器線程不同的線程更改其SourceCollection。」我在想,「你在開玩笑嗎?」

此外,我認爲對可觀察集合中的對象的更改會自動使其調用OnCollectionChanged(),是嗎?

在此先感謝大家。

回答

2

使用Dispatcher.CurrentDispatcher不是您應該從BG線程執行的操作。您需要將Dispatcher用於在UI線程上創建的DependencyObject派生的對象。

此外,您正在從BG線程內迭代* ViewModel對象(PrinterViewModel)。這實際上違背了MVVM。你的模型應該做異步的東西,你的ViewModel應該以視圖可以消耗的方式處理這些異步操作(通過調度器通過調度到適當的線程)。

另外,您正在關閉循環變量(pv)。壞,壞。這(取決於執行順序)可能意味着在調度程序出現時,您將在同一個PrinterViewModel實例上獲得多個pv.Printer.SetAbilities(...)調用。在循環內部創建一個局部變量並在匿名方法中使用它來避免這個問題。

+0

謝謝你的批評。 – Micah 2010-10-08 12:31:43

+0

Application.Current.Dispatcher怎麼樣? – Micah 2010-10-08 14:43:05

+0

那麼,我把它放在xaml.cs中,而不是將我的數據源放入xaml中,並將其中一個控件的調度程序傳遞給我的虛擬機。這工作。再次感謝。 – Micah 2010-10-08 17:14:08

0

可能是thisthis對於通過線程更改Observable Collection將會很有幫助。

0

您應該使用與任何WPF控件關聯的Dispatcher,而不是將Dispatcher.CurrentDispatcher用於後臺線程。

而且

Dispatcher.CurrentDispatcher.Invoke((Action)delegate 
     { 
      action.Invoke(); 
     }, null); 

是多餘的,它應該是

wpfDispatcher.Invoke(action, null); 

終於爲第一個塊,通常應避免將循環變量的lambda表達式,使用臨時分配的技巧繞開這些偷偷摸摸的關閉問題。幾乎可以肯定的是,雖然這不是問題。

+0

如果我使用連接到其中一個控件的調度程序,那麼我是不是將VM緊密地耦合到V? – Micah 2010-10-07 21:46:41

+0

這是唯一的方法。您應該使用與WPF線程關聯的調度程序。您可以在視圖中使用它來合併邏輯,或者將它作爲您正在調用的VM類/方法的參數傳遞。 – Grozz 2010-10-07 21:53:04