KnowDotNet

Creating and Handling Events - Part 3

by Brian Davis

In Part 1 and Part 2 of this tutorial, we learned how to create custom events that can be raised from within our classes.  Although that makes for some good object-oriented code, it isn't really all that useful unless we write code to handle these events in other classes.  You have probably handled events many times before, but you may not be aware of how you handled them, because Visual Studio generated the code for you.  This quick tutorial will show you how to handle events, whether they are your own custom events or simply events in framework classes.  We will take a look at both VB.NET and C# and see how they handle events differently.

Using our
Car class example from Parts 1 and 2, we will write a class that handles the different events that we created.  Let's say that we were creating software that will watch the activities of a Car so that it can report on it.  We create a class called CarWatcher that will be used to control and monitor a Car's behavior.

In VB, we have two options for handling events - using the
Handles keyword or the AddHandler statement.  The Handles keyword allows us to designate a particular method to handle certain events within the actual declaration line of the method.  For instance, we could create a method to handle the Start event of an instance of our Car class like this:

VB
Public WithEvents Car1 As New Car
...
Public Sub Car_Start(ByVal sender As Object, ByVal e As EventArgs) Handles Car1.Start
    MessageBox.Show(
"Car1 started")
  
End If
End
Sub


Notice that using the Handles keyword requires that we use the WithEvents keyword in our declaration of Car1.  If we omit the WithEvents keyword, then the compiler will indicate a build error on the method definition line saying, "Handles clause requires a WithEvents variable."  The only requirement for the method that handles the event is that it must have the same signature (parameters) as the delegate that is defined as the event handler for the event.  In this case, the Start event uses the default EventHandler, so it requires an Object for the sender and an instance of EventArgs.  If we put the Handles clause on a method whose signature does not match the delegate, then the compiler will give us a build error saying, "Method Car_Start cannot handle Event 'Start' because they do not have the same signature."

The other way to handle events in VB.NET is to use the
AddHandler statement.  This statement identifies the event that should be handled and a pointer to the method that should handle the event.  Using our same example from before, here is how to use the AddHandler statement:

VB
Public Car1 As New Car
...

Public Sub New()
  
AddHandler Car1.Start, AddressOf Me.Car_Start
End Sub
...
Public Sub Car_Start(ByVal sender As Object, ByVal e As EventArgs)
    MessageBox.Show(
"Car1 started")
  
End If
End
Sub


Notice that the event handling method is the same; we have simply omitted the Handles clause from its definition line.  In this example, the AddHandler statement is included within a constructor, although it could be put in any method (it just won't begin handling the event until this line executes).  This method of handling events is similar to the mechanism used in C#.  When using C#, we use the += operator to add an event handler to an event.  Here is the code for hooking up our event handler in C#:

C#
public Car Car1 = new Car();
...
public CarWatcher()
{
    Car1.Start +=
new EventHandler(Car1_Start);
}
...
private void Car1_Start(object sender, EventArgs e)
{
    MessageBox.Show(
"Car1 started");
}


The syntax is pretty similar, but in this case we create an instance of the delegate EventHandler and associate it with the Start event using the += operator.  Just as in VB, we must make sure that the signature for the method Car1_Start matches the delegate.  If we do not, then we get an error form the compiler saying, "Method ___ does not match delegate ___."

Adding an event handler for the CruiseControlSet event is pretty similar.  Here is what our
CarWatcher class may look like if we need it to handle both the Start and CruiseControlSet events from our previous examples:

VB
Public Class CarWatcher

  
Public WithEvents Car1 As New Car

  
Public Sub New()
      
AddHandler Car1.CruiseControlSet, AddressOf Me.Car1_CruiseControlSet
  
End Sub

  Public Sub Car_Start(ByVal sender As Object, ByVal e As EventArgs) Handles Car1.Start
       MessageBox.Show(
"Car1 started")
  
End Sub

  Public Sub Car1_CruiseControlSet(ByVal sender As Object, ByVal e As CruiseControlSetEventArgs)
       MessageBox.Show(
"Car1 cruise control set to " & e.CruiseControlSpeed.ToString())
  
End Sub

End
Class

C#
public class CarWatcher
{
  
public Car Car1 = new Car();

  
public CarWatcher()
   {
       Car1.Start +=
new EventHandler(Car1_Start);
       Car1.CruiseControlSet +=
new CruiseControlSetEventHandler(Car1_CruiseControlSet);
   }

  
private void Car1_Start(object sender, EventArgs e)
   {
       MessageBox.Show(
"Car1 started");
   }

  
private void Car1_CruiseControlSet(object sender, CruiseControlSetEventArgs e)
   {
       MessageBox.Show(
"Car1 cruise control set to " + e.CruiseControlSpeed.ToString());
   }
}


In our event handling methods for the CruiseControlSet event, we take an instance of CruiseControlSetEventArgs as a parameter.  This will give us access to our custom property CruiseControlSpeed so that we can display to the user the speed to which the cruise control was set.

Now we have a complete example of creating and handling custom events.  This logic can be used to create simple events for notification or complex events to convey meaningful information associated with a particular event.  You will quickly find that using events is a clean and elegant approach to object-oriented programming, and it can make the process of maintaining many instances of active objects much easier.  In many cases, it can replace the age-old paradigm of polling objects for their status, eliminating the associeated overhead.  To view a demo application with complete source code showing how this example works in both VB and C# (1.1 Framework), download the demo solution here.