如何让 VBA 代码等到 vbModeless 用户窗体关闭

How to have VBA code wait until vbModeless userform is closed

我想使用无表单用户表单,以便用户在回答用户表单上的问题之前可以导航 excel sheet。我需要暂停或循环代码直到用户窗体关闭(隐藏或卸载)。

与此类似的问题: 但是这里的解决方案不适用于我的应用程序;我的用户窗体是在一个很长的子程序中间打开的,该子程序需要在用户窗体关闭后完成执行。

Dim popupActive as Boolean

popupActive = True
StartingSINT_Popup.Show vbModeless 'Open userform

'have VBA code wait until userform is closed
wait until popupActive = False 'set to false with OK button on userform

'continue code with info input inside StartingSINT_Popup userform

下面的函数不是我写的,但是我已经用了很长时间了,确实有效。

Private Function IsLoaded(ByVal formName As String) As Boolean
    Dim frm As Object
    For Each frm In VBA.UserForms
        If frm.Name = formName Then
            IsLoaded = True
            Exit Function
        End If
    Next frm
    IsLoaded = False
End Function

您需要对字符串名称进行硬编码,而不是使用表单的 .Name 属性,因为表单可能尚未加载且不包含此 属性。

以下是如何使用此功能的一小段:

Do While IsLoaded("StartingSINT_Popup")
    Debug.Print Time; " StartingSINT_Popup Is Loaded!"
Loop

My userform is opened in the middle of a long subroutine which needs to finish executing after the userform is closed.

您的程序做的事情太多,需要分解成更小、更专业的程序。

正确的做法是将范例从过程转变为事件驱动.

而不是像这样显示表单的默认实例:

StartingSINT_Popup.Show vbModeless 'Open userform

有一个 class 模块包含它的 WithEvent 个实例:

Private WithEvents popup As StartingSINT_Popup

Private Sub Class_Initialize()
    Set popup = New StartingSINT_Popup
End Sub

Public Sub Show()
    popup.Show vbModeless
End Sub

Private Sub popup_Closed()
    ' code to run when the form is closed
End Sub

在表单的代码隐藏中,声明一个 Closed 事件:

Public Event Closed()

然后在 QueryClose 处理程序中提出它:

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    If CloseMode = 0 Then 'controlbox was clicked (the "red X button")
        Cancel = True 'would otherwise destroy the form instance
        Me.Hide 'always hide, never unload
    End If
    RaiseEvent Closed
End Sub

现在假设您将那个命名为 class PopupPresenter,您的程序现在可以这样做:

Private presenter As PopupPresenter

Public Sub DoStuff()
    Set presenter = New PopupPresenter

    'do stuff...

    presenter.Show

    'rest of the code in this scope will run immediately AND THIS IS FINE

End Sub

将演示者保持在模块级别,以便对象在 DoStuff 完成时不会超出范围,并传递任何 variables/values 或声明演示者对象需要在以下时间完成其工作表格已关闭。您可以通过公开属性或 public fields/variables(虽然更喜欢属性,但这是另一个主题)来做到这一点:

Private WithEvents popup As StartingSINT_Popup
Public Foo As String

Private Sub Class_Initialize()
    Set popup = New StartingSINT_Popup
End Sub

Public Sub Show()
    popup.Show vbModeless
End Sub

Private Sub popup_Closed()
    ' code to run when the form is closed
    MsgBox Foo
End Sub
Private presenter As PopupPresenter

Public Sub DoStuff()
    Set presenter = New PopupPresenter

    'do stuff...

    presenter.Show
    presenter.Foo = "some data"

    'rest of the code in this scope will run immediately AND THIS IS FINE

End Sub

这是一个替代方案...

1.在原来的[public]模块(调用userform 1的那个)中,声明一个public布尔变量。

Public done As Boolean

2。在用户表单 1 中,

一个。为布尔变量分配默认值

b。调用用户窗体 2

c。做一个 while 循环...

  • 检查默认值
  • 包括 DoEvents 方法(允许用户窗体 2 在循环中继续推进)

代码

    Private Sub event_click()

    done = False

    Dim userform2 As New userform
    userform2.Show Modeless

    'This will loop through until userform2 changes done variable to "True"
    Do While done = False
    DoEvents
    Loop

    'Code after done with userform2
    dataSource.Refresh

    End Sub

3。在用户窗体 2 中,更改布尔值以中断循环

代码

    Private Sub submit_Click()

    'Userform submit code
    Dim name As String        
    name = TextBox.Value
    sql = "INSERT INTO table (field) VALUES ('" & name & "')"        
    Call query(sql)

    'IMPORTANT: change Boolean variable to break loop before exiting userform
    done = True

    Unload Me

    End Sub