在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke

时间:2011-08-24 20:20:07

标签: c# winforms

我抛出了以下异常:

  

在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke。

这是我的代码:

if (InvokeRequired)
{
    BeginInvoke(new UpdateTextFieldDelegate(WriteToForm), finished, numCount);
}
else
    Invoke(new UpdateTextFieldDelegate(WriteToForm), finished, numCount);

我在这个网站上找到了关于这个主题的网页,但我不知道出了什么问题。

8 个答案:

答案 0 :(得分:53)

Invoke和BeginInvoke之间的区别在于前者是同步的(等待完成),而后者是异步的(类似于“忘掉”)。但是,两者都通过向UI消息循环发布消息来工作,这将导致委托在到达该消息时被执行。

InvokeRequired属性确定您是否需要完全调用或者是否已经在正确的线程上,而不是您是否需要同步或异步调用。如果InvokeRequired为false,您(理论上)已经在UI线程上运行,并且可以直接执行同步操作(或者如果需要异步触发它们,则仍然是BeginInvoke)。这也意味着如果InvokeRequired为false,则无法使用Invoke,因为当前线程上的消息循环无法继续。这是您上面的代码的一个大问题,但不一定是您报告的错误。如果你注意递归调用,可以实际上在任何一种情况下都使用BeginInvoke,等等。

但是,如果没有窗口句柄,则不能使用任何一个。如果表单/控件已经实例化但未初始化(即,在它首次显示之前),它可能还没有句柄。并且Dispose()会清除句柄,例如在Form关闭后。在任何一种情况下,InvokeRequired都将返回false,因为没有句柄就无法调用。您可以检查IsDisposed,并且还有一个属性IsHandleCreated,它更具体地测试句柄是否存在。通常情况下,如果IsDisposed为真(或者如果IsHandleCreated为false),您想要进入一个特殊情况,例如简单地将操作丢弃为不适用。

所以,你想要的代码可能更像是:

if (IsHandleCreated)
{
    // Always asynchronous, even on the UI thread already.  (Don't let it loop back here!)
    BeginInvoke(new UpdateTextFieldDelegate(WriteToForm), finished, numCount);
    return; // Fired-off asynchronously; let the current thread continue.

    // WriteToForm will be called on the UI thread at some point in the near future.
}
else
{
    // Handle the error case, or do nothing.
}

或者也许:

if (IsHandleCreated)
{
    // Always synchronous.  (But you must watch out for cross-threading deadlocks!)
    if (InvokeRequired)
        Invoke(new UpdateTextFieldDelegate(WriteToForm), finished, numCount);
    else
        WriteToForm(finished, numCount); // Call the method (or delegate) directly.

    // Execution continues from here only once WriteToForm has completed and returned.
}
else
{
    // Handle the error case, or do nothing.
}

答案 1 :(得分:8)

这通常发生在多线程场景中,其中一些外部源(可能是NetworkStream)在表单正确初始化之前将数据推送到表单。

在处理表单后,也会显示该消息。

您可以检查IsHandleCreated以查看是否已创建表单,但是您需要将所有内容置于正确的错误处理中,因为如果您尝试更新表单时Invoke语句可能会引发异常你的申请正在结束。

答案 2 :(得分:5)

这是我的回答

假设您想在文本框中输入“Hello World”。 然后,如果您使用“Ishandlecreated”,那么如果尚未创建处理程序,则不会执行您的操作。因此,如果尚未创建,则必须强制自己使用CreateHandlers。

这是我的代码

if (!IsHandleCreated)
    this.CreateControl();

this.Invoke((MethodInvoker)delegate
{
  cmbEmail.Text = null;

});

答案 3 :(得分:3)

如果您要在显示控件或使用控件执行其他操作之前使用其他线程中的控件,请考虑在构造函数中强制创建其句柄。这是使用CreateHandle函数完成的。在多线程项目中,“控制器”逻辑不在WinForm中,此功能有助于避免这些类型的错误。

答案 4 :(得分:2)

假设表单尚未处理但尚未完全初始化,只需将var X = this.Handle;放在if语句之前...... this表示相应表单的实例。

请参阅http://msdn.microsoft.com/en-us/library/system.windows.forms.control.handle.aspx

答案 5 :(得分:1)

你可能在表单的构造函数中调用它,此时底层系统窗口句柄还不存在。

答案 6 :(得分:0)

在调用调用方法之前添加以下内容:

while (!this.IsHandleCreated) 
System.Threading.Thread.Sleep(100)  

此解决方案对我有用。

答案 7 :(得分:0)

那呢:

public static bool SafeInvoke( this Control control, MethodInvoker method )
{
    if( control != null && ! control.IsDisposed && control.IsHandleCreated && control.FindForm().IsHandleCreated )
    {
        if( control.InvokeRequired )
        {
            control.Invoke( method );
        }
        else
        {
            method();
        }
        return true;
    }
    return false;
}

像这样使用它:

this.label.SafeInvoke(new MethodInvoker( () => { this.label.Text = yourText; }));