Sabuncu Sabuncu - 9 months ago 25
C# Question

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

I have the following Windows Forms code:

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


When debugging the above code using breakpoints, I am observing that the non-invoke-required path is hit on a first pass, but only once about every 10 runs. I am surprised because the code is on a separate thread, and I am expecting
InvokeRequired
to be true at all times. Can someone please shed light?

Answer Source

It most likely happens because you start the thread in the form's constructor. At that point the form isn't shown yet and its window handle might not have been created by the time the first iteration is performed.

From the 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.

So the way I see it you have two options. 1) Wait until the handle has been created by checking the IsHandleCreated property:

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

    ...
}

2) Check the Handle property to force a window handle to be created (I put it in a while loop just in case):

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

    ...
}

As you see I access this rather than textBox1. I recommend you to do this when checking and invoking as well because Control.Invoke() will lookup the absolute parent (the form) anyway.