wyj7485 发表于 2005-5-12 15:17:00

在VBA里实现定时事件触发器Timer

<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>众所周知,在<SPAN lang=EN-US>VB的标准控件里含有一个可以定时触发事件的Timer控件。可是,在VBA里却没有提供这一控件。而事实上,我们编写的很多程序都经常要用到后台运行或是定时触发某一事件的功能,怎么办呢?其实很简单,我们可以自己编写一个Timer控件。</SPAN><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>在<SPAN lang=EN-US>VBA的工程里添加一个类模块,并将它命名为Timer,然后将下面的代码写入到这个模块中。</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Option Explicit<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>'TimerEnd变量用于控制定时事件的状态,<SPAN lang=EN-US>False表示启动定时事件,True表示停止定时事件</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Private TimerEnd As Boolean<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>'Timer事件是一个每隔固定秒数自动触发的事件,该秒数由<SPAN lang=EN-US>StartTimer方法的Interval参数控制</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Public Event Timer()<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>'StartTimer方法用于启动定时事件,该方法的<SPAN lang=EN-US>Interval参数用于设置定时事件的间隔秒数(1-32767之间的整数)</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Public Sub StartTimer(ByVal Interval As Integer)<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       Dim Starttime As Single<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       '</SPAN>用<SPAN lang=EN-US>Timer函数返回从午夜开始到现在经过的秒数</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       Starttime=Timer<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       TimerEnd=False<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       If Interval&lt;1 Then<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                                                       MsgBox "Interval</SPAN>参数的值必须是<SPAN lang=EN-US>1-32767之间的整数",,"错误"<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                                                       Exit Sub<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       End If<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       '</SPAN>用循环结构重复触发<SPAN lang=EN-US>Timer事件</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       Do<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                                                       If TimerEnd Then Exit Do<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                                                       If Timer&gt;=Starttime+Interval Then<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                                                                                       Starttime=Timer<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                                                                                       RaiseEvent Timer<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                                                       End If<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                                                       '</SPAN>在循环过程中,用<SPAN lang=EN-US>DoEvents函数将控制权转让给操作系统,这样可以实现后台运行定时事件的效果。</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                                                       DoEvents<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       Loop<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>End Sub<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>'EndTimer方法用于停止定时事件<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Public Sub EndTimer()<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       TimerEnd=True<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>End Sub<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Private Sub Class_Terminate()<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       TimerEnd=True<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>End Sub<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Private Sub Class_Initialize()<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       TimerEnd=False<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>End Sub<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>这样,便做好了一个可以定时触发事件的<SPAN lang=EN-US>Timer类。下面只需要在相应的模块里添加这个类的实例,便可以像使用VB中的Timer控件一样使用了。</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>下面是一个具体的<SPAN lang=EN-US>VBA程序例子。该例运行后可以通过单击窗体让窗体每隔1秒向右移动一段距离(而且不会影响窗体上其它控件的使用)。</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>首先,在<SPAN lang=EN-US>VBA工程里添加一个用户窗体UserForm1,然后在其代码编辑器里写入下面的代码:</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Option Explicit<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>'声明<SPAN lang=EN-US>MyTimer为带有事件的模块级Timer对象变量</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Private WithEvents MyTimer As Timer<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>'在<SPAN lang=EN-US>UserForm的初始化事件里生成Timer的一个实例</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Private Sub UserForm_Initialize()<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       Set MyTimer=New Timer<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>End Sub<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>'在<SPAN lang=EN-US>UserForm的click事件里启动定时触发器,设置间隔秒数为1<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Private Sub UserForm_Click()<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       MyTimer.StartTimer 1<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>End Sub<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>'在<SPAN lang=EN-US>UserForm的结束事件里关闭定时触发器</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Private Sub UserForm_Terminate()<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       MyTimer.EndTimer '</SPAN>一定要在程序结束前调用<SPAN lang=EN-US>EndTimer方法</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Set MyTimer=Nothing<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>End Sub<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>'在<SPAN lang=EN-US>Timer事件里书写需要定时重复运行的代码</SPAN><o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>Private Sub MyTimer_Timer()<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><SPAN style="mso-spacerun: yes">                       UserForm1.Left=UserForm1.Left+5<o:p></o:p></SPAN>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>End Sub<o:p></o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><o:p>        </o:p>


<P class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left>其实,如果只是编写一段小程序,那也不一定非要制作一个<SPAN lang=EN-US>Timer类,只要将需要定时重复运行的代码放入到一个循环中即可。另外,在循环中可以利用Timer函数控制间隔秒数,而通常也必须在循环中添加DoEvents函数,以便让该段代码在后台运行。最后,千万不要忘记书写循环的跳出条件,否则,这段后台执行的代码将会一直驻留内存,即使窗体被卸载。</SPAN><o:p></o:p>

wyj7485 发表于 2005-5-12 15:38:00

范例程序:


evaporated 发表于 2005-5-12 15:51:00

看了以后很有启发.


原来有人贴过用api来实现Timer的.经实践很不稳定.


有关窗体和事件的操作会导致致命错误.

xgngg 发表于 2005-10-10 17:03:00

<P>楼主能不能用控制直线,让他水平移动,而不是让UserForm移动呢?</P>

wyj7485 发表于 2005-10-11 14:06:00

当然可以!选择一条直线,设定两点,直线的Move方法实现.

nonsmall 发表于 2009-1-19 11:02:00

<p>CPU 占用还是很高啊</p><p>有办法降低CPU占用吗</p>

geabus 发表于 2009-1-21 10:35:00

<p>占用率肯定高啦,一直在循环调用时间查询函数</p><p>基本原理的问题,与系统底层连接不够,最好还是调用系统内部的相关函数,否则代码低效</p>
页: [1]
查看完整版本: 在VBA里实现定时事件触发器Timer