从Backgroundworker DoWork访问窗口控件

时间:2010-08-11 22:01:13

标签: winforms backgroundworker

我的问题如下:

我有一个Windows窗体,我在其中放置了一个LayoutPanel,当窗体加载时,多个控件如:文本框和标签被添加到LayoutPanel。

然后在按钮上单击,我需要处理用户在这些动态创建的控件上输入的数据。对于那个purpouse我使用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 个答案:

答案 0 :(得分:10)

您只能从GUI线程访问Windows窗体控件。如果要从另一个线程操作它们,则需要使用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 :(得分:1)

正如您已经知道的那样,从UI线程以外的任何线程访问控制值是一个很大的禁忌。我想说一个合理的实现是使用.NET同步机制(如WaitHandle)来暂停后台线程,同时UI线程更新您选择的线程安全数据结构。

这个想法是你的后台线程通知UI线程(通过你已经熟悉的委托机制)它需要信息,然后等待。当UI完成使用信息填充共享变量时,它会重置WaitHandle,后台工作程序将恢复。

在没有写出和测试所有代码的情况下,让我给你一些资源: