Monday, December 20, 2010

C# form topmost in application scope only

I was creating a little "Find" dialog box for my recent treeview application. I wanted the box to be like in Visual Studio itself, in the fact that the find box is topmost for anything in Visual Studio, but not over the rest of the programs that are running.

In the C# form designer, you can set the form to have "Topmost = true", which places the form on top of every window in the entire Windows scope. Not quite what I wanted...

To get it to be topmost, you need to call the Show() method when displaying the form, passing to it a reference to the parent form that it will always be on top of. Like so:

FindForm frm = new FindForm();

frm.Show(this);


(FindForm is my search dialog, and this is the parent form calling it)

Now it's topmost in the application scope but not in the Windows scope!

Wednesday, December 15, 2010

C# GUI Hangs on Invoke

If you've ever tried doing any advanced work using GUIs in C#, you'll be friends/enemies with the good old fashioned Cross-thread operation not valid exceptions. GUI controls run on their own thread and any other thread can't access them.

The great work around is to invoke the control. For instance, I use the following method to update a richTextBox control from any thread I happen to be in:

public delegate void StringParameterDelegate(string value);

public void UpdateRichTextBoxStatus(string value)

{

if (InvokeRequired)

{

// We're not in the UI thread, so we need to call Invoke

Invoke(new StringParameterDelegate(UpdateRichTextBoxStatus), new object[] { value });

return;

}

// Must be on the UI thread if we've got this far

richTextBoxStatus.Text = value + richTextBoxStatus.Text;

}



It checks to see if the control needs invoking (to avoid the cross-thread exception). If it does, it will reursively call itself again using a delegate and then update the richTextBox.

This worked fine and dandy most of the time. However, I had one condition where different invocations were happening at the same time, on different controls, but being invoked from an external, managed DLL. What happened is the invoke occured, but then the whole program hung on a call to the DLL.

Further investigation (god bless the parallel stacks threading debugging view) showed that one of the invocations went into a sleep state, waiting for the other invocation to finish. The Invoke call itself is synchronous, so the next invoke call was waiting for the original to finish. And hence the program just hung there. Waiting... waiting... never finishing.

Well there's an easy workaround for this. Instead of calling Invoke, call BeginInvoke! This makes the call asynchronous. The function thus becomes:

public delegate void StringParameterDelegate(string value);

public void UpdateRichTextBoxStatus(string value)

{

if (InvokeRequired)

{

// We're not in the UI thread, so we need to call BeginInvoke

BeginInvoke(new StringParameterDelegate(UpdateRichTextBoxStatus), new object[] { value });

return;

}

// Must be on the UI thread if we've got this far

richTextBoxStatus.Text = value + richTextBoxStatus.Text;

}