2012-01-05 113 views
9

我有一個winform應用程序(一種形式),在這個窗體上有一個RichTextBox。在這個表單的構造函數中,我創建了類MyClass的一個實例。在「Form_Load」中,我從MyClass實例調用方法InitialisationBackgroundWorker - 跨線程操作無效

在表單構造

myClass = new MyClass(RichTextBox richTextBox); 

在Form_Load

myClass.Initialisation(); 

Initialisation方法,在一個循環中,我讀了一些parmeters做其他的東西。爲了不凍結應用程序(因爲某些操作可能需要一段時間,幾秒鐘),我使用BackgroundWorker。我像這樣使用它(見下面的代碼)。

當我執行,我得到這個錯誤:跨線程操作無效:控制「的RichTextBox」 從比它是在創建的線程以外的線程訪問。

你能告訴我怎麼解決這個問題嗎?工作完美的,當我不訪問richTextBox

public Class MyClass 
{ 
    static BackgroundWorker _bw; 
    public MyClass() 
    { 
     _bw = new BackgroundWorker 
     { 
      WorkerReportsProgress = true, 
      WorkerSupportsCancellation = true 
     }; 
     _bw.DoWork += bw_DoWork; 
     _bw.ProgressChanged += bw_ProgressChanged; 
     _bw.RunWorkerCompleted += bw_RunWorkerCompleted; 
    } 
    static void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     foreach (....) 
     { 
      if (....) 
      { 
       richtextBox.Text.AppendText("MyText"); 
      } 
     } 
     e.Result = true; 
    } 
    static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){} 
    static void bw_ProgressChanged(object sender, ProgressChangedEventArgs e){} 
} 

回答

29

使用BackgroundWorker並不免除您的正常線程規則 - 如只有 UI線程可以訪問UI組件。

如果你想使用進度/完成事件(其提出的UI線程),你需要使用Control.Invoke/Control.BeginInvoke就像你在其他情況下,更新從BackgroundWorker其他的UI比。例如:

if (....) 
{ 
    Action action =() => richtextBox.Text.Add("MyText"); 
    richtextBox.Invoke(action); // Or use BeginInvoke 
} 
11

試試這個代碼,

BeginInvoke((MethodInvoker)delegate 
{ 
    richtextBox.Text.Add("MyText"); 
}); 
+0

This Works,thanks – mili 2014-08-27 00:47:59

3

使用BackgroundWorker組件,只有ProgressChangedRunWorkerCompleted事件允許您調用UI控件上的方法/屬性(應始終在UI線程上完成)。由於您正在更新在非UI線程上運行的DoWork事件中的UI,因此您將收到此錯誤,如果需要,您應該可以使用DoWork事件中的Invoke或BeginInvoke方法更新UI控件。

0

地址:

e.Result = "MyText"; 

在你bw_DoWork 人和:

richTextBox1.AppendText((string)e.Result); 

在你bw_RunWorkerCompleted

(改變,以適合您的代碼)

編輯:

如果它的BackgroundWorker的工作期間做過很多次,你可以添加:

_bw.ReportProgress(0, "MyText"); 

bw_DoWork 和:

richTextBox1.AppendText((string)e.UserState); 

bw_ProgressChanged

1

爲了使它更清潔,基於喬恩斯基特的建議下,我做了該做同樣的擴展方法,你可以改變「this Label control」到this TextBox control或簡單地用「this Control control」(基本上允許每個控制可以很容易地更新):

internal static class ExtensionMethods 
{ 
    /// <summary> 
    /// Updates the label text while being used in a multithread app. 
    /// </summary> 
    /// <param name="control">The control.</param> 
    /// <param name="text">The text.</param> 
    internal static void UpdateThreadedText(this Label control, string text) 
    { 
     Action action =() => control.Text = text; 
     control.Invoke(action); 
    } 

    /// <summary> 
    /// Refreshes the threaded. 
    /// </summary> 
    /// <param name="control">The control.</param> 
    internal static void RefreshThreaded(this Label control) 
    { 
     Action action = control.Refresh; 
     control.Invoke(action); 
    } 
} 

然後使用很簡單:

this.yourLabelName.UpdateThreadedText("This is the text"); 
this.yourTextBoxName.UpdateThreadedText("This is the text"); 
this.yourControlName.UpdateThreadedText("This is the text"); 

this.yourLabelName.RefreshThreaded(); 

對我來說很好:)

0

你也可以試試這個。

this.Invoke(new MethodInvoker(delegate() 
{ 
    richtextBox.Text.Add("MyText"); 
}));