How to have VBA code wait until vbModeless userform is closed

試著忘記壹切 提交于 2019-12-04 17:11:03
K.Dᴀᴠɪs

I didn't author the following function, but I have used it for a long time and it works.

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

You will need to hardcode the string name, and not use the .Name property of the form because the form may not be loaded yet and not contain this property.

Here is a small snippet of how you can use this function:

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.

Your procedure is doing too many things and needs to be broken down into smaller, more specialized procedures.

The correct way to do this, is to shift the paradigm from procedural to event-driven.

Instead of showing the form's default instance like this:

StartingSINT_Popup.Show vbModeless 'Open userform

Have a class module that holds a WithEvent instance of it:

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

In the form's code-behind, declare a Closed event:

Public Event Closed()

And then raise it in the QueryClose handler:

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

Now say you named that class PopupPresenter, your procedure can now do this:

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

Keep the presenter at module level so that the object doesn't go out of scope when DoStuff finishes, and pass any variables/values or state that the presenter object needs to do its job when the form is closed. You can do this by exposing properties or public fields/variables (prefer properties though, but that's a whole other topic):

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
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!