2012-10-04 164 views
3

我有一個BackgroundWorker的調用函數在BackgroundWorker的_DoWork做一個長期的過程,當發生功能錯誤,我會提示一個定製的消息框:BackgroundWorker的線程必須STA

WPFMessageBoxResult result = WPFMessageBox.Show("Activation Fail", "Error!!", WPFMessageBoxButtons.OK, WPFMessageBoxImage.Error); 

例外下面發生在WPFMessageBoxResult類別:

The calling thread must be STA, because many UI components require this. 

謝謝。

回答

4

您不應該嘗試與後臺線程中的任何UI組件進行交互。

一種方法可能是在doWork方法中捕獲異常並將其分配給backgroundworker的result屬性,然後檢查結果是否爲異常類型,如果不使用結果則返回null。然後在backgroundWorker_completed事件中檢查它。

BackgroundWorker_DoWork(sender,) 
{ 
    try 
    { 
     // do work   
    } 
    catch (Exception ex) 
    { 
     BackgroundWorker w = sender as BackgroundWorker; 
     if (w != null) 
      w.Result = ex; 
    } 
} 

然後

BackgroundWorker_Completed() 
{ 
    if (s.Result != null && e.Result is Exception) 
    { 
     Exception ex = e.Result as Exception; 
     // do something with ex 
    } 
} 
3

您的後臺線程只是一個工作線程而不是用戶界面線程。 WPF和WinForms都要求將執行用戶界面操作的線程標記爲STA(單線程單元),因爲用戶界面代碼不是線程安全的。他們還要求您添加一個消息泵,以便分派Windows消息。

我建議您不要在工作線程中顯示消息框,而是向主用戶界面線程發送消息,並讓該線程顯示消息框。爲此,您應該將主UI線程中對Dispatcher的引用傳遞給工作線程。然後使用Dispatcher.BeginInvoke請求在主線程上回調委託。

或者,您可以等待後臺線程完成,然後檢查結果並向用戶顯示相應的答案。無論哪種方式,工作線程都不應直接與用戶界面交互。

4

平時用的WinForms/WPF使用的invoke()跳上UI線程,如果你需要用從長時間運行的任務的UI交互。您可以從屬於UI的任何對象調用invoke,但要確保在調用範圍內只能儘可能少地使用代碼。由於此代碼位於UI線程上,因此如果UI太長,它將阻止/掛起UI。

public void BackgroundWorkerMethod() 
{ 
    try 
    { 
     // do work 
    } 
    catch (Exception e) 
    { 
     uiObject.Invoke(new Action(() => { 
      // now you are on the UI thread 
      Message.ShowMessage(e.Message); 
     }); 
    } 
} 
3

您必須使用此方法

void BGW_DoWork(object sender, DoWorkEventArgs e) 
    { 
     try 
     { 
      Dispatcher.BeginInvoke(new Action(() => 
      { 
       Button btn = new Button(); 
       btn.Width = 100; 
       btn.Height = 50; 
       btn.Content = "Test"; 
       myG.Children.Add(btn); 
      } 
      )); 
     } 
      catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message); 
     } 

    }