No Timer Control


Visual Basic’s Timer control is one of those controls that ought not to be a control. You always have to put it on a form, even if it doesn’t belong on one. If you want a Timer on a program or component that has no user interface, tough luck. You’ll have to create an invisible Form just to hold the Timer. If you want to encapsulate a timer within another class, you can’t do it without weird hacks to communicate between your class and an invisible form.


Yet the Timer has no need for the features offered by controls. It has no visible surface. Its properties are often (if not usually) set at run time rather than design time, so the design-time property list isn’t much use. The only reason it’s still a control is that a control was the only way to generate events in previous versions of Visual Basic. But no more. Now any class can raise events. Unfortunately, they didn’t add a noncontrol Timer to the product—at least not in a completed form. There’s actually a timer class named XTimer in one of the sample projects provided with Visual Basic.


I hate to be one of those programmers who can’t use anything that’s “not invented here.” I even thought about throwing my CTimer class away and using the XTimer class provided in the sample. But I like this interface of mine a little better, and it seemed kind of cheap to steal a sample and put it in the VBCore component (although I don’t think the XTimer author, Glenn Hackney, would have minded).


Anyway, I’m going to use CTimer, but I’m not going to talk about the implementation except to say that it calls the SetTimer API function to create a timer object, and waits for Windows to call back to TimerProc at the designated interval. This is easier said than done, but it’s just a typical callback problem. We handled worse in the subclassing example at the end of Chapter 6. You can study the code in TIMER.CLS and TIMER.BAS.


For now, the only thing of interest is how you create and use a CTimer ob­ject. First you have to declare the object at the form level using the WithEvents
syntax:

Private WithEvents timerAnimate As CTimer

Next you must create a CTimer object in an initialization event such as Form_Load:

    Set timerAnimate = New CTimer

A new timer starts out with an interval of zero, so its event won’t be called until you set a positive interval. The Fun ’n Games program does this in the SetTimer procedure:

Private Sub SetTimer(eatAnimateA As Integer)
eatAnimate = eatAnimateA
Select Case eatAnimate
Case eatNone
timerAnimate.Interval = 0
‘ Hide XPictureGlass object
pgControl.Visible = False
cmdAnimate.Caption = “&Animate Picture”
cmdAnimateCtl.Caption = “Animate &Control”
‘ Remove active CPictureGlass object from memory
Set pgPicture = Nothing
Case eatPicture, eatControl
timerAnimate.Interval = 10
Case eatCardBacks
timerAnimate.Interval = 100
End Select
End Sub

The CTimer class differs from the Timer control in that it doesn’t have a separate Enabled property. Setting the CTimer Interval property to 0 is equivalent to setting the Enabled property of Timer to False.


Finally you put your periodic code in the CTimer ThatTime event:

Private Sub timerAnimate_ThatTime()
Select Case eatAnimate
Case eatNone
Exit Sub
Case eatCardBacks
AnimateBacks
Case eatPicture
AnimatePicture
Case eatControl
AnimateControl
End Select
End Sub

Debugging the CTimer class in source code can be an adventure. If you don’t value your time, try it with FUNNGAMEDEB.VBG. This project group points to SUBTIMER.VBP rather than SUBTIMER.DLL. When debugging source, you can’t set breakpoints in the ThatTime event procedure or click the End button while the timer is active. If you try it, you’ll see some behavior that will be unpredictable except for its outcome—the lockup and eventual death of Visual Basic. Those timer events just keep coming even when you’re stopped in the debugger, and there’s no way Visual Basic can break in multiple events at the same time. The CTimer class works fine in the debugger as long as it’s compiled. Try it with FUNNGAME.VBG or FUNNGAME.VBP.