在WPF中使用Backgroundworker弹出主应用程序的消息框

问题描述 投票:0回答:6

在 WPF 应用程序中,我使用 BackgroundWorker 定期检查服务器上的情况。虽然这工作正常,但我想弹出一个消息框,如果检查过程中出现问题,通知用户。

这是我所拥有的:

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

但是这个MessageBox并不会遮挡主窗口。我仍然可以单击我的 WPF 应用程序并使用 MessageBox 更改我喜欢的任何内容。

我该如何解决这个问题?谢谢,


编辑:

仅供参考,这就是我最终所做的:

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");
}
c# wpf multithreading backgroundworker messagebox
6个回答
13
投票

更换

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

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

这将导致消息框位于主线程上,并阻止对 UI 的所有访问,直到收到响应。作为额外的好处, this.Invoke 将返回一个可以转换为 DialogResult 的对象。


11
投票

它不但不会挡住主窗口,还很有可能消失在主窗口后面。这是它在不同线程上运行的直接结果。当您没有指定消息框的所有者时,它会使用 GetActiveWindow() API 函数寻找所有者。它只考虑使用相同消息队列的窗口。这是线程特定的属性。当然,丢失消息框是很难处理的。

同样,MessageBox 仅禁用属于同一消息队列的窗口。因此不会阻塞主线程创建的窗口。

通过让 UI 线程显示消息框来解决您的问题。使用 Dispatcher.Invoke 或利用 ReportProgress 或 RunWorkerCompleted 事件。听起来这些活动在这里不合适。


8
投票

致电

ReportProgress
并将
this
传递给
MessageBox.Show


2
投票

正如 Stephen 和 Hans 所说,使用 ReportProgress 事件,并将数据传递到 UI 线程。如果您想要执行 MessageBox 以外的任何操作(例如,更新控件),这一点尤其重要,因为后台线程无法直接执行此操作。你会得到一个跨线程异常。

因此,无论您需要做什么(更新进度条、向 UI 记录消息等),将数据传递给 UI 线程,告诉 UI 线程需要做什么,但让 UI 线程去做。


2
投票

我像这样修改了它并且对我来说效果很好

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

0
投票

为有关使用 ReportProgress 的建议添加一些技巧:

这是鱼与熊掌兼得的好方法!或者换句话说,这是一种让您在 BackgroundWorker

 中编写 UI 交互代码并且不会遇到跨线程操作的方法。有一天我把这个放在一起做一个项目,买家要小心!

' Init background worker Dim _BGWorker As BackgroundWorker ' Your Windows Form's _Load event handler: Private Sub Form_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' Just for this example, we're newing up the _BGWorker here _BGWorker = New BackgroundWorker() _BGWorker.WorkerReportsProgress = True End Sub ' Magical UI Action Handling ProgressChanged Event Handler Thing (v24.4.550-alpha9) ™ © Private Sub _BGWorker_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles _BGWorker.ProgressChanged ' Take the UserState object and cast to an Action delegate type Dim uiAction As Action = CType(e.UserState, Action) ' Check if an action was passed If uiAction IsNot Nothing Then ' Run it if so! uiAction() End If End Sub ' Standard DoWork handler for BackroundWorker Private Sub _BGWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles _BGWorker.DoWork '...your background worker code... ' EXAMPLE: ' ' Me.Text = "Background worker is (trying to) change form title! :-O" ' ' This line would normally fail with an exception when written directly in the DoWork ' handler of a BackgroundWorker. Exception you would see is: ' System.InvalidOperationException: Cross-thread operation not valid: Control 'FormName' ' accessed from a thread other than the thread it was created on.' ' BUT... ' If we write something like this: _BGWorker.ReportProgress(-1, New Action( Sub() '...and put that line inside this action: Me.Text = "Background worker is changing form title! :-O" ' Then NO PROBLEM! UI-interactive code MAGIC! ' Well, it's not magic... This works because this code is not executed here immediately. ' It is an Action Delegate: https://learn.microsoft.com/en-us/dotnet/api/system.action-1?view=net-7.0 ' You can write ANY UI-interactive code inside blocks like this in your BackgroundWorkers ' and they will all execute on the main thread, because the Action delegate is being ' shipped to the ProgressChanged event handler on the main Form thread. 'TODO: Add your other UI code... End Sub)) ' Now simply repeat the above section every time you wish write UI-interactive ' code in your BG workers! SHAZAM! '...your background worker code... End Sub
    
© www.soinside.com 2019 - 2024. All rights reserved.