Although Timer events are the best tool for
The WithEvents keyword specifies that the variable
mWidget will be used to handle an object's events. You
specify the kind of object by supplying the name of
the class from which the object will be created.
The variable mWidget is declared in the Declarations
section of Form1 because WithEvents variables must be
module-level variables. This is true regardless of the
type of module you place them in.
The variable mblnCancel will be used to cancel the
Limitations on WithEvents Variables
You should be aware of the following limitations on
the use of WithEvents variables:
A WithEvents variable cannot be a generic object
variable. That is, you cannot declare it As Object --
you must specify the class name when you declare the
You cannot declare a WithEvents variable As New. The
event source object must be explicitly created and
assigned to the WithEvents variable.
You cannot declare WithEvents variables in a
standard module. You can declare them only in class
modules, form modules, and other modules that define
You cannot create arrays of WithEvents variables.
Writing Code to Handle an Event
As soon as you declare a variable WithEvents, the
variable name appears in the left-hand drop down of
the module's code window. When you select mWidget, the
Widget class's events will appear in the right-hand
drop down, as shown in Figure 9.9.
Figure 9.9 An event associated with a WithEvents
Selecting an event will display the corresponding
event procedure, with the prefix mWidget_. All the
event procedures associated with a WithEvents variable
will have the variable name as a prefix. Add the
following code to the mWidget_PercentDone event
Private Sub mWidget_PercentDone(ByVal Percent As _
Single, Cancel As Boolean)
lblPercentDone.Caption = CInt(100 * Percent) & "%"
If mblnCancel Then Cancel = True
Whenever the PercentDone event is raised, the event
procedure displays the percent complete in a Label
control. The DoEvents statement allows the label to
repaint, and also gives the user the opportunity to
click the Cancel button. Add the following code for
the Click event of the button whose caption is Cancel.
Private Sub Command2_Click()
mblnCancel = True
If the user clicks the Cancel button while LongTask is
running, the Command2_Click event will be executed as
soon as the DoEvents statement allows event processing
to occur. The module-level variable mblnCancel is set
to True, and the mWidget_PercentDone event then tests
it and sets the ByRef Cancel argument to True.
Connecting a WithEvents Variable to an Object
Form1 is all set up to handle a Widget object's
events. All that remains is to find a Widget
When you declare a variable WithEvents at design time,
there is no object associated with it. A WithEvents
variable is just like any other object variable. You
have to create an object and assign a reference to the
object to the WithEvents variable. Add the following
code to the Form_Load event procedure to create the
Private Sub Form_Load()
Set mWidget = New Widget
When the code above is executed, Visual Basic creates
a Widget and connects its events to the event
procedures associated with mWidget. From that point
on, whenever the Widget raises its PercentDone event,
the mWidget_PercentDone event procedure will be
To call the LongTask method, add the following code to
the Click event of the button whose caption is Start
' Start Task button.
Private Sub Command1_Click()
mblnCancel = False
lblPercentDone.Caption = "0%"
Call mWidget.LongTask(14.4, 0.66)
If Not mblnCancel Then lblPercentDone.Caption = 100
Before the LongTask method is called, the label that
displays the percent complete must be initialized, and
the module-level Boolean flag for canceling the method
must be set to False.
LongTask is called with a task duration of 14.4
seconds. The PercentDone event is to be raised once
every two-thirds of a second. Each time the event is
raised, the mWidget_PercentDone event procedure will
When LongTask is done, mblnCancel is tested to see if
LongTask ended normally, or if it stopped because
mblnCancel was set to True. The percent complete is
updated only for the former case.
Running the Program
Press F5 to put the project in Run mode. Click the
Start Task button. Each time the PercentDone event is
raised, the label is updated with the percentage of
the task that's complete. Click the Cancel button to
stop the task. Notice that the appearance of the
Cancel button doesn't change immediately when you
click it. The Click event can't happen until the
DoEvents statement allows event processing.
You may find it instructive to run the program with
F8, and step through the code a line at a time. You
can clearly see how execution enters LongTask, and
then re-enters Form1 briefly each time the PercentDone
event is raised.
What would happen if, while execution was back in
Form1's code, the LongTask method was called again?
Confusion, chaos, and eventually (if it happened every
time the event was raised) a stack overflow.
Handling Events for a Different Widget
You can cause the variable mWidget to handle events
for a different Widget object by assigning a reference
to the new Widget to mWidget. In fact, you can make
the code in Command1 do this every time you click the
button, by adding two lines of code:
Set mWidget = New Widget '<- New line.
Call mWidget.LongTask(14.4, 0.66)
Set mWidget = Nothing '<- New line.
The code above creates a new Widget each time the
button is pressed. As soon as the LongTask method
completes, the reference to the Widget is released by
setting mWidget to Nothing, and the Widget is
A WithEvents variable can only contain one object
reference at a time, so if you assign a different
Widget object to mWidget, the previous Widget object's
events will no longer be handled. If mWidget is the
only object variable containing a reference to the old
Widget, the object will be destroyed.
Note You can declare as many WithEvents variables as
you need, but arrays of WithEvents variables are not
Terminating Event Handling for a WithEvents Variable
As long as there is a Widget object assigned to the
variable mWidget, the event procedures associated with
mWidget will be called whenever the Widget raises an
event. To terminate event handling, you can set
mWidget to Nothing, as shown in the following code
' Terminate event handling for mWidget.
Set mWidget = Nothing
When a WithEvents variable is set to Nothing, Visual
Basic disconnects the object's events from the event
procedures associated with the variable.
Important A WithEvents variable contains an object
reference, just like any other object variable. This
object reference counts toward keeping the object
alive. When you are setting all references to an
object to Nothing in order to destroy it, don't forget
the variables you declared WithEvents.
For More Information The event procedures associated
with WithEvents variables look a lot like event
procedures for controls on forms. "Comparing
WithEvents to Control Events on Forms" discusses the
similarities and differences.
44. Which datatypes in VB are capable of accepting
Find more freelance jobs