使用 BackgroundWorker 关闭模态窗口

Posted

技术标签:

【中文标题】使用 BackgroundWorker 关闭模态窗口【英文标题】:Closing a Modal Window with a BackgroundWorker 【发布时间】:2015-03-04 23:05:25 【问题描述】:

我有一个在后台工作程序中运行的带有命名管道服务器的 GUI,它从 DoWork() 事件处理程序中的命名管道获取命令,并与 RunWorkerCompleted() 事件处理程序中的 GUI 控件交互。

GUI 有时会打开模态窗口,这会阻止 RunWorkerCompleted() 事件处理程序在窗口关闭之前运行。

有什么方法可以让我用 Named Pipe Server 和 Backgroundworker 关闭这些 Modal 窗口?

到目前为止,我发现的最好的事情是这篇文章 (How to continue executing code after calling ShowDialog()) 中的 hack,我将使用 Show() 和 Parent.Enabled = False 而不是 ShowDialong()。不过,我希望有一些更清洁的东西。

【问题讨论】:

模式窗口不会阻止 RWC 事件处理程序运行,它没有这样的权力。你需要在另一个角落寻找你的问题。使用 Debug + Windows + Threads 调试器窗口找出 UI 线程在做什么。 感谢您指出我误解了我的问题!您是正确的,模态窗口并没有阻止 RWC 运行。仅当模式窗口因 RWC 而打开 时,才会实际出现该问题。例如,如果我的 BGW 在 DW 中接收到一个命令来按下 GUI 中的按钮,然后它会进入 RWC 并调用按钮的 Click() 事件处理程序。如果 Click() 打开模态窗口以确认/取消按钮按下,则 RWC 不会退出,直到模态窗口关闭。但是,直到 RWC 的最后一行,我才重新启动 BGW,这就是它看起来被锁定的原因。 【参考方案1】:

在模态形式中放置一个可以在辅助线程上调用的方法,例如

Public Sub CloseFromBackground()
    If Me.InvokeRequired Then
        Me.Invoke(New MethodInvoker(AddressOf CloseFromBackground))
    Else
        Me.Close()
    End If
End Sub

显然,您需要对该表单的引用才能调用它,因此您无法将其仅分配给局部变量。我想您可以从OpenForms 集合中获得参考。如果你这样做了,那么你可以使用每个表单的Modal 属性来确定哪个是模态显示的。

例如

Imports System.Threading

Public Class Form1

    Private f2 As Form2

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Me.BackgroundWorker1.RunWorkerAsync()

        Me.f2 = New Form2()
        Me.f2.ShowDialog()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Thread.Sleep(2000)
        Me.f2.CloseFromBackground()
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        MessageBox.Show("Done!")
    End Sub
End Class


Public Class Form2

    Public Sub CloseFromBackground()
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf CloseFromBackground))
        Else
            Me.Close()
        End If
    End Sub

End Class

【讨论】:

非常感谢。我将您的方法置于模态表单中,当我在 DoWork() 事件处理程序中调用 CloseFromBackground() 时,我不再被阻止进入 RunWorkerCompleted() 事件处理程序。不过我有点困惑,因为我期待 CloseFromBackground() 实际关闭模态表单。然而,它似乎在做的只是允许 RunWorkerCompleted() 事件处理程序运行,然后我通过调用其 OK_Button_Click() 事件处理程序关闭模态表单。 这看起来很奇怪。我只是整理了一个例子,它对我来说按预期工作。我已将代码添加到我的答案中。您的项目中可能发生的事情比您告诉我们的要多。 虽然 Hans 的评论帮助我意识到我不了解问题的根本原因,但我不想让您的评论得不到答复(看看它如何回答我之前的原始问题我完全理解我的问题)。 不同的行为是因为我没有为 Form2 创建一个新对象。而不是“Me.f2.ShowDialog()”我有“Form2.ShowDialog()”,而不是“Me.f2.CloseFromBackground()”我有“Form2.CloseFromBackground()”。我认为 VS2010 从 Form2 类创建了一个 Form2 对象以及它从设计器生成的所有其他代码,但似乎我错了。如果我将“Private Form2 As New Form2”添加到 Form1 类,那么它就像您的代码一样工作。我很惊讶我之前在类而不是对象上调用 CloseFromBackground() 时没有遇到异常? 您没有收到异常,因为您没有在课堂上调用它。你在一个对象上调用它,即类的默认实例。问题是默认实例是线程特定的,因此,当您在辅助线程上调用 Form2.CloseFromBackground 时,您实际上是在创建 Form2 的新实例并关闭它而不是关闭现有实例。有关默认实例的更多信息,请阅读:jmcilhinney.blogspot.com.au/2009/07/…

以上是关于使用 BackgroundWorker 关闭模态窗口的主要内容,如果未能解决你的问题,请参考以下文章

vue给模态框弹窗添加路由,实现页面后退可以关闭模态框

微信小程序弹窗广告实现7.23

在同一个aspx页面中同时使用jqgrid和jquery-ui,ui的dialog弹窗一闪就关闭,如何让弹窗留住?

是否可以在没有 BackgroundWorker 的情况下使 WinForms 响应? [关闭]

如何在窗体的关闭事件中停止 BackgroundWorker?

javascript window.showModalDialog模态窗返回数据