C#无效操作异常和跨线程操作

C#无效操作异常和跨线程操作,c#,.net,visual-studio,C#,.net,Visual Studio,在我的windows窗体中,我有一个文本框和一个按钮,文本框“tb_LogBox”是多行文本框。我正在尝试创建一个后台工作程序,当我编译并运行它时,它应该调用一个函数,即“LogTimer.DnT()”。Visual studio抛出InvalidOperationException 我得到的实际错误 跨线程操作无效:从创建控件“tb_LogBox”的线程以外的其他线程访问控件“tb_LogBox”。下面的示例代码说明了我要做的事情 private void button1_Click(obje

在我的windows窗体中,我有一个文本框和一个按钮,文本框“tb_LogBox”是多行文本框。我正在尝试创建一个后台工作程序,当我编译并运行它时,它应该调用一个函数,即“LogTimer.DnT()”。Visual studio抛出InvalidOperationException

我得到的实际错误 跨线程操作无效:从创建控件“tb_LogBox”的线程以外的其他线程访问控件“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 + " =@= " + 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);
    }
}

您需要在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());
    });
}

在执行操作中执行以下操作:

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

您不能使用其他线程(BackgroundWorker使用.NET线程池线程)对UI组件进行更改。这是您在WinForms编程中必须习惯的一个主要障碍。

您需要将Ui更改整理到Ui线程上。这可以通过在tb_LogBox.AppendText周围使用invoke/begininvoke调用来执行

在Winforms应用程序中:

    this.BeginInvoke((MethodInvoker)delegate
        {
            tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
        });
    this.Dispatcher.BeginInvoke(
        (Action)delegate()
        {
            tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
        });
在WPF应用程序中:

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

希望这有帮助

尝试使用begininvoke方法:

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

这将比调用更平滑。

BackgroundWorker正在自己的线程上执行,所有与WinForms GUI元素相关的操作都必须在创建它们的线程上运行。您当前使用BackgroundWorker的方式与仅使用ThreadPool.QueueUserWorkItem()对操作进行排队相同。要使用BackgroundWorker与GUI通信,请使用ReportProges或在worker方法中设置DoWorkEventArgs.Result属性,并对GUI线程上的相应事件作出反应。您还可以在WinForms控件上使用Invoke/BeginInvoke直接在GUI线程上执行任意代码。在您的情况下,这意味着将访问tb_LogBox的线路替换为:

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

无法从后台工作线程的执行线程访问主机线程。您可以使用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 + " =@= " + 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);
    }
}