我正在修改现有的WinForms项目。该项目有UserControl。 此UserControl具有DataSet变量,该变量是从程序的另一部分在不同的线程中设置的。
我想要做的是根据DataSet动态地为该控件添加另一个控件。
因此,在加载DataSet之后,我正在调用RefreshChildControl函数并尝试将新的ChildUserControls添加到flowLayoutPanel。这就是问题开始的地方:)。我得到“跨线程操作无效:控制'ChildUserControl'从一个线程访问,而不是它在”异常时创建的线程。我试图使用if(this.InvokeRequired)和Invoke这个方法,但它没有帮助。 MyUserControl上的InvokeRequired为false。
那么,有没有什么好方法可以执行这样的任务?或者我错过了一些重要的东西?
编辑:
我试图跳过InvokeRequired测试,只需调用this.FindForm()。调用此方法。我有“在创建窗口句柄之前无法在控件上调用Invoke或BeginInvoke”。例外。顺便说一下,当我用这个控件打开另一个表单时,一切都运行良好。
答案 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