2009-10-20 77 views
0

在我的Windows窗體中我有一個文本框和一個按鈕,文本框「tb_LogBox」是多行文本框我試圖創建一個應該調用一個函數的後臺工作, LogTimer.DnT()「當我編譯時,並運行它Visual Studio中拋出InvalidOperationException。C#InvalidOperationException和跨線程操作

實際發生的錯誤 跨線程操作無效:從其創建的線程以外的線程訪問控制'tb_LogBox'。下面的代碼示例說明了什麼,我試圖做

private void button1_Click(object sender, EventArgs e) 
{ 
    try 
    { 
     var bw = new BackgroundWorker(); 
     bw.DoWork += ExecuteOperations ; 
     bw.RunWorkerAsync(); 
    } 
    catch (Exception ex) 
    { 
     tb_LogBox.AppendText(Environment.NewLine + " [email protected]= " + ex.Message+" "+ex.Source); 
    } 
} 

private void ExecuteOperations(object sender, DoWorkEventArgs e) 
{ 
    var FuncCall = new LogTimer(); 
    tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT()); // the line i am getting the error. on 
} 

public class LogTimer 
{ 
    public string DnT() 
    { 
     const string datePat = @"d/MM/yyyy"; 
     var dateTime = DateTime.Now(); 
     return dateTime.ToString(datePat); 
    } 
} 

回答

4

嘗試使用開始invoke方法:

BeginInvoke(new Action(() => 
    { 
     tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT()); 
    })); 

這將是比調用順暢。

0

您需要調用控件的方法在UI線程:

private void ExecuteOperations(object sender, DoWorkEventArgs e) 
{ 
    var FuncCall = new LogTimer(); 
    tb_LogBox.Invoke((MethodInvoker)delegate{ 
     tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime()); 
    }); 
} 

我不知道是什麼LogTimer做,但它很可能的情況是,你應該創建一個代理裏面還有:

private void ExecuteOperations(object sender, DoWorkEventArgs e) 
{ 
    tb_LogBox.Invoke((MethodInvoker)delegate{ 
     var FuncCall = new LogTimer(); 
     tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime()); 
    }); 
} 
1

在ExecuteOperations這樣做:

tb_LogBox.Invoke((MethodInvoker)delegate() { tb_LogBox.AppendText(...) })); 

您不能使用其他線程(BackgroundWorker使用.NET線程池線程)更改UI組件。這是您在WinForms編程中必須習慣的主要障礙。

2

您需要將UI更改編組到UI線程中。這可以通過使用周圍的tb_LogBox.AppendText

在WinForms應用程序的調用/ BeginInvoke來調用執行:

this.BeginInvoke((MethodInvoker)delegate 
     { 
      tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime()); 
     }); 
在WPF應用程序

this.Dispatcher.BeginInvoke(
     (Action)delegate() 
     { 
      tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime()); 
     }); 

希望這有助於!

0

BackgroundWorker正在其自己的線程上執行,並且所有與WinForms GUI元素相關的操作都必須在它們創建的線程上運行。他們使用BackgroundWorker的方式與使用ThreadPool.QueueUserWorkItem()排隊操作完全相同。對於使用BackgroundWorker與GUI進行通信,使用R​​eportProgess或在worker方法中設置DoWorkEventArgs.Result屬性,並對GUI線程上的相應事件做出反應。您還可以使用WinForms控件上的Invoke/BeginInvoke直接在GUI線程上執行任意代碼。在你的情況下,這意味着替換訪問tb_LogBox的行:

tb_LogBox.Invoke(new Action(() => 
    tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime()); 
)); 
0

你不能從後臺工作者的執行線程訪問主機線程。您可以使用BackgroundWorker的ReportProgress方法將信息發送到主機線程。

private void button1_Click(object sender, EventArgs e) 
{ 
    try 
    { 
     var bw = new BackgroundWorker(); 
     bw.DoWork += ExecuteOperations; 
     bw.ProgressChanged += bw_ProgressChanged; 
     bw.RunWorkerAsync(); 
    } 
    catch (Exception ex) 
    { 
     tb_LogBox.AppendText(Environment.NewLine + " [email protected]= " + ex.Message + " " + ex.Source); 
    } 
} 

private static void ExecuteOperations(object sender, DoWorkEventArgs e) 
{ 
    var FuncCall = new LogTimer(); 
    string text = Environment.NewLine + FuncCall.DnT(); 

    (sender as BackgroundWorker).ReportProgress(0, text); 
} 
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    tb_LogBox.AppendText(e.UserState as string); 
} 

public class LogTimer 
{ 
    public string DnT() 
    { 
     const string datePat = @"d/MM/yyyy"; 
     var dateTime = DateTime.Now; 
     return dateTime.ToString(datePat); 
    } 
}