我有一些旧的Excel VBA代码,我想定期运行任务。如果我使用VB6,我会使用定时器控件。
我找到了Application.OnTime()方法,它适用于在Excel工作表中运行的代码,但我无法在用户表单中使用它。该方法永远不会被调用。
如何让Application.OnTime()在用户表单中调用方法,还是有其他方法来安排代码在VBA中运行?
答案 0 :(得分:8)
我找到了解决方法。如果在只调用用户表单中的方法的模块中编写方法,则可以使用Application.OnTime()调度模块方法。
有点像kludge,但它会做,除非有人有更好的建议。
以下是一个例子:
''//Here's the code that goes in the user form
Dim nextTriggerTime As Date
Private Sub UserForm_Initialize()
ScheduleNextTrigger
End Sub
Private Sub UserForm_Terminate()
Application.OnTime nextTriggerTime, "modUserformTimer.OnTimer", Schedule:=False
End Sub
Private Sub ScheduleNextTrigger()
nextTriggerTime = Now + TimeValue("00:00:01")
Application.OnTime nextTriggerTime, "modUserformTimer.OnTimer"
End Sub
Public Sub OnTimer()
''//... Trigger whatever task you want here
''//Then schedule it to run again
ScheduleNextTrigger
End Sub
''// Now the code in the modUserformTimer module
Public Sub OnTimer()
MyUserForm.OnTimer
End Sub
答案 1 :(得分:3)
我需要一个可见的倒数计时器,无论是对工作簿进行更改还是最小化Excel窗口,都可以保持在其他窗口之上并顺利运行。所以,我根据自己的目的调整了@ don-kirkby的创意{{3}},并认为我会分享结果。
code above
下面的代码需要创建模块和用户表单,如评论中所述,或者您可以下载此答案底部的.xlsm
。
我使用进行更准确,更顺畅的倒计时(也可以自定义,最低可达~100毫秒Windows Timer API,具体取决于您的处理器。甚至还有一个“滴答滴答”timer resolution。 ⏰
插入新模块并将其另存为 modUserFormTimer
。在工作表中添加两个sound,标记为 Start Timer 和 Stop Timer 和form control command buttons程序btnStartTimer_Click
和btnStopTimer_Click
。
Option Explicit 'modUserFormTimer
Public Const showTimerForm = True 'timer runs with/without the userform showing
Public Const playTickSound = True 'tick tock (a WAV sounds could be embedded: `https:// goo.gl/ ReuUyd`)
Public Const timerDuration = "00:00:20" 'could also Insert>Object a WAV for tick or alarm
Public Const onTimerStart_MinimizeExcel = True 'minimize Excel? (countdown remains visible)
Public Const onTimerStart_MaximizeExcel = True 'maximize Excel when timer completes?
'timer could be on top of other applications; instructions here: `https:// goo.gl/ AgmWrM`
'safe for 32 or 64 bit Office:
Private Declare PtrSafe Function SetTimer Lib "User32" (ByVal hWnd As Long, ByVal nIDEvent As Long, _
ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As Long
Private Declare PtrSafe Function KillTimer Lib "User32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
Public Declare PtrSafe Function Beep Lib "kernel32" (ByVal dwFreq As Long, ByVal dwDuration As Long) As Long
Public schedTime As Date 'this is the "major" timer set date
Private m_TimerID As Long
Public Sub OnTimerTask()
'the procedure that runs on completion of the "major timer" (timer won't reschedule)
Unload frmTimer
''''''''''''''''''''''''''''''
MsgBox "Do Something!" ' < < < < < Do Something Here
''''''''''''''''''''''''''''''
End Sub
Public Sub btnStartTimer_Click()
schedTime = Now() + TimeValue(timerDuration)
InitTimerForm
End Sub
Public Sub btnStopTimer_Click()
'clicking the 'x' on the userform also ends the timer (disable the close button to force continue)
schedTime = 0
frmTimer.UserForm_Terminate
End Sub
Public Sub InitTimerForm()
'run this procedure to start the timer
frmTimer.OnTimer
Load frmTimer
If showTimerForm Then
If onTimerStart_MinimizeExcel Then Application.WindowState = xlMinimized
frmTimer.Show 'timer will still work if userform is hidden (could add a "hide form" option)
End If
End Sub
Public Sub StartTimer(ByVal Duration As Long)
'Begin Millisecond Timer using Windows API (called by UserForm)
If m_TimerID = 0 Then
If Duration > 0 Then
m_TimerID = SetTimer(0, 0, Duration, AddressOf TimerEvent)
If m_TimerID = 0 Then
MsgBox "Timer initialization failed!", vbCritical, "Timer"
End If
Else
MsgBox "The duration must be greater than zero.", vbCritical, "Timer"
End If
Else
MsgBox "Timer already started.", vbInformation, "Timer"
End If
End Sub
Public Sub StopTimer()
If m_TimerID <> 0 Then 'check if timer is active
KillTimer 0, m_TimerID 'it's active, so kill it
m_TimerID = 0
End If
End Sub
Private Sub TimerEvent()
'the API calls this procedure
frmTimer.OnTimer
End Sub
接下来,创建用户表单,将其另存为 frmTimer
。添加名为txtCountdown
的文本框。将属性ShowModal
设置为False
。将以下内容粘贴到表单的代码窗口中:
Option Explicit 'code for userform "frmTimer"
'requires a textbox named "txtCountdown" and "ShowModal" set to False.
Dim nextTriggerTime As Date
Private Sub UserForm_Initialize()
ScheduleNextTrigger
End Sub
Public Sub UserForm_Terminate()
StopTimer
If schedTime > 0 Then
schedTime = 0
End If
If onTimerStart_MaximizeExcel Then Application.WindowState = xlMaximized 'maximize excel window
Unload Me
End Sub
Private Sub ScheduleNextTrigger() 'sets the "minor" timer (for the countdown)
StartTimer (1000) 'one second
End Sub
Public Sub OnTimer()
'either update the countdown, or fire the "major" timer task
Dim secLeft As Long
If Now >= schedTime Then
OnTimerTask 'run "major" timer task
Unload Me 'close userForm (won't schedule)
Else
secLeft = CLng((schedTime - Now) * 60 * 60 * 24)
If secLeft < 60 Then 'under 1 minute (don't show mm:ss)
txtCountdown = secLeft & " sec"
Else
'update time remaining in textbox on userform
If secLeft > 60 * 60 Then
txtCountdown = Format(secLeft / 60 / 60 / 24, "hh:mm:ss")
Else 'between 59 and 1 minutes remain:
txtCountdown = Right(Format(secLeft / 60 / 60 / 24, "hh:mm:ss"), 5)
End If
End If
If playTickSound Then Beep 16000, 65 'tick sound
End If
End Sub
assigned。有许多方法可以根据具体需求进行定制或调整。我将用它来计算并显示我屏幕角落里一个受欢迎的Q&amp; A网站的实时统计数据......
注意,由于它包含VBA宏,因此该文件可能会启动您的病毒扫描程序(与使用VBA的任何其他非本地文件一样)。如果您担心,请不要下载,而是使用提供的信息自行构建。)
答案 2 :(得分:0)
如何将所有代码移动到&#39;定时器&#39;模块。
Dim nextTriggerTime As Date
Dim timerActive As Boolean
Public Sub StartTimer()
If timerActive = False Then
timerActive = True
Call ScheduleNextTrigger
End If
End Sub
Public Sub StopTimer()
If timerActive = True Then
timerActive = False
Application.OnTime nextTriggerTime, "Timer.OnTimer", Schedule:=False
End If
End Sub
Private Sub ScheduleNextTrigger()
If timerActive = True Then
nextTriggerTime = Now + TimeValue("00:00:01")
Application.OnTime nextTriggerTime, "Timer.OnTimer"
End If
End Sub
Public Sub OnTimer()
Call MainForm.OnTimer
Call ScheduleNextTrigger
End Sub
现在你可以从mainform调用:
call Timer.StartTimer
call Timer.StopTimer
为防止出错,请添加:
Private Sub UserForm_Terminate()
Call Timer.StopTimer
End Sub
将触发:
Public Sub OnTimer()
Debug.Print "Tick"
End Sub
答案 3 :(得分:0)
感谢user1575005 !!
使用模块中的代码来设置Timer()进程:
Dim nextTriggerTime As Date
Dim timerActive As Boolean
Public Sub StartTimer()
Debug.Print Time() & ": Start"
If timerActive = False Then
timerActive = True
Call ScheduleNextTrigger
End If
End Sub
Public Sub StopTimer()
If timerActive = True Then
timerActive = False
Application.OnTime nextTriggerTime, "OnTimer", Schedule:=False
End If
Debug.Print Time() & ": End"
End Sub
Private Sub ScheduleNextTrigger()
If timerActive = True Then
nextTriggerTime = Now + TimeValue("00:00:10")
Application.OnTime nextTriggerTime, "OnTimer"
End If
End Sub
Public Sub OnTimer()
Call bus_OnTimer
Call ScheduleNextTrigger
End Sub
Public Sub bus_OnTimer()
Debug.Print Time() & ": Tick"
Call doWhateverUwant
End Sub
Private Sub doWhateverUwant()
End Sub