更新UI线程控件时,将触发非Invoke-Required路径

时间:2017-03-11 20:15:32

标签: c# multithreading invokerequired

我有以下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始终是真的。有人可以放光吗?

1 个答案:

答案 0 :(得分:1)

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

来自MSDN

  

如果控件的句柄尚不存在,InvokeRequired将向上搜索控件的父链,直到找到具有窗口句柄的控件或表单。如果找不到合适的句柄,InvokeRequired方法将返回 false

     

[...]

     

一种解决方案是在启动后台线程之前等待表单的句柄创建。通过调用Handle属性强制创建句柄,或者等到Load事件开始后台进程。

所以我看到你有两种选择。 1)请等待,直到检查IsHandleCreated property

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

    ...
}

2)检查Handle属性以强制创建窗口句柄(我将其放在while循环中以防万一):

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

    ...
}

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