2014-01-30 41 views
5

我們有一個根據MVVM模式構建的應用程序。在不同的時間,我們啓動任務去數據庫檢索數據,然後填充一個ObservableCollection,WPF控件綁定到該數據。C#,MVVM,任務和UI線程

我們有點困惑,當我們填充ObservableCollection時,我們在任務線程上這樣做,而不是UI線程,但UI仍然更新/行爲正確。我們期待一個錯誤,並且必須更改代碼以在UI線程上填充集合。

這是一個危險的情況,我們是否應該在UI線程上填充?

代碼來獲取數據:

Task.Factory.StartNew(() => 
    UiDataProvider.RefreshForwardContractReport(fcrIdField.Value) 
) 
.ContinueWith(task => { 
    if (task.Result.OperationSuccess) 
    { 
     // This updates the ObseravableCollection, should it be run on UI thread?? 
     RefreshReport(task.Result.OperationResult); 
    } 
}); 
+0

如果從數據庫中檢索數據比在UI線程上花費時間要少,因爲它會拖累或凍結屏幕,這種流派不是一個好用戶經驗。你應該在另一個線程上完成這項任務。 – Vishal

+0

簡單的解決方案。檢查你正在使用的線程。沒有任何需要承擔任何事情。 – Will

回答

3

延續在UI線程上運行可能有多種原因。 MVVM框架可能會有所幫助,或者其他的東西讓它在UI線程上運行,或者你只是幸運而已。

爲了確保在UI線程上繼續運行,您可以像之前那樣捕獲UI TaskScheduler

var uiScheduler = TaskScheduler.FromCurrentSyncronizationContext(); 

Task.Factory.StartNew(() => 
    UiDataProvider.RefreshForwardContractReport(fcrIdField.Value), 
    TaskCreationOptions.LongRunning 
) // Ensures the task runs in a new thread 
.ContinueWith(task => { 
    if (task.Result.OperationSuccess) 
    { 
     RefreshReport(task.Result.OperationResult); 
    } 
}, uiScheduler); // Runs the continuation on the UI thread. 

這假定外部方法是從UI開始運行的。否則,您可以捕獲最高級別的UI調度程序,並在您的應用程序中全局訪問它。

如果您可以使用異步/等待,那麼代碼變得更容易。

var result = await Task.Factory.StartNew(
    () => UiDataProvider.RefreshForwardContractReport(fcrIdField.Value), 
    TaskCreationOptions.LongRunning 
); 

if (result.OperationSuccess) 
{ 
    RefreshReport(result.OperationResult); 
} 
1

您應該使用其他(非UI)線程來檢索從DB長數據或其他來源爲防止UI凍結。但是當你嘗試從UI線程中改變UI元素時,它可能會拋出一些異常。若要從沒有UI線程改變UI您應該添加更新任務UI線程:

Deployment.Current.Dispatcher.BeginInvoke(() => 
{ 
    some udate action here 
}); 
2

考慮MVVM情況下,當你在ContinueWith部分,我同意你在非UI線程,你更新ViewModel中的屬性(綁定到UI元素),而不是UI元素本身。

嘗試更新的ContinueWith部分的UI元素,說喜歡

.ContinueWith(task => myTextblock.Text = "SomeText") 

在這種情況下,日子會把你得到你期待的例外。

P.S - 「myTextBlock」是視圖中的文本塊。

1

如果您只在ViewModel中更改日期,則應該沒有問題。 ui更新將在您的視圖模型中使用INotifyPropertyChanged引發。

我更喜歡寫異步和等待,而不是繼續。對於大多數同事來說,它更具可讀性。但這只是代碼的問題,所以你的實現將沒有什麼不同。

0

我相信你應該像對待用戶界面一樣對待你的ViewModel。也就是說,不要直接從非UI線程修改它。 INotifyPropertyChanged不會自動將工作線程封送到可以更新控件的UI線程。

相關問題:

INotifyPropertyChanged with threads

INotifyPropertyChanged causes cross-thread error

+2

「INotifyPropertyChanged不會自動執行從工作線程到UI線程的編組魔法」實際上,正在監視INPC屬性(即,不是DependencyProperty)***的綁定將***將所有更新集中到UI上線。我相信這是在4.0中增加的。親自嘗試一下。 – Will

0

一個事件類添加爲一個屬性,並使用後臺線程(調度員)做您的治療。一旦它結束,你調用該事件upDate窗口(UI線程)。

您也可以使用background worker它們是用於在完成後在小線程中執行代碼和更新UI的小線程。

4

WPF不斷允許每個版本的更多的跨線程操作。其他MVVM平臺根本不允許它們。在ObservableCollection的情況下,除非您使用的是EnableCollectionSynchronization,否則從後臺線程進行更新不正確。

我@avo同意,你應該把你的視圖模型(你邏輯 UI),就好像它有UI線程親和力(如字面 UI)。所有的數據綁定更新應該在UI上下文中完成。

由於@Cameron指出,這是最容易通過async完成:

var result = await Task.Run(
    () => UiDataProvider.RefreshForwardContractReport(fcrIdField.Value)); 
if (result.OperationSuccess) 
{ 
    RefreshReport(result.OperationResult); 
}