每隔一定时间运行代码的VBA宏定时器样式,例如每120秒运行一次。

41

我需要每隔120秒运行一段代码。我在寻找一种在VBA中简单实现此操作的方法。我知道可以从Auto_Open事件获取计时器值以避免使用魔法数字,但我不太清楚如何启动一个定时器以使某些内容每120秒运行一次。

如果可能的话,我不想使用带有延迟的无限循环。


编辑:

基于提供的答案的跨帖链接是:Excel VBA Application.OnTime. 我认为使用它是个坏主意... 思路如何?

6个回答

68

当工作簿首次打开时,请执行此代码:

alertTime = Now + TimeValue("00:02:00")
Application.OnTime alertTime, "EventMacro"

那么只需要在工作簿中创建一个名为“EventMacro”的宏,即可重复执行该操作。

Public Sub EventMacro()
    '... Execute your actions here'
    alertTime = Now + TimeValue("00:02:00")
    Application.OnTime alertTime, "EventMacro"
End Sub

1
谢谢这段代码 :) 我需要尽快在工作中使用它,而这是我第一个去的页面 :) 耶! - Pavle Stojanovic
你好,当Excel表格被最小化或另一个Excel表格被打开时,会出现错误吗? - Arun Joe Cheriyan
@ArunCheriyan 我看不出为什么会出错。如果有错误的话,请尝试提出一个新问题并引用这个问题。 - Alain
1
这不是无限递归吗?在几千/百万/十亿个函数调用后,它不会导致堆栈溢出吗? - Berry Tsakala
3
Application.OnTime 调用子程序时会在新的堆栈顶部“从头开始”运行。这不像普通的递归方法调用。由于每个调用都有自己的堆栈,所以不可能出现堆栈溢出。 - Alain

23

是的,您可以使用Application.OnTime实现此操作,然后将其放入循环中。它有点像闹钟,您不断点击贪睡按钮,以便在您希望再次响铃时响起。以下示例每三秒更新单元格A1中的时间。

Dim TimerActive As Boolean
Sub StartTimer()
    Start_Timer
End Sub

Private Sub Start_Timer()
    TimerActive = True
    Application.OnTime Now() + TimeValue("00:00:03"), "Timer"
End Sub

Private Sub Stop_Timer()
    TimerActive = False
End Sub

Private Sub Timer()
    If TimerActive Then
        ActiveSheet.Cells(1, 1).Value = Time
        Application.OnTime Now() + TimeValue("00:00:03"), "Timer"
    End If
End Sub
你可以将StartTimer过程放在你的Auto_Open事件中,并更改Timer过程中所执行的操作(现在它只是用ActiveSheet.Cells(1, 1).Value = Time更新A1中的时间)。 注意:你需要将代码(除了StartTimer)放在一个模块中,而不是工作表模块中。如果你将其放在工作表模块中,则需要稍加修改代码。

子程序计时器看起来像是一个无限递归函数; 除非OnTime的句柄被保留在子程序之外,否则子程序将无限引用其调用者。 - Berry Tsakala

9
在工作簿事件中:
Private Sub Workbook_Open()
    RunEveryTwoMinutes
End Sub

在一个模块中:

Sub RunEveryTwoMinutes()
    //Add code here for whatever you want to happen
    Application.OnTime Now + TimeValue("00:02:00"), "RunEveryTwoMinutes"
End Sub

如果你只想在工作簿打开后执行第一段代码,那么只需在Workbook_Open事件中添加2分钟的延迟即可。


1
谢谢,我找到了错误的原因。我没有把宏放在模块中。现在我的代码也可以正常工作了。感谢您的解释。 :) - Mentos

2

(这是从MS Access帮助文件中改编的,我相信XL也有类似的内容。)基本上,TimerInterval是一个表单级别的属性。一旦设置,使用子表单_Form_Timer来执行您打算执行的操作。

Sub Form_Load()
    Me.TimerInterval = 1000 '1000 = 1 second
End Sub

Sub Form_Timer()
    'Do Stuff
End Sub

谢谢你的建议,但是我不想把这个做成表单的一部分。 - FinancialRadDeveloper
1
为了以后的参考,不幸的是,Excel没有TimerInterval属性。据我所知,只有MS Access具有该表单属性,而使用VBA的其他MS产品都没有相应的属性。 - Eliza Bennet

2

我发现使用OnTime有些痛苦,尤其是在以下情况下:

  1. 你正在编程时,窗口焦点每次事件触发时都会被打断。
  2. 你打开了多个工作簿,关闭了应该使用计时器的那个工作簿,但它仍然会触发并重新打开工作簿(如果你忘记正确终止事件)。

Chip Pearson的这篇文章非常启发人。我现在更喜欢使用Windows定时器,而不是OnTime


根据CPearson的文章,您只需要停止计时器(取消挂起的OnTime事件)。这将消除您所描述的“工作簿关闭时触发事件”的问题。此外,该文章警告使用Windows计时器和更改单元格值可能会导致Excel崩溃。 OnTime需要更少的代码,只需正确处理即可。 - like2think

0

我的解决方案:

Option Explicit
Public datHora As Date

Function Cronometro(action As Integer) As Integer 
'This return the seconds between two >calls
Cronometro = 0
  If action = 1 Then 'Start
    datHora = Now
  End If
  If action = 2 Then 'Time until that moment
    Cronometro = DateDiff("s", datHora, Now)
  End If
End Function

如何使用?非常简单...

dummy= Cronometro(1) ' This starts the timer

seconds= Cronometro(2) ' This returns the seconds between the first call and this one

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接