2009-10-20 78 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); 
    } 
}