Walkthrough: Handling Events (Visual Basic)
This is the second of two topics that demonstrate how to work with events. The first topic, Walkthrough: Declaring and Raising Events, shows how to declare and raise events. This section uses the form and class from that walkthrough to show how to handle events when they take place.
The Widget
class example uses traditional event-handling statements. Visual Basic provides other techniques for working with events. As an exercise, you can modify this example to use the AddHandler
and Handles
statements.
To handle the PercentDone event of the Widget class
Place the following code in
Form1
:Private WithEvents mWidget As Widget Private mblnCancel As Boolean
The
WithEvents
keyword specifies that the variablemWidget
is 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 inForm1
becauseWithEvents
variables must be class-level. This is true regardless of the type of class you place them in.The variable
mblnCancel
is used to cancel theLongTask
method.
Writing Code to Handle an Event
As soon as you declare a variable using WithEvents
, the variable name appears in the left drop-down list of the class's Code Editor. When you select mWidget
, the Widget
class's events appear in the right drop-down list. Selecting an event displays the corresponding event procedure, with the prefix mWidget
and an underscore. All the event procedures associated with a WithEvents
variable are given the variable name as a prefix.
To handle an event
Select
mWidget
from the left drop-down list in the Code Editor.Select the
PercentDone
event from the right drop-down list. The Code Editor opens themWidget_PercentDone
event procedure.Note
The Code Editor is useful, but not required, for inserting new event handlers. In this walkthrough, it is more direct to just copy the event handlers directly into your code.
Add the following code to the
mWidget_PercentDone
event handler:Private Sub mWidget_PercentDone( ByVal Percent As Single, ByRef Cancel As Boolean ) Handles mWidget.PercentDone lblPercentDone.Text = CInt(100 * Percent) & "%" My.Application.DoEvents() If mblnCancel Then Cancel = True End Sub
Whenever the
PercentDone
event is raised, the event procedure displays the percent complete in aLabel
control. TheDoEvents
method allows the label to repaint, and also gives the user the opportunity to click the Cancel button.Add the following code for the
Button2_Click
event handler:Private Sub Button2_Click( ByVal sender As Object, ByVal e As System.EventArgs ) Handles Button2.Click mblnCancel = True End Sub
If the user clicks the Cancel button while LongTask
is running, the Button2_Click
event is executed as soon as the DoEvents
statement allows event processing to occur. The class-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 now set up to handle a Widget
object's events. All that remains is to find a Widget
somewhere.
When you declare a variable WithEvents
at design time, no object is associated with it. A WithEvents
variable is just like any other object variable. You have to create an object and assign a reference to it with the WithEvents
variable.
To create an object and assign a reference to it
Select (Form1 Events) from the left drop-down list in the Code Editor.
Select the
Load
event from the right drop-down list. The Code Editor opens theForm1_Load
event procedure.Add the following code for the
Form1_Load
event procedure to create theWidget
:Private Sub Form1_Load( ByVal sender As System.Object, ByVal e As System.EventArgs ) Handles MyBase.Load mWidget = New Widget End Sub
When this code executes, Visual Basic creates a Widget
object 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 is executed.
To call the LongTask method
Add the following code to the
Button1_Click
event handler:Private Sub Button1_Click( ByVal sender As Object, ByVal e As System.EventArgs ) Handles Button1.Click mblnCancel = False lblPercentDone.Text = "0%" lblPercentDone.Refresh() mWidget.LongTask(12.2, 0.33) If Not mblnCancel Then lblPercentDone.Text = CStr(100) & "%" End Sub
Before the LongTask
method is called, the label that displays the percent complete must be initialized, and the class-level Boolean
flag for canceling the method must be set to False
.
LongTask
is called with a task duration of 12.2 seconds. The PercentDone
event is raised once every one-third of a second. Each time the event is raised, the mWidget_PercentDone
event procedure is executed.
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 in the former case.
To run 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 is complete.Click the Cancel button to stop the task. Notice that the appearance of the Cancel button does not change immediately when you click it. The
Click
event cannot happen until theMy.Application.DoEvents
statement allows event processing.Note
The
My.Application.DoEvents
method does not process events in exactly the same way as the form does. For example, in this walkthrough, you must click the Cancel button twice. To allow the form to handle the events directly, you can use multithreading. For more information, see Managed Threading.
You may find it instructive to run the program with F11 and step through the code a line at a time. You can clearly see how execution enters LongTask
, and then briefly re-enters Form1
each time the PercentDone
event is raised.
What would happen if, while execution was back in the code of Form1
, the LongTask
method were called again? At worst, a stack overflow might occur if LongTask
were called every time the event was raised.
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 Button1_Click
do this every time you click the button.
To handle events for a different widget
Add the following line of code to the
Button1_Click
procedure, immediately preceding the line that readsmWidget.LongTask(12.2, 0.33)
:mWidget = New Widget ' Create a new Widget object.
The code above creates a new Widget
each time the button is clicked. As soon as the LongTask
method completes, the reference to the Widget
is released, and the Widget
is destroyed.
A WithEvents
variable can contain only 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 is destroyed. If you want to handle events from several Widget
objects, use the AddHandler
statement to process events from each object separately.
Note
You can declare as many WithEvents
variables as you need, but arrays of WithEvents
variables are not supported.