Не помню кто из великих сказал… И не помню что. Но в процессе разработки vba-приложений время от времени возникает необходимость использовать таймер. «Из коробки» решения, к сожалению, нет.
по два(!) раза.
Application.OnTime?
Данный метод отложенных вызовов сам по себe решает проблему, но лишь один раз. В том смысле, что таймер срабатывает лишь единожды — указать переодичность вызовов, увы, нельзя.Application.OnTime + рекурсия?
Очевидно, что в конце необходимой процедуры можно вызывать её же с помощью данного метода, но не менее очевидны и недостатки этого решения — отсутствие достаточного контроля и неудобство вызова:- не таймер вызывает действие;
- чтобы остановить цикл придётся объявлять глобальную переменную;
- добавление «костыля» в тело отдельно взятой процедуры. Если в следующий раз будет нужно вызвать по таймеру другую — то и в неё придётся переносить данный «костыль».
Evaluate в помощь?
Написать свой класс и вызывать внутри необходимую процедуру с помощью Application.Evaluate. Особо упорные в стремлении создать приемлемый метод вызова таймера доходят до этого решения и упираются в стран/ш/ный баг, отловить который достаточно сложно: udf-функция переданная в переменной будет вызываться каждый разПотому что гладилус
Способ обойти этот баг пока найден лишь один:ActiveSheet.Evaluate "0+" & nameOfProcedure
Решение
Для простоты вызова написан не класс, а метод. Достаточно указать интервал, имя вызываемой процедуры и, опционально, кол-во необходимых вызовов:StartTimer 60, "CalculateTotal()", 30 'вызов CalculateTotal() каждую минуту в течении получаса.
StopTimer 'остановка таймера
модуль
Option Explicit
Private Type Timer
interval As Long
procedure As String
times As Long
enabled As Boolean
initialized As Boolean
ticks As Long
End Type
Private Timer As Timer
Public Sub StartTimer(ByRef interval As Long, _
ByRef procedure As String, _
Optional ByRef times As Long)
With Timer
.interval = interval
.procedure = procedure
.times = times
.enabled = True
.initialized = False
.ticks = 0
End With
InvokeTimer
End Sub
Public Sub StopTimer()
Timer.enabled = False
End Sub
Private Function InvokeTimer()
If Timer.ticks > Timer.times And Not Timer.times = 0 Then StopTimer
If Not Timer.enabled Then Exit Function
If Timer.initialized Then
ActiveSheet.Evaluate "0+" & Timer.procedure
Else
Timer.initialized = True
End If
Timer.ticks = Timer.ticks + 1
Application.OnTime Now + 1 / 86400 * Timer.interval, "InvokeTimer"
End Function