2

一般来说,我是 C# 和 GUI 开发的新手。如果这是一个老问题,请直接引导我到那个来源,我会记下这个问题。

我的情况:所以我正在编写一个 GUI,它将一个函数的控制台输出重定向到我的 Windows 应用程序表单中的文本框。控制台输出仅向用户显示设备序列号、当前软件版本等信息。

我的问题:这个表格很好用,除了更新软件的过程需要几分钟,这会冻结我的表格,直到它完成。现在,我知道实施后台工作人员将缓解这个问题。但是,当我实现后台工作人员时,我收到以下错误。

跨线程操作无效:控件“TextBox”从创建它的线程以外的线程访问。

我的代码摘要如下:

public class Form1 : Form
{
    this.backgroundWorker1.DoWork += new
    System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
         BackgroundWorker bw = sender as BackgroundWorker;     
         BigProcess(bw);
    }

    private void BigProcess(BackgroundWorker bw)
    {
        // lengthy operation that includes lots of
        Console.WriteLine("feedback stuff for the user");
    }

    private void button1_Click(object sender, EventArgs e)
    {
        this.backgroundWorker1.RunWorkerAsync();
    }
}

在这个项目中,我还有一个TextBoxStreamWriter派生的类,StringWriter它为我处理控制台输出的重定向。在TextBoxStreamWriter我重写WriteLine方法和Write方法。

这是我正在做的一个例子:

public override void WriteLine(string value)
{
      base.WriteLine(DateTime.Now.ToString(value));
      textBoxOutput.AppendText(value.ToString() + Environment.NewLine);
      writer.Write(value);
}

调用此方法时会引发 InvalidOperationException。

我怎样才能使这个线程安全?任何帮助,将不胜感激。

4

3 回答 3

1

您只需要将文本框的更新返回到 UI 线程 - 例如:

if (textBoxOutput.InvokeRequired)
{
  Invoke((MethodInvoker)(() => textBoxOutput.AppendText(value.ToString() + Environment.NewLine);
}
else
{
   textBoxOutput.AppendText(value.ToString() + Environment.NewLine)
}
于 2013-03-01T14:21:10.030 回答
1

BackgroundWorker公开一个事件:RunWorkerCompleted用于使用长时间运行操作的结果更新 UI 。该事件将在 UI 线程中触发。

因此,您需要做的就是处理该事件,而不是手动编组到 UI 线程。

backgroundWorker1.RunWorkerCompleted += (s, args)=> 
    textBoxOutput.AppendText(args.Result.ToString() + Environment.NewLine);

然后,您可以将结果设置为DoWork您想要打印的任何内容。

如果您想在整个后台处理过程中更新 UI,那么您实际上拥有“进度更新”。BGW 内置了对在整个后台工作过程中更新 UI 的支持。有关示例,请参阅 BGW 的 MSDN 页面。

于 2013-03-01T15:05:24.670 回答
0

试试这个:

public override void WriteLine(string value)
{
    if (InvokeRequired)
    {
        Invoke(() => WriteLine(value));
        return;
    }

    base.WriteLine(DateTime.Now.ToString(value));
    textBoxOutput.AppendText(value.ToString() + Environment.NewLine);
    writer.Write(value);
}

这将检查 WriteLine 是否是从与拥有表单的线程不同的线程调用的。如果是,它将要求拥有表单的线程再次调用该方法,仅在正确的线程上。

于 2013-03-01T14:21:10.527 回答