winforms 在创建窗口句柄之前,不能对控件调用Invoke或BeginInvoke

nwlls2ji  于 2023-02-05  发布在  其他
关注(0)|答案(8)|浏览(352)

我得到了以下抛出的异常:
在创建窗口句柄之前,无法对控件调用Invoke或BeginInvoke。
这是我的代码:

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

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

kx1ctssn

kx1ctssn1#

Invoke和BeginInvoke之间的区别在于前者是同步的(等待完成),而后者是异步的(类似于触发和遗忘)。然而,两者都是通过将消息发布到UI消息循环来工作的,这将导致委托在到达该消息时被执行。
InvokeRequired属性确定您是否需要Invoke,或者它是否已经在正确的线程上,而不是您想要同步调用还是异步调用。如果InvokeRequired为false,则(理论上)已经在UI线程上运行,可以直接执行同步操作(如果需要异步触发它们,则仍然使用BeginInvoke)。这也意味着如果InvokeRequired为false,则不能使用Invoke,因为当前线程上的消息循环无法继续。这是上面代码的一个大问题,但不一定是您报告的错误。如果您注意递归调用等,您 * 可以 * 实际上在任何情况下使用BeginInvoke。
但是,没有窗口句柄就不能使用其中任何一个。如果窗体/控件已经示例化但尚未初始化(即在它第一次显示之前)它可能还没有句柄。并且句柄被Dispose清除(),例如在窗体关闭之后。在这两种情况下,InvokeRequired都将返回false,因为没有句柄就无法调用。您可以选中IsDisposed、还有一个属性IsHandleCreated,它更具体地测试句柄是否存在。通常,如果IsDisposed为真(或IsHandleCreated为假),您希望插入一个特殊情况,例如简单地将操作丢弃为不适用。
因此,您需要的代码可能更像是:

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.
}
tez616oj

tez616oj2#

这种情况通常发生在多线程场景中,在该场景中,一些外部源(可能是NetworkStream)在窗体正确初始化之前将数据推送到窗体。
该消息也可以在释放窗体后显示。
您可以检查IsHandleCreated以查看是否已经创建了表单,但是您需要将所有内容置于适当的错误处理中,因为如果您试图在应用程序关闭时更新表单,Invoke语句可能会引发异常。

vltsax25

vltsax253#

这是我答案
假设你想把"Hello World"写进一个文本框,那么如果你使用"Ishandlecreated",那么你的操作将不会发生,如果处理程序还没有创建,那么你必须强迫它自己创建处理程序。
下面是我的代码

if (!IsHandleCreated)
    this.CreateControl();

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

});
gv8xihay

gv8xihay4#

假设表单没有被释放,但还没有完全初始化,只需将var X = this.Handle;放在if语句之前...... this表示相应表单的示例。
参见http://msdn.microsoft.com/en-us/library/system.windows.forms.control.handle.aspx

yhxst69z

yhxst69z5#

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

myss37ts

myss37ts6#

这个怎么样

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; }));
xpcnnkqh

xpcnnkqh7#

您可能是在窗体的构造函数中调用此函数,此时基础系统窗口句柄尚不存在。

inn6fuwd

inn6fuwd8#

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

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

这个解决方案对我很有效。

相关问题