Reputation: 118
edit: error I got is unrelated to my question. :/ -1
I starting a service on a new thread, then I want to catch an error and display a message box. because its not in the UI thread I get an error. How can I get arround this problem?
(WPF window)
Code:
xmlServiceHost = XcelsiusServiceHost.CreateXmlServiceHost(Properties.Settings.Default.XmlDataService);
serviceThread = new Thread(_ =>
{
try { xmlServiceHost.Open(); }
catch (AddressAccessDeniedException)
{
CreateRegisterDashboardServiceFile();
//Error not in UI thread.
//this.ShowUserInfoMessage("The dashboard service needs to be registered. Please contact support.");
}
});
serviceThread.Start();
Upvotes: 1
Views: 9905
Reputation: 4657
In general, accessing the UI thread from any other thread requires using a Dispatcher
on the UI thread to marshal the method calls to the UI thread.
The solution provided by @hans certainly will work, but there's a much leaner way of doing it that also doesn't have the requirement of tracking the reference to the main window or its dispatcher yourself.
For example, here's how to show a modal MessageBox owned by the application's main window, from another thread, in one line:
public void SomeMethodOnAnotherThread()
{
// You can, of course, do whatever you want inside the anonymous method, but you should limit it to only what needs to be sent to the UI thread
Application.Current.Dispatcher.Invoke( ( ) => { MessageBox.Show( Application.Current.MainWindow, "Hello from the other thread!" ); } );
}
Upvotes: 0
Reputation: 1031
(This answer is for WPF.)
Well, you can open a message box from - lets say - a worker thread, but you cannot set its parent to something that belongs to the UI thread (because the worker thread would be changing the parent window by adding a new child, and the parent window does not belong to the worker thread, it normally belongs to the UI thread), so you are basically forced to leave the parent null.
This will lead to a bunch of message boxes lying behind your application window if users don't close them but reactivate the application window.
What you should do is to create the message box on the UI thread with the proper parent window. For that you will need the dispatcher for the UI thread. The dispatcher will open your message box on the UI thread and you can set its proper parent.
In situations like that I normally pass the UI dispatcher into the worker thread when I start the thread and then use a little helper class, this is particularly useful for handling exceptions on the worker thread:
/// <summary>
/// a messagebox that can be opened from any thread and can still be a child of the
/// main window or the dialog (or whatever)
/// </summary>
public class ThreadIndependentMB
{
private readonly Dispatcher uiDisp;
private readonly Window ownerWindow;
public ThreadIndependentMB(Dispatcher UIDispatcher, Window owner)
{
uiDisp = UIDispatcher;
ownerWindow = owner;
}
public MessageBoxResult Show(string msg, string caption="",
MessageBoxButton buttons=MessageBoxButton.OK,
MessageBoxImage image=MessageBoxImage.Information)
{
MessageBoxResult resmb = new MessageBoxResult();
if (ownerWindow != null)
uiDisp.Invoke(new Action(() =>
{
resmb = MessageBox.Show(ownerWindow, msg, caption, buttons, image);
}));
else
uiDisp.Invoke(new Action(() =>
{
resmb = MessageBox.Show( msg, caption, buttons, image);
}));
return resmb;
}
}
Upvotes: 5
Reputation: 118
Just showing a normal message box on that thread works fine. The "this" keyword and calling a method on my UI thread is the problem.
xmlServiceHost = XcelsiusServiceHost.CreateXmlServiceHost("http://localhost:123/naanaa");//Properties.Settings.Default.XmlDataService);
serviceThread = new Thread(_ =>
{
try { xmlServiceHost.Open(); }
catch (AddressAccessDeniedException)
{
CreateRegisterDashboardServiceFile();
System.Windows.MessageBox.Show("The dashboard service needs to be registered. Please contact support.");
}
});
serviceThread.Start();
Upvotes: 1