2010-01-18 41 views
1

我想使用BackgroundWorker從GUI執行數據庫事務。如何等待BackgroundWorker的完成?

如何命令BackgroundWorker執行工作,然後等待工作人員完成工作,同時保持GUI的響應?

爲此我需要使用DoEvents還是有其他方法?

回答

10

問:「我如何命令BackgroundWorker執行工作,然後等待工作人員完成,同時保持GUI的響應?」真的在問「我如何使用BackgroundWorker?」。這就是BackgroundWorker確實是

當你寫一個後臺任務,你基本上分手的方法分爲四個部分:

  1. 準備工作運行任務。
  2. 任務本身。
  3. 任務運行時向UI報告進度。
  4. 當任務完成時清理東西。

所以你需要寫四種方法。第一種方法是創建BackgroundWorker,將事件處理程序添加到其DoWork,ProgressChangedRunWorkerCompleted事件中,將UI置於任務運行時需要處於的狀態,並調用RunWorkerAsync來啓動任務。

其他三個是那三個事件處理程序。 DoWork是一個DoWorkEventHandler,它可以完成這項工作,並在需要向UI報告其進度時調用ReportProgressProgressChangedProgressChangedEventHandler,當ReportProgress被調用時,它實際上更新了UI。 RunWorkerCompleted是一個RunWorkerCompletedEventHandler,告訴用戶界面該工作已完成。

就是這樣。

嗯,不完全是全部。首先,您必須確保您檢查並處理完成處理程序中的Error屬性。如果你不這樣做,你將無法知道你的do-work方法拋出一個異常,因爲它不會在UI線程中發生,因此不會拋出你可以看到的異常。 (這將在輸出窗口中顯示出來,如果你正在尋找它。)

其次,你必須確保該DoWorkEventHandler不會在UI觸摸任何東西。如果你使用MVVM模式,並且你沒有計劃這個意外事件,這可能會很棘手,因爲通過數據綁定的魔力,你的模型中可能有更新UI綁定的視圖的東西,這意味着在您的做工方法中操縱模型是操縱用戶界面。假設你正在實現INotifyPropertyChanged,避免麻煩的好方法是建立一個機制,在後臺任務運行時,你的視圖不會引發PropertyChanged事件。

您還需要弄清楚任務運行時哪些UI應該被禁用。這取決於你的用戶界面。答案可能是「全部」,而且可能不是。使用模態表單進行進度指示器吸引人的一件事是,它可以讓您免於解決這個問題,因爲在模態表單處於打開狀態時,您的整個UI都被禁用。如果您在啓動方法中禁用了控件或關閉了屬性更改通知,則需要在完成方法中重新啓用它。

請記住:您的用戶界面的活動部分對您的數據模型所做的任何更新都是跨線程數據訪問錯誤的潛在來源。如果你的用戶界面和後臺線程更新了同一個對象,就會發生不好的事情 - 如果你沒有在你的完成處理器中處理Error屬性,它們特別糟糕,並且跨線程異常在你不知情的情況下殺死後臺任務。如果這聽起來像是我在說這些,那是因爲我一生中花了很多時間去做這件事情是錯誤的。

2

我通常等待RunWorkerCompleted與單獨的模態形式進度條。這迫使最終用戶等待完成您的操作,但GUI以可以移動表單的方式響應。

對於最終用戶來說,更好的解決方案是在狀態欄中呈現一些進度,並允許他/她只能執行某些不能破壞程序邏輯的操作。

這是你想要的嗎?

+0

感謝您的回覆。我想要啓動後臺進程並開始進程「旋轉循環」並等待工作人員完成,然後停止旋轉循環,而不打開新的表單 - 旋轉圓圈位於主窗體的狀態欄上。 – joek1975 2010-01-18 18:41:16

+1

您可以使用具有選取框樣式的進度條,或者設置一些獨立的Windows.Forms.Timer來爲旋轉的圓圈設置動畫,或者您可以使用ProgressChanged事件中的百分比報告。 更多信息,請訪問http://msdn.microsoft.com/en-us/library/cc221403(VS.95).aspx – 2010-01-18 18:50:48

1

您使用:

Application.DoEvents(); 

,但你應該閱讀this link一種更好的方法來從一個BackgroundWorker線程

基本上更新UI,代碼是這樣的:

// The declaration of the textbox. 
private TextBox m_TextBox; 

// Updates the textbox text. 
private void UpdateText(string text) 
{ 
    // Set the textbox text. 
    m_TextBox.Text = text; 
} 


public delegate void UpdateTextCallback(string text); 

然後,在您的線程中,您可以調用m_TextBox上的Invoke方法,傳遞委託來調用以及參數。

m_TextBox.Invoke(new UpdateTextCallback(this.UpdateText), 
       new object[]{」Text generated on non-UI thread.」}); 

this link也,以獲取更多信息

1

可以調用的RunWorkerAsync像這樣:

this.backgroundWorker1.RunWorkerAsync(); 

然後你需要使用RunWorkerCompleted事件。

下面是從MSDN的例子:

private void backgroundWorker1_RunWorkerCompleted(
    object sender, RunWorkerCompletedEventArgs e) 
{ 
    // First, handle the case where an exception was thrown. 
    if (e.Error != null) 
    { 
     MessageBox.Show(e.Error.Message); 
    } 
    else if (e.Cancelled) 
    { 
     // Next, handle the case where the user canceled 
     // the operation. 
     // Note that due to a race condition in 
     // the DoWork event handler, the Cancelled 
     // flag may not have been set, even though 
     // CancelAsync was called. 
     resultLabel.Text = "Canceled"; 
    } 
    else 
    { 
     // Finally, handle the case where the operation 
     // succeeded. 
     resultLabel.Text = e.Result.ToString(); 
    } 

    // Enable the UpDown control. 
    this.numericUpDown1.Enabled = true; 

    // Enable the Start button. 
    startAsyncButton.Enabled = true; 

    // Disable the Cancel button. 
    cancelAsyncButton.Enabled = false; 
} 

就是這樣。我不認爲有任何需要調用DoEvents。在BackgroundWorker運行時,用戶界面仍應該有響應。