views:

592

answers:

3

How can I force a section of code to be executed on my main thread?

This is why I'd like to know:

I have a custom created message box that at times gets shown from a thread that is not the main thread. However, when the message box constructor is called I get an InvalidOperationException saying "The calling thread must be STA, because many UI components require this." This makes sense, UI elements need to be handled on the main thread.

My MessageBox.ShowMessage(...) function is a static function that creates an instance of my custom message box and shows it. Is there a something I could put in ShowMessage that would force the message box to be created and shown on the main thread? Elsewhere in my code I use the Control.BeginInvoke to handle similar issues, but since it is a static function there is no already existing UI element for me to call BeginInvoke on.

Do I have to call MessageBox.ShowMessage from with a Control.BeginInvoke? I'd prefer the BeginInvoke (or some equivalent) to be called from within ShowMessage.

+2  A: 

There are a few options here:

  • make the second thread STA (you can only do this for your own Thread - not for ThreadPool threads) - via .SetApartmentState(ApartmentState.STA);
  • see if SynchronizationContext.Current is non-null; if so, use Send/Post
  • pass the form/control in as an ISynchronizeInvoke instance (may not apply to WPF - I'm not 100% sure)
Marc Gravell
Do you have a link that explains how to go about implementing the first option you list?
w4g3n3r
Call Thread.SetApartmentState() before starting the thread.
itowlson
When creating the Thread, just call .SetApartmentState as shown in the (edited) answer. Let me know if you need more assistance creating the thread in the first place.
Marc Gravell
Is any of these option better than the others? Is it good practice to have multiple threads STA? SychronizationContext.Current is null, does that mean that option is eliminated?
Dan Vogel
Marc, can you explain if this is better/worse than telling the main form to invoke the messagebox?
BenAlabaster
@balabaster - different. I'd *probably* prefer just one STA, but either works AFAIK.
Marc Gravell
+3  A: 

Your thinking is right -- in order to get it to work properly, you're going to need to get it called from the main thread.

The simplest way? When you start your main form, save a reference in a static variable that is visible to your ShowMessage() call. Then, your ShowMessage can do the standard:

if(myForm.InvokeRequired)
{
     myForm.Invoke(() => ShowMessage(arg1,arg2,arg3));
     return;
}
.... other code here....
Jonathan
He may not actually need an explicit variable -- in WPF he could use Application.Current.MainWindow, in WinForms a window from the Application.OpenForms collection.
itowlson
+2  A: 

Instead of directly showing the message box, just send a message to your main thread, which signals the main thread to display a message box.

Blank Xavier
+1 this is actually much less intrusive IMO
Rick