Winforms 从Backgroundworker DoWork访问windows控件

Winforms 从Backgroundworker DoWork访问windows控件,winforms,backgroundworker,Winforms,Backgroundworker,我的问题如下: 我有一个windows窗体,我在其中放置了一个布局面板,当窗体加载时,多个控件(如:文本框和标签)将添加到布局面板 然后单击按钮,我需要处理用户在这些动态创建的控件上输入的数据。出于这个目的,我使用了一个后台工作人员,该人员负责获取这些控件并读取它们的数据 我的问题是Backgroundworker不允许我从DoWork方法访问控件,但我需要这样做,因为我将报告操作的进度 以下是我的代码中澄清概念的部分: private void frmMyForm_Load(object se

我的问题如下:

我有一个windows窗体,我在其中放置了一个布局面板,当窗体加载时,多个控件(如:文本框和标签)将添加到布局面板

然后单击按钮,我需要处理用户在这些动态创建的控件上输入的数据。出于这个目的,我使用了一个后台工作人员,该人员负责获取这些控件并读取它们的数据

我的问题是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.
       }
    }
使用委托设置文本很简单,但是让整个父控件操作子控件如何(只是为了获取信息,我不想设置任何数据,只需要获取名称、文本之类的内容)


希望我说清楚,谢谢大家。

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

其思想是,后台线程通知UI线程(通过您已经熟悉的委托机制)它需要信息,然后等待。当UI用信息填充完共享变量后,它将重置WaitHandle,后台工作程序将继续

在不编写和测试所有代码的情况下,让我为您提供一些参考资料:

  • WaitHandle.WaitOne文档及其用法示例:
  • 我自己最喜欢的在UI线程上调用事件的方法:

您只能从GUI线程访问Windows窗体控件。如果要从另一个线程操作它们,则需要使用该方法传入一个委托,以便在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;
        }));
    }
}
我想定义一个扩展方法,允许我使用cleaner lambda语法:

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

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