从其他类线程安全地更新WinForm控件

4

请问有人能帮我解决以下问题:

有两个类MainForm和LWriter。下面是来自LWriter的一个方法,除了写入文件外,还通过mainForm.UpdateLog(text)向RichTextBox控件发送一些更新。一切都运作良好,但是这个WriteOutput方法还会进行一些复杂的处理,在计算期间会使窗体冻结。

我认为应该将WriteOutput封装在单独的线程中。请问有人能帮我解释如何将WriteOutput(LWriter类)放置在一个线程中,并以安全的方式从主窗体中调用mainForm.UpdateLog()?

我对线程不太熟悉,因此需要帮助,谢谢。

public void WriteOutput(string output, Links[] links)
{
   try {
      using (StreamWriter sw = new StreamWriter(output)) {
         for (int x= 1; x<links.Length;x++) {
       ...
           sw.WriteLine( ... );
           sw.Flush();                              
         }
         mainForm.UpdateLog(<text>);
      }
   } catch(Exception e) { ... }
}

2
为什么要用线程让它变得复杂呢?在WinForms中,最简单的在后台执行任务的方法之一就是使用BackgroundWorker类。在DoWork方法中,您需要适当地保护任何可能共享的对象(并且不要触摸UI)。在DoWorkCompleted事件中,您可以更新UI。 - user166390
4个回答

7
通常情况下,您应该在BackgroundWorker中运行此类耗时操作。定义一个工作方法:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{   
    // execute your WriteOutput method
}

将其设置为DoWork事件处理程序:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync(); // start the worker

要安全地从不同的线程更新UI,请使用Control.BeginInvoke方法:

mainForm.BeginInvoke(
   () => { mainForm.UpdateLog(<text>); });

4

正如Sonu所建议的那样,delegate可用于线程安全调用,您可以使用Linq缩短代码:

this.BeginInvoke( (Action) delegate ()
{
       //code to update UI
});

请参阅此链接以获取更多关于it技术方面的信息。


迄今为止最优雅的解决方案。谢谢。 - AH.

3

delegate 可用于线程安全调用。

请查看 http://msdn.microsoft.com/zh-cn/library/ms171728.aspx

            // This delegate enables asynchronous calls for setting
    // the text property on a TextBox control.
    delegate void SetTextCallback(string text);

    // This method demonstrates a pattern for making thread-safe
    // calls on a Windows Forms control. 
    //
    // If the calling thread is different from the thread that
    // created the TextBox control, this method creates a
    // SetTextCallback and calls itself asynchronously using the
    // Invoke method.
    //
    // If the calling thread is the same as the thread that created
    // the TextBox control, the Text property is set directly. 

    private void SetText(string text)
    {
        // InvokeRequired required compares the thread ID of the
        // calling thread to the thread ID of the creating thread.
        // If these threads are different, it returns true.
        if (this.textBox1.InvokeRequired)
        {   
            SetTextCallback d = new SetTextCallback(SetText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.textBox1.Text = text;
        }
    }

0

与 UI 控件的交互必须在 UI 线程上进行。您可以在后台线程上构建字符串,但在与其交互之前,应使用 Control.Invoke 或 Control.BeginInvoke 将其传输到 UI 线程。

这方面有很多示例可供参考,可以在网络和堆栈溢出上找到。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接