确保在主UI线程中创建子控件

时间:2011-11-30 18:41:45

标签: c# .net winforms multithreading

我正在修改现有的WinForms项目。该项目有UserControl。 此UserControl具有DataSet变量,该变量是从程序的另一部分在不同的线程中设置的。

我想要做的是根据DataSet动态地为该控件添加另一个控件。

因此,在加载DataSet之后,我正在调用RefreshChildControl函数并尝试将新的ChildUserControls添加到flowLayoutPanel。这就是问题开始的地方:)。我得到“跨线程操作无效:控制'ChildUserControl'从一个线程访问,而不是它在”异常时创建的线程。我试图使用if(this.InvokeRequired)和Invoke这个方法,但它没有帮助。 MyUserControl上的InvokeRequired为false。

那么,有没有什么好方法可以执行这样的任务?或者我错过了一些重要的东西?

编辑:

我试图跳过InvokeRequired测试,只需调用this.FindForm()。调用此方法。我有“在创建窗口句柄之前无法在控件上调用Invoke或BeginInvoke”。例外。顺便说一下,当我用这个控件打开另一个表单时,一切都运行良好。

2 个答案:

答案 0 :(得分:2)

首先。最简单的解决方案是每次都执行Invoke。什么都不会发生。 其次,使用SynchronizationContext。

using System.Threading;
public class YourForm
{
    SynchronizationContext sync;
    public YourForm()
    {
        sync = SynchronizationContext.Current;
        // Any time you need to update controls, call it like this:
        sync.Send(UpdateControls);
    }

    public void UpdateControls()
    {
        // Access your controls.
    }
}

SynchronizationContext将为您管理所有线程问题。它会检查您是从同一个线程还是从另一个线程调用。如果相同,它将立即执行您的代码。否则它将通过表单的消息循环进行调用。

答案 1 :(得分:1)

如果在构造之后没有立即看到用户控件,则不会在您认为创建它的线程上创建句柄。它不是C#对象,它的线程父是重要的,它是父句很重要的Windows Handle对象。

要强制在您认为创建它的线程上立即创建控件,那么 读出control.Handle,它将强制实际控制并分配句柄。

 MyUserControl uc = new MyUserControl(); // the handle is not created here
 uc.Visible = false;
 IntPtr dummy = uc.Handle; //  The control is immediately given a real handle

您也可以尝试使用uc.CreateControl,但如果控件不可见,则不会创建句柄。

现在,即使用户控件不可见,您也可以让另一个线程更新您的用户控件。

 uc.BeginInvoke((Action)(() => uc.Text = "ha ha"));

如果省略dummy = uc.Handle行,您将收到一个异常,您无法在没有句柄的控件上调用BeginInvoke。

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.createcontrol(v=vs.90).aspx