Introduction to Event Handling
One of the most useful capabilities of the OOP Languages is their inbuilt ability to be aware of a large number of events like MouseOver, MouseClick, and so on so that we can write codes to react to any event that we are interested. This is made possible by the rich set of classes that have been built in the .NET Framework
The events handling is very simple as we have seen in the previous example where buttonClicked event is handled by the following code:
Private Sub btnOk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOk.Click
The Handles keyword is used to tell the program to look for the specific event. The sub or the function that handles the event will be called immediately when the event occurs. This is a simple implementation. In some cases you may have to raise an application event which will be caught by the event handler. Please see the following code fragment to understand this.
Public Event TimeExpired(ByVal Status As String)
RaiseEvent TimeExpired("Your time has run out")
The first line declares the event Time expired and the second statement raises the event. In order that this code works you need to make a reference to the System namespace. The Event statement should be located outside any procedure or Sub and at the class level. The RaiseEvent statement should be available inside some procedure in the application. Events must be raised within the scope of the class or module or structure where they are declared. Thus you cannot raise an event declared in the base class from within a derived class. The object that raised the event is the sender or the source of the event. Some of the examples of these kinds of objects are forms, controls etc.
An event handler can be any procedure that is called when the corresponding event occurs. You cannot use a function as an event handler as it would necessitate the function to return a value to the source object. The event handler cannot be made use of unless you first associate the procedure with the event by using the keyword Handles or AddHandler statement.
While handles clause is used with design time activity AddHandler and RemoveHandler statements are more flexible and they allow you to dynamically connect or disconnect the events with one or more event handlers at run time. You are also relieved from adding one more line of code using withEvents.
Let us see an example of event handling using Handles clause.
Dim withEvents AnEvent as new EventRaised()
Sub EventEgs()
AnEvent.RaiseEvents()
End Sub
Sub AnEvent_EventHandler() Handles AnEvent.EventOne, AnEvent.EventTwo
MsgBox(“Received Event”)
End Sub
Class EventRaised
Public Event EventOne()
Public Event EventTwo()
Sub RaiseEvents()
RaiseEvent EventOne()
RaiseEvent EventTwo()
End Sub
End Class
There are some limitations in using this kind of solution as listed below:
• You cannot use a WithEvents variable as a object variable. That is, you cannot declare it as Object — you must specify the class name when you declare the variable.
• You cannot use WithEvents to declaratively handle shared events, since they are not tied to an instance that can be assigned to a WithEvents variable. Similarily, you cannot use WithEvents or Handles to handle events from a Structure. In both cases, you can use the AddHandler statement to handle those events.
• You cannot create arrays of WithEvents variables.
• WithEvents variables allow a single event handler to handle one or more kind of event, or one or more event handlers to handle the same kind of event.
The other method is employing the AddHandler and RemoveHandler clause. We shall see this in the next section
.
.
Handling Events by attaching a delegate
A delegate is kind of OO function pointer that permits a function to be invoked indirectly by making reference to the function. They are employed to attach to event handlers and pass a procedure from on procedure to another. However delegates can also be used for other tasks not related to events like free threading or procedures that need to call different versions of functions at compile time. An illustration on the use of delegates is given by the following codes:
The output is given below:
5 is positive; use the sign "+".
-3 is negative; use the sign "-".
0 is zero; use the sign "".
Delegates are reference type based on the class System.Delegate and are capable of referencing both share methods and instance methods. Using Delegates is particularly advantageous in situations where you need an intermediary between a calling procedure and the procedure being called. In some cases it may be required that when an object raises an event, you may want to raise different event handlers under different circumstances.
In these situations the object that raised the event cannot know in advance, which EventHandler will handle the specific event. These situations are handled well by use of delegates. While creating your own delegates is allowed, Visual Basic creates the delegates in most cases and also manages the details. Thus the Event statement implicitly defines a delegate class which is given a name
It has the same signature as the event and also creates the AddressOf statement implicity. Event Delegates are multicast, in other words they can keep references to more than one event handling method. A delegate serves as an event dispatcher for the class which had raised the event by maintaining a list of registered event handlers for the event.
The AddHandler and Removehandler statements allow you to start and stop event handling at any time during the program execution. Unlike the keyword Hanles which specifies the event handling procedure at design time, the AddHandler statement connects the procedures to events at run time. Addhandler statement invokes the event’s AddHandler accessor for custom events. Let us see and illustration for this:
Sub TestEvents()
Dim Obj As New Class1()
‘ Associate an event handler with an event.
AddHandler Obj.Ev_Event, AddressOf EventHandler
Obj.CauseSomeEvent() ‘ Ask the object to raise an event.
End Sub
Sub EventHandler()
‘ This procedure handles events raised by the object Obj.
MsgBox("EventHandler caught event.") ‘ Handle the event.
End Sub
Public Class Class1
Public Event Ev_Event() ‘ Declare an event.
Sub CauseSomeEvent()
RaiseEvent Ev_Event() ‘ Raise an event.
End Sub
End Class
The next illustration demostrates declaring and consuming events.This code defines the behaviour of Laughing Doll that takes user input to start and stop laugh and to pause laughing. I this case the event is raised on one class and the hadling of the event is done in another class.
Handling Events by overriding protected method of base class
When you inherit from a control or a component, you create a new control or component that incorporates all of the functionality of its base class. Any event handlers that are defined by the base class will be included in the inherited component.
The following example shows a typical event handler:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button1.Click
Static Counter As Integer = 0
Counter += 1
MessageBox.Show(" This button has been clicked " & _
Counter.ToString() & " time(s).")
End Sub
In order to allow this method to be overridden in an inheriting class, you must add the Overridable keyword and change the access level to Protected, Protected Friend, or Public. The following example shows an event handler that can be overridden:
Protected Overridable Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Static Counter As Integer = 0
Counter += 1
MessageBox.Show(" This button has been clicked " & _
Counter.ToString() & " time(s).")
End Sub
Overriding an inherited event handler is the same as overriding any other kind of inherited method, with one important difference: When you override an inherited event handler, you have to remove the Handles clause.
To override a method in an inherited component
• Add the Overrides keyword to your method declaration.
Note You should not add a Handles clause to the method. The event handler is already associated with the event in the base class, and this association is passed on to the inheriting class. In other words, the method will be executed when the event is fired and does not require an additional Handles clause.
The following example shows how to override the event handler from the previous example:
Protected Overrides Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Static Counter As Integer = 0
Counter += 1
MessageBox.Show(" This inherited button has been clicked " & _
Counter.ToString() & " times.")
End Sub
The Handles clause is no longer associated with the method. This is not an oversight, but rather an important part of how events are handled in the .NET Framework. The event handler is already associated with the event in the base class, and this association is passed on to the inheriting class. In other words, the method will be executed when the event is fired and does not require an additional Handles clause. Adding an additional Handles clause, as shown below, will create an additional association with the event, and will cause the method to be executed twice per event.
‘ INCORRECT
Protected Overrides Sub Button1_Click(ByVal sender As System.Object, _
ByVal e as System.EventArgs) Handles Button1.Click
Static Counter as Integer = 0
‘ This variable will be incremented twice each time the button is clicked.
Counter += 1
‘ The message box will be displayed twice for every time the button is clicked, and will display ‘inaccurate information.
MessageBox.Show (" This inherited button has been clicked " & _
Counter.ToString() & " times.")
End Sub
This problem caused by overriding event handlers might not be intuitive and can lead to bugs that are difficult to isolate. Setting the appropriate associations with your event handlers is very important. Use caution, and be aware of event associations that are already in place.