从Backgroundworker的DoWork中访问Windows控件

4
我的问题如下:
我有一个Windows窗体,在其中放置了一个LayoutPanel,当窗体加载时,会向LayoutPanel添加多个控件,例如:文本框和标签。
然后在单击按钮时,我需要处理用户在那些动态创建的控件上输入的数据。为此,我使用Backgroundworker来获取这些控件并读取它们的数据。
我的问题是Backgroundworker不允许我从DoWork方法中访问控件,但我必须这样做,因为我将报告操作的进度。
以下是我的部分代码以澄清概念:
private void frmMyForm_Load(object sender, EventArgs e)
    {
       //I add multiple controls, this one is just for example
       LayoutPanel1.add(TextBox1);

       ....
    }

private void bgwBackground_DoWork(object sender, DoWorkEventArgs e)
    {

       foreach (Control controlOut in LayoutPanel1.Controls)
       {
           //do some stuff, this one is just for example
           string myString = controlOut.Name; //-> Here is the error, cant access controls from different Thread.
       }
    }

设置文本很简单,只需使用委托,但如何获取整个父控件以操作子控件(仅用于获取信息,不想设置任何数据,只需要获取名称、文本等)。

希望我表述清楚了,谢谢大家。

2个回答

16

你只能从GUI线程中访问Windows Forms控件。如果你想要从其他线程操作它们,你需要使用Control.Invoke方法,在GUI线程上执行一个委托。在你的情况下,你应该可以这样做:

private void bgwBackground_DoWork(object sender, DoWorkEventArgs e)
{
    foreach (Control controlOut in LayoutPanel1.Controls)
    {
        this.Invoke(new MethodInvoker(delegate {
            // Execute the following code on the GUI thread.
            string myString = controlOut.Name;
        }));
    }
}

我想定义一个扩展方法,使我能够使用更简洁的 lambda 语法:

// Extension method.
internal static void Invoke(this Control control, Action action) {
    control.Invoke(action);
}

// Code elsewhere.
this.Invoke(() => {
    string myString = controlOut.Name;
});

1

正如你已经意识到的那样,从UI线程以外的任何线程访问控制值是不可取的。我认为一个合理的实现方法是使用.NET的同步机制,比如WaitHandle,在UI线程更新您选择的线程安全数据结构时暂停后台线程。

思路是您的后台线程通过已经熟悉的委托机制通知UI线程需要信息,然后等待。当UI完成用信息填充共享变量后,它会重置WaitHandle,后台工作线程恢复。

没有编写和测试所有代码,让我给您一些资源:


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