How to have VBA code wait until vbModeless userform is closed

后端 未结 3 1296
终归单人心
终归单人心 2021-02-11 02:38

I\'d like to use a formless userform so the user can navigate the excel sheet before answering the question on the userform. I need to pause or loop the code until the userform

相关标签:
3条回答
  • 2021-02-11 03:14

    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
    
    0 讨论(0)
  • 2021-02-11 03:19

    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
    
    0 讨论(0)
  • 2021-02-11 03:20

    Here is an alternative...

    1. In the original [public] module (the one that calls userform 1), declare a public Boolean variable.

    Public done As Boolean

    2. In userform 1,

    a. Assign a default value to the Boolean variable

    b. Call Userform 2

    c. Have a do while loop that...

    • checks for that default value
    • includes the DoEvents method (allows userform 2 to push through despite loop)

    Code

        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. In userform 2, change value of Boolean to break loop

    Code

        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
    
    0 讨论(0)
提交回复
热议问题