Предполагается, что форма отображается следующим образом:
UserForm1.Show
DoSomething
Тогда форма будет модальной , что означает, что вызов DoSomething
не будет выполняться, пока форма не будет закрыта.Пока отображается модальная форма, она управляет циклом сообщений, и хост-приложение все время недоступно: единственное, с чем пользователь может взаимодействовать, это форма.
Если форма отображается следующим образом:
UserForm1.Show vbModeless
DoSomething
Затем отображается форма, и вызов DoSomething
выполняется немедленно;пользователь по-прежнему может взаимодействовать с хост-приложением, а код в форме может выполняться асинхронно.
Но такой цикл выглядит так:
Do
' do stuff
DoEvents
Loop While {condition}
Плохой дизайн: без DoEvents
Цикл будет полностью перехватывать цикл сообщений, модальное или немодальное не будет иметь значения, и приложение хоста, скорее всего, будет идти (не отвечая), пока цикл не завершится. С DoEvents
это занятая петля, постоянно говорящая Windows: «Эй, у вас есть что запустить?- что вы хотите сделать, это зарегистрировать процедуру, которая будет вызываться раз в секунду для обновления метки таймера в форме.
Эта процедура должна быть в отдельном стандартном модуле - в идеале тот же модуль, которыйпоказ формы и, в идеале, отработка того же экземпляра этой формы.
Option Explicit
Private theForm As UserForm1
Public Sub ShowTheForm()
If theForm Is Nothing Then Set theForm = New UserForm1
theForm.Show vbModeless
End Sub
Public Sub OnTimerTick()
If theForm Is Nothing Then Exit Sub
theForm.HandleTimerTick
End Sub
Теперь форме необходимо предоставить процедуру HandleTimerTick
и запланировать макрос OnTimerTick
.Таким образом, у вас может быть элемент управления CommandButton
, который при щелчке запускает цикл планирования:
Dim TimerStart As Double
Private Sub CommandButton1_Click()
' validate inputs...
TimerStart = Timer
Application.OnTime Now + TimeValue("00:00:01"), "OnTimerTick"
End Sub
Public Sub HandleTimerTick()
'timer has ticked, we're at least 1 second later.
Dim secondsElapsed As Double
secondsElapsed = Timer - TimerStart
'update the textbox accordingly...
TextBox1.Text = Format$(secondsToRun - secondsElapsed, "hh:mm:ss")
'now determine if we need to schedule another tick:
If Int(secondsToRun - secondsElapsed) > 0 Then
Application.OnTime Now + TimeValue("00:00:01"), "OnTimerTick"
End If
End Sub
Обратите внимание, что больше нет явного цикла, нет DoEvents
: просто запланированный макрос, который сообщает форме "эйтам, галочка!и форма отвечает, обновляя себя и, при необходимости, перепланируя другой тик.