更新 UI 线程控制时命中非调用必需路径

Non-Invoke-Required path gets hit when updating UI thread control

我有以下 Windows 表单代码:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        new Thread(SampleFunction).Start();
    }

    void SampleFunction()
    {
        for (int i = 0; i < 10; i++)
        {
            if (textBox1.InvokeRequired == true)
                textBox1.Invoke((MethodInvoker)delegate { textBox1.Text += "HI. "; });
            else
                textBox1.Text += "HII. "; // Sometimes hit on first pass of the loop.
            Thread.Sleep(1000);
        }
    }

当使用断点调试上述代码时,我观察到非调用所需的路径在第一次通过时被命中,但大约每 10 次运行只有一次。我很惊讶,因为代码在一个单独的线程上,我希望 InvokeRequired 始终为真。有人可以解释一下吗?

这很可能是因为您在窗体的构造函数中启动了线程。此时表单尚未显示,其 window 句柄可能在第一次迭代执行时尚未创建。

来自MSDN

If the control's handle does not yet exist, InvokeRequired searches up the control's parent chain until it finds a control or form that does have a window handle. If no appropriate handle can be found, the InvokeRequired method returns false.

[...]

One solution is to wait until the form's handle has been created before starting the background thread. Either force handle creation by calling the Handle property, or wait until the Load event to start the background process.

所以在我看来,您有两种选择。 1) 等到通过检查 IsHandleCreated property:

创建句柄
for (int i = 0; i < 10; i++)
{
    while (!this.IsHandleCreated) { Thread.Sleep(25); }

    ...
}

2) 检查 Handle 属性 以强制创建 window 句柄(我把它放在 while循环以防万一):

for (int i = 0; i < 10; i++)
{
    while (this.Handle == IntPtr.Zero) { Thread.Sleep(25); }

    ...
}

如您所见,我访问的是 this 而不是 textBox1。我建议您在检查和调用时也这样做,因为 Control.Invoke() 无论如何都会查找绝对父级(表单)。