我遇到了一个问题,我创建了一个VBA计时器,该计时器从指定时间倒数到零。这个宏运行了一段时间,因此,当我尝试打开另一个工作簿时,什么也没发生,就像宏阻止另一个工作簿打开一样?
我的计时器子
Private Sub Timer15_main(play As Boolean)
Dim UserInput As String
If play Then
UserInput = TextBox1.Value 'this is what the user inputs and how long the timer should run
Else
timer_15_pause_button = False
UserInput = "00:15:00" 'this is what the user inputs and how long the timer should run
End If
'validate userinput und ensure hh:mm:ss format
Select Case Len(UserInput) - Len(Replace$(UserInput, ":", ""))
Case 2 'input format is hh:mm:ss
Case 1 'input format is mm:ss
UserInput = "00:" & UserInput
Case 0 'input format is ss
UserInput = "00:00:" & UserInput
Case Else
MsgBox "invalid input"
Exit Sub
End Select
'we need to convert the string UserInput into a double and
'convert it into seconds (Timer uses seconds!)
Dim SecondsToRun As Long
SecondsToRun = CDbl(TimeValue(UserInput)) * 24 * 60 * 60
TextBox4.Value = Format$((SecondsToRun / 24 / 60 / 60) + Time(), "hh:mm:ss")
Dim TimerStart As Double
TimerStart = Timer 'remember when timer starts
Do
If SecondsToRun - (Timer - TimerStart) < 10 Then
TextBox1.BackColor = RGB(255, 0, 0)
End If
TextBox1.Value = Format$((SecondsToRun - (Timer - TimerStart)) / 24 / 60 / 60, "hh:mm:ss")
'count backwards from 01:15 format as hh:mm:ss
DoEvents
If timer_15_pause_button = True Then
Exit Sub
End If
Loop While TimerStart + SecondsToRun > Timer 'run until SecondsToRun are over
TextBox1.BackColor = RGB(255, 255, 255)
'TextBox4.Value = ""
End Sub
答案 0 :(得分:2)
假设表单显示如下:
UserForm1.Show
DoSomething
然后,该表单为 modal ,这意味着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
:只是一个计划的宏,它告诉形式“嘿,打勾!”表格会通过更新自身进行响应,并在需要时重新安排另一个刻度。