Как заставить код VBA ждать, пока пользовательская форма vbModeless не будет закрыта - PullRequest
2 голосов
/ 30 апреля 2019

Я бы хотел использовать бесформенную форму пользователя, чтобы пользователь мог перемещаться по листу Excel, прежде чем ответить на вопрос о форме пользователя.Мне нужно сделать паузу или зациклить код, пока пользовательская форма не будет закрыта (скрыта или выгружена).

Проблема, аналогичная этой: Как я могу дождаться запуска определенного кода, когда форма закрыта и закрыта?установить vbModeless? , но решение здесь не работает для моего приложения;Моя пользовательская форма открывается в середине длинной подпрограммы, которую необходимо завершить после закрытия пользовательской формы.

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

Ответы [ 2 ]

4 голосов
/ 30 апреля 2019

Моя пользовательская форма открывается в середине длинной подпрограммы, которая должна завершиться после закрытия пользовательской формы.

Ваша процедура делает слишком много вещей и должна быть разбита на более мелкие, более специализированные процедуры.

Правильный способ сделать это - изменить парадигму с процедурной на управляемой событиями .

Вместо показа экземпляра формы по умолчанию, подобного этому:

StartingSINT_Popup.Show vbModeless 'Open userform

Имейте модуль класса, который содержит 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

Теперь скажите, что вы назвали этот класс 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, и передавайте любые переменные / значения или утверждайте, что объект презентатора должен выполнять свою работу, когда форма закрыта. Вы можете сделать это, выставив свойства или открытые поля / переменные (хотя предпочитайте свойства, но это совсем другая тема):

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
2 голосов
/ 30 апреля 2019

Я не создавал следующую функцию, но долгое время использовал ее, и она работает.

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
...