开发者

Popping a MessageBox for the main app with Backgroundworker in WPF

In a WPF app, I am using a BackgroundWorker to periodically check for a condition on the server. While that works fine, I want to pop a MessageBox notifing the users if something fails during the check.

Here's what I have:

public static void StartWorker()
{
    worker = new BackgroundWorker();

    worker.DoWork += DoSomeWork;
    worker.RunWorkerAsync();
}

private static void DoSomeWork(object sender, DoWorkEventArgs e)
{
    while (!worker.CancellationPending)
    {
        Thread.Sleep(5000);

        var isOkay = CheckCondition();

        if(!isOkay)
           MessageBox.Show("I should block the main window");                
    }   开发者_如何转开发
}

But this MessageBox does not block the main window. I can still click on my WPF app and change anything I like with the MessageBox around.

How do I solve this? Thanks,


EDIT:

For reference, this is what I ended up doing:

public static void StartWorker()
{
    worker = new BackgroundWorker();

    worker.DoWork += DoSomeWork;
    worker.ProgressChanged += ShowWarning;
    worker.RunWorkerAsync();
}

private static void DoSomeWork(object sender, DoWorkEventArgs e)
{
    while (!worker.CancellationPending)
    {
        Thread.Sleep(5000);

        var isOkay = CheckCondition();

        if(!isOkay)
           worker.ReportProgress(1);                
    }   
}

private static void ShowWarning(object sender, ProgressChangedEventArgs e)
{
    MessageBox.Show("I block the main window");
}


Replace

MessageBox.Show("I should block the main window"); 

with

this.Invoke((Func<DialogResult>)(() => MessageBox.Show("I should block the main window")));

that will cause the message box to be on the main thread and will block all access to the UI until it gets a response. As a added bonus this.Invoke will return a object that can be cast in to the DialogResult.


It doesn't only not block the main window, it is also very likely to disappear behind it. That's a direct consequence of it running on a different thread. When you don't specify an owner for the message box then it goes looking for one with the GetActiveWindow() API function. It only considers windows that use the same message queue. Which is a thread-specific property. Losing a message box is quite hard to deal with of course.

Similarly, MessageBox only disables windows that belong to the same message queue. And thus won't block windows created by your main thread.

Fix your problem by letting the UI thread display the message box. Use Dispatcher.Invoke or leverage either the ReportProgress or RunWorkerCompleted events. Doesn't sound like the events are appropriate here.


Call ReportProgress and pass this to MessageBox.Show.


As Stephen and Hans have said, use the ReportProgress event, and pass data to the UI thread. This is especially important if you want to do anything other tha a MessageBox (for isntance, update a control) because the background thread can't do this directly. You'll get a cross-thread exception.

So whatever you need to do (update progress bar, log messages to the UI, etc.), pass data to the UI thread, tell the UI thread what needs to be done, but let the UI thread do it.


I modified it like this and worked fine for me

 return Application.Current.Dispatcher.Invoke(() => MessageBox.Show(messageBoxText, caption, button, icon));
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜