怎么可能呢?我有一个基于System.Windows.Forms.Form派生的Windows窗体控件,其中包含WebBrowser控件。Webbrowser对象实例在窗体的构造函数中创建(在InitializeComponent()方法中)。然后,在后台线程中操作WebBrowser的内容,我发现在某些情况下Form.InvokeRequired == false,而WebBrowser.InvokeRequired == true。这是怎么回事呢?
怎么可能呢?我有一个基于System.Windows.Forms.Form派生的Windows窗体控件,其中包含WebBrowser控件。Webbrowser对象实例在窗体的构造函数中创建(在InitializeComponent()方法中)。然后,在后台线程中操作WebBrowser的内容,我发现在某些情况下Form.InvokeRequired == false,而WebBrowser.InvokeRequired == true。这是怎么回事呢?
Form.InvokeRequired
在窗体显示之前返回 false
。
我进行了简单的测试:
Form2 f2 = new Form2();
Thread t = new Thread(new ThreadStart(() => PrintInvokeRequired(f2)));
t.Start();
t.Join();
f2.Show();
t = new Thread(new ThreadStart(() => PrintInvokeRequired(f2)));
t.Start();
t.Join();
private void PrintInvokeRequired(Form form)
{
Console.WriteLine("IsHandleCreated: " + form.IsHandleCreated + ", InvokeRequired: " + form.InvokeRequired);
}
输出结果为:
IsHandleCreated: False, InvokeRequired: False
IsHandleCreated: True, InvokeRequired: True
还要注意的是,这在MSDN上有一定的文档记录:
如果控件的句柄尚不存在,则 InvokeRequired 沿着控件的父级链搜索,直到找到具有窗口句柄的控件或窗体。如果找不到适当的句柄,则 InvokeRequired 方法返回 false。
这意味着如果不需要调用 Invoke(调用发生在同一线程上)或者控件在不同的线程上创建但是控件的句柄尚未创建,则 InvokeRequired 可以返回 false。
如果控件的句柄尚未创建,则不能简单地调用控件的属性、方法或事件。这可能会导致控件的句柄在后台线程上被创建,将控件隔离到没有消息泵的线程上,并使应用程序不稳定。
您可以通过在后台线程上返回 false 时还检查 IsHandleCreated 的值来防止此情况。如果控件句柄尚未创建,则必须等待它被创建后才能调用 Invoke 或 BeginInvoke。通常,这仅在应用程序的主窗体(如 Application.Run(new MainForm()))的构造函数中创建后台线程之前(在显示窗体或调用 Application.Run 之前)发生。
您的解决方案还要检查 IsHandleCreated
的值。
编辑:
Handle
可以在 WebBrowser 控件内部或外部的任何时候创建。这不会自动创建父窗体的句柄。
我创建了一个示例:
public Form2()
{
InitializeComponent();
Button button1 = new Button();
this.Controls.Add(button1);
Console.WriteLine("button1: " + button1.IsHandleCreated + " this: " + this.IsHandleCreated);
var tmp = button1.Handle; // Forces the Handle to be created.
Console.WriteLine("button1: " + button1.IsHandleCreated + " this: " + this.IsHandleCreated);
}
输出结果如下:
按钮1:假,当前状态:假
按钮1:真,当前状态:假
我一直在调查这个奇怪的行为。 我需要从不同的线程操作一些控件(例如显示连接到主机的设备信息或根据不同设备状态触发操作)。
这个链接给了我一个很好的提示: http://csharpfeeds.com/post/2898/Control.Trifecta_InvokeRequired_IsHandleCreated_and_IsDisposed.aspx
我仍然不知道微软人员打算如何使用他们自己的东西(在许多方面都不同意),但是,在一个应用程序中,我不得不使用以下肮脏和恶心的解决方法:
多么丑陋啊! 我想知道是否有其他人有更好的方法来做到这一点。
_my_control = new ControlClass( );
_my_control.Owner = this;
IntPtr hnd;
// Force Handle creation by reading it.
if ( !_my_control.IsHandleCreated || _my_control.Handle == IntPtr.Zero )
hnd = _my_control.Handle;
(抱歉在这个有点旧的帖子中发帖,但我认为这可能对某些人有用)。