Publish and Subscribe

In .NET, controls publish a set of events to which other classes can subscribe. When the publishing class raises an event, all the subscribed classes are notified.

Tip

This design is similar to the Publish/Subscribe (Observer) Pattern described in the seminal work Design Patterns by Gamma, et al. (Addison Wesley). Gamma describes the intent of this pattern, "Define a one to many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically."

With this event mechanism, the control says, "Here are things I can notify you about," and other classes might sign up, saying, "Yes, let me know when that happens." For example, a button might notify any number of interested observers when it is clicked. The button is called the publisher because the button publishes the Click event, and the other classes are the subscribers because they subscribe to the Click event.

Events and Delegates

Events are implemented with delegates. The publishing class defines a delegate that encapsulates a method that the subscribing classes implement. When the event is raised, the subscribing classes' methods (the event handlers) are invoked through the delegate.

Note

A delegate type defines the signature of methods that can be encapsulated by instances of that delegate type. A delegate can be marked as an event to restrict access to that delegate for use as an event handler.

For more information on the relationship between delegates and events, please see either Programming VB.NET or Programming C#, by Jesse Liberty (O'Reilly).

When you instantiate a delegate, pass in the name of the method the delegate will encapsulate. Register the event using the += operator (in C#) or the Handles and WithEvents keywords in VB.NET. (VB.NET can alternatively use the AddHandler keyword.) You may register more than one delegate with an event; when the event is raised, each of the delegated methods will be notified.

For example, when declaring an event, the .NET documentation describes the event and delegate used in the Button control's Click event:

image with no caption

public delegate void EventHandler(object sender, EventArgs e);
public event EventHandler Click;

EventHandler is defined to be a delegate for a method that returns void and takes two arguments: one of type Object and the other of type EventArgs. The Click event is implemented with the EventHandler delegate.

Event Arguments

By convention, event handlers in the .NET Framework are designated in C# to return void, are implemented as a sub in VB.NET, and take two parameters. The first parameter is the "source" of the event: the publishing object. The second parameter is an object derived from EventArgs.

EventArgs is the base class for all event data. Other than its constructor, the EventArgs class inherits all its methods only from Object, though it does add a public static field empty, which represents an event with no state (to allow for the efficient use of events with no state).

If an event has no interesting data to pass, then the passed event argument will be of type EventArgs, which has no public properties, being essentially a placeholder. However, if there is interesting data, such as the location of a mouseclick or which key was pressed, then the event argument will be of a type derived from EventArgs, and it will have properties for the data being passed.

The general prototype for an event handler is as follows:

image with no caption

private void Handler (object sender, EventArgs e)

image with no caption

Private Sub Handler (ByVal sender As Object, ByVal e As EventArgs)

Note

By convention, the name of the object argument is sender and the name of the EventArgs argument is e.

The ByVal keyword in the VB.NET version indicates that the arguments are passed by value, rather than by reference (ByRef). If neither ByVal nor ByRef is included, the default behavior in VB.NET is by value, so the use of the keyword here is redundant. However, using it explicitly serves as a form of documentation, and since Visual Studio .NET explicitly includes the byVal keyword, you will often see it included in event handlers.

While technically you pass by value, the object passed is itself a reference. A copy of the reference is made, but it refers to the original object, and changes made within the method will affect the original object through that copy of the reference. This book refers to passing a reference by value as "pass by reference."

Some events use the base class EventArgs, but EventArgs objects contain no useful additional information about the event. The controls that do require their event handlers to know additional information about the event will pass in an object of a type derived from EventArgs.

For example, the TreeView AfterCollapse event handler receives an argument of type TreeViewEventArgs, derived from EventArgs. TreeViewEventArgs has the properties Action and Node, each of which has values pertaining to the actual event. The specifics of the event argument for each control are detailed when that control is discussed later in this book.

Control Events

Every form and control used in Windows Forms derives from System.Windows.Forms.Control, so they inherit all of the more than 50 public events contained by the Control object. Some of the most commonly used Control events are listed in Table 4-1 through Table 4-3. For each (public) event, a protected method handles the event.

Many controls support other events, in addition to the inherited events. For example, the TreeView class exposes several events for handling node expansion and collapse. Control-specific events are detailed in the relevant sections.

Table 4-1. Common Control events

Event

Event argument

Description

Click

EventArgs

Raised when a control is clicked by the mouse.

ControlAdded

ControlEventArgs

Raised when a new control is added to Control.ControlCollection.

ControlRemoved

ControlEventArgs

Raised when a control is removed from Control.ControlCollection.

DockChanged

EventArgs

Raised if the Dock property— i.e., which edge of the parent container the control is docked to—is changed, either by user interaction or program control.

DoubleClick

EventArgs

Raised when a control is double-clicked. If a control has both a Click and DoubleClick event handler, the DoubleClick will be preempted by the Click event.

Enter

EventArgs

Raised when a control receives focus. Suppressed for Form objects. For nested controls, cascades up and down the container hierarchy.

Layout

LayoutEventArgs

Raised when any change occurs that affects the layout of the control (e.g., the control is resized or child controls are added or removed).

Leave

EventArgs

Raised when focus leaves the control.

Move

EventArgs

Raised when a control is moved.

Paint

PaintEventArgs

Raised when a control is redrawn.

ParentChanged

EventArgs

Raised when the parent container of a control changes.

Resize

EventArgs

Raised when a control is resized. Generally preferable to use the Layout event.

SizeChanged

EventArgs

Raised when the Size property is changed, either by user interaction or programmatic control. Generally preferable to use the Layout event.

TextChanged

EventArgs

Raised when the Text property changes, either by user interaction or programmatic control.

Validating

CancelEventArgs

Raised when a control is validating. If CancelEventArgs Cancel property set true, then all subsequent focus events are suppressed.

Suppressed if Control.CausesValidation property set false.

Validated

EventArgs

Raised when a control completes validation.

Suppressed if the CancelEventArgs.Cancel property passed to the Validating event is set true.

Suppressed if Control.CausesValidation property set false.

Note

Although all Forms controls inherit from System.Windows.Forms.Control, not all controls necessarily expose all the events contained in Control. For example, the Windows user interface does not allow you to double-click a button control. Yet the Button class inherits from Control via the ButtonBase class. In fact, even though Visual Studio .NET does not expose a DoubleClick event for a Button control, you can add the event and hook it up manually. Your program will compile and run, but the DoubleClick event will never be raised for a button.

.NET can suppress the Click and DoubleClick events on selected controls by setting the StandardClick and StandardDoubleClick values, respectively, of the ControlStyles enumeration.

The events listed in Table 4-2 implement drag-and-drop.

Table 4-2. Control Drag-and-Drop events

Event

Event argument

Description

DragDrop

DragEventArgs

Raised when a drag-and-drop operation is completed.

DragEnter

DragEventArgs

Raised when an object is dragged onto the control. At the time this event is raised, the drag operation is still in progress (i.e., the user hasn't yet let the mouse button go up).

DragLeave

DragEventArgs

Raised when an object is dragged off of the control.

DragOver

DragEventArgs

Raised when an object is dragged over the control.

GiveFeedback

GiveFeedbackEventArgs

Raised during a drag operation to allow modification to the mouse pointer.

The events listed in Table 4-3 are raised when a mouse interacts with a control. Some of these low-level events, in addition to being raised, are synthesized into the higher-level Click and DoubleClick events.

Table 4-3. Control Mouse events

Event

Event argument

Description

MouseEnter

EventArgs

Raised when mouse pointer enters control.

MouseMove

MouseEventArgs

Raised when mouse pointer moved over control.

MouseHover

EventArgs

Raised when mouse hovers over control.

MouseDown

MouseEventArgs

Raised when mouse button pressed while mouse pointer is over control.

MouseWheel

MouseEventArgs

Raised when mouse wheel moved while control has focus.

MouseUp

MouseEventArgs

Raised when mouse button released while mouse pointer is over control.

MouseLeave

EventArgs

Raised when mouse pointer leaves control.

Implementing an Event

Events were demonstrated back in Chapter 2. There, a Button was added to a form and the Click event for the button was handled. Handling the event was demonstrated both in a text editor and in Visual Studio .NET. Those examples also showed that the syntax and mechanics of handling events is somewhat different in C# and VB.NET, although the underlying fundamentals are the same.

The code samples shown in Example 4-1 (in C#) and Example 4-3 (in VB.NET) are duplicates of those shown in Example 2-5 and Example 2-6.

In C#

Example 4-1, reproduced here from Example 2-5, demonstrates the basic principles of implementing an event handler in C# by using a text editor.

Example 4-1. Hello World Windows application with button control in C# (HelloWorld-win-button.cs)

image with no caption

using System;
using System.Drawing;
using System.Windows.Forms;
   
namespace ProgrammingWinApps
{
   public class HelloWorld : System.Windows.Forms.Form
   {
   
      private Button btn;
   
      public HelloWorld(  )
      {
         Text = "Hello World";
   
         btn = new Button(  );
         btn.Location = new Point(50,50);
         btn.Text = "Goodbye";
         btn.Click += new System.EventHandler(btn_Click);
   
         Controls.Add(btn);
      }
   
      static void Main(  ) 
      {
         Application.Run(new HelloWorld(  ));
      }
   
      private void btn_Click(object sender, EventArgs e)
                     {
                     Application.Exit(  );
                     }
   }
}

Handling an event in C# involves two steps:

Implement the event handler method

The event handler method, highlighted in Example 4-1, is called btn_Click. It has the required signature (two parameters: sender of type object and e of type EventArgs), and, as required, it returns void.

The code in the body of the event handler method performs whatever programming task is required to respond to the event. In this example, the event handler closes the application with the static method Application.Exit( ).

Hook up the event handler method to the event

This is done by instantiating an EventHandler delegate, which encapsulates the btn_Click method, then using the += operator to add that delegate to the button's Click event. This is done in Example 4-1 with the following line of code:

image with no caption

btn.Click += new System.EventHandler(btn_Click);

As easy as this is, Visual Studio .NET makes it even simpler. The fundamentals in working with events in the IDE will be shown with a simple application.

Open Visual Studio .NET and create a new Visual C# Windows Application project. Name it csEvents. Put a Label and Button control on the form. Using the Properties window, set the control properties to the values listed in Table 4-4.

Table 4-4. HelloWorld-Events Control properties

Control type

Property

Value

Form

(Name)

Form1

Text

Events Demonstrator

Size

250,200

Label

(Name)

lblTitle

Text

Events Demonstrator

Size

150,25

Button

(Name)

btnTest

Text

Do It!

After the controls are placed and the properties set, Visual Studio .NET should look similar to Figure 4-1.

csEvents layout in Visual Studio .NET

Figure 4-1. csEvents layout in Visual Studio .NET

There are several different ways to implement an event in Visual Studio .NET with C#.

Tip

C# and VB.NET differ in the user interface used by Visual Studio .NET to implement events, although the underlying class libraries and technology are the same. These differences are primarily a nod to backward compatibility for VB6 programmers. The VB.NET version will be detailed shortly.

Double-click the control

Double-click the button control. Visual Studio.NET takes this as an indication that you want to implement the default event handler for the button (the click event). Visual Studio.NET creates and registers an event handler, and moves you to the code window with the cursor placed in the body of the event handler:

image with no caption

private void btnTest_Click(object sender, System.EventArgs e)
{
   
}

Visual Studio.NET has created a method declaration that follows the event handler prototype exactly. The method name defaults to the name of the control with an underscore character and the default event name concatenated on the end.

Tip

Each control has a default event (whichever event is most commonly used with that control). For many controls, it is the Click event, but not always. The default event for the TreeView control is AfterSelect, although that control does have a Click event. The default event for each control will be detailed when the control is covered.

Enter a line of code to pop up a message box in response to the button click:

image with no caption

private void btnTest_Click(object sender, System.EventArgs e)
{
   MessageBox.Show("Click event just handled.","Event Demo");
}

Running the form will produce the application shown in Figure 4-2. Clicking the button will pop up a message dialog with the words "Click event just handled," as shown in Figure 4-3.

Events Demonstrator application

Figure 4-2. Events Demonstrator application

Event Demonstrator MessageBox

Figure 4-3. Event Demonstrator MessageBox

When you developed Example 4-1 in a text editor, you saw that in addition to implementing the event handler method, you also had to add a delegate encapsulating that method to the event. In Example 4-1, it was done with the following line of code:

image with no caption

                     btn.Click += new System.EventHandler(btn_Click);
                  

When using Visual Studio .NET, registering the event is done for you automatically. This can be seen by going to the code window for the form, finding and expanding the region of code labeled Windows Form Designer generated code, and looking for the following section of code inside the InitializeComponent method:

image with no caption

//
// btnTest
// 
this.btnTest.Location = new System.Drawing.Point(56, 128);
this.btnTest.Name = "btnTest";
this.btnTest.TabIndex = 2;
this.btnTest.Text = "Do It!";
this.btnTest.Click += new System.EventHandler(this.btnTest_Click);
                  

The highlighted line of code adds the method to the EventHandler delegate.

Use the lightning bolt icon in the Properties window

Double-clicking on the control only allows you to handle the default event using the default event handler method name. You can also create event handlers for any of a control's events, and name them whatever you like. To do so in C#, highlight the control in the design window and view the Properties window for the control (select View Properties Window from the menu, press F4, or right-click and select Properties).

At the top of the Properties window is a row of buttons, shown in Figure 4-4. The first two buttons on the left sort the window's contents by category or alphabetically. The right-most button displays Property pages, if any. Of most interest here are the remaining two buttons.

Property Window button bar

Figure 4-4. Property Window button bar

The Properties button (

image with no caption

) causes the window to display all the supported events for the control, while the Events button (

image with no caption

) causes the window to display all the supported events for the control, either alphabetically or by category.

If there is an event handler already defined for an event, it will be listed in the column next to the event name. Clicking on that name and pressing the Enter key will take you to that event handler in the code window.

If there is no event handler listed next to an event, highlight the event and press the Enter key to create an event handler with the default name. The method skeleton will be created, the event will be registered, and you will be taken to that method in the code window, where you can enter the body of the method.

Alternatively, you can enter any method name you wish. Pressing Enter will use the name you entered to create a skeleton event handler method and automatically hook up that event handler method to the event.

Finally, when an event is highlighted in the Properties window, a drop-down arrow will appear in the column for the method names. Clicking on the drop-down will display all the methods in the code available to be event handlers. This can be used to assign the same method to many different events, either for the same control or for different controls.

To demonstrate this last point, go to the code window for csEvents. Create a generic event handler method by adding the code shown in Example 4-2 to the Form1 class.

Example 4-2. Generic event handler in C#

image with no caption

private void GenericEventHandler(object sender, EventArgs e)
{
   MessageBox.Show("Generic event handler", "Event Demo");
}

Now go back to the form designer and highlight the button. If the Events are not visible in the Property window, click on the Events button. Slide down to the MouseEnter event and click the drop-down arrow. You will see all the available methods, as shown in Figure 4-5.

Event drop-down

Figure 4-5. Event drop-down

Click on GenericEventHandler to hook that handler method to the event.

Now add the same event handler to the Click method of the Label control named lblTitle. Click on the lblTitle control, find the Click event in the list of events in the Property window, click the drop-down arrow, and select GenericEventHandler. Run the program.

You will see that every time the mouse moves over the button, a dialog box similar to that shown in Figure 4-3 appears with the message Generic Event Handler. In fact, it is not possible to click on the button with your mouse because the MouseEnter event occurs before you have the opportunity to click on the button. (You can however click the button by pressing the Enter key once the button has focus.)

Clicking on the label containing the title also brings up the Generic Event Handler message.

Visual Studio .NET does all the work of hooking the GenericEventHandler method to both the lblTitle Click event and the btnTest MouseOver event. You can see how this was done by examining the region of code labeled Windows Form Designer generated code. In the section of code initializing the lblTitle control, the following line hooks the GenericEventHandler method to the Click event:

image with no caption

this.lblTitle.Click += new System.EventHandler(this.GenericEventHandler);

Similarly, the GenericEventHandler method is hooked to the btnTest MouseEnter event with this line of code:

image with no caption

this.btnTest.MouseEnter +=
         new System.EventHandler(this.GenericEventHandler);

Warning

It is very easy to add event handlers to a form by double-clicking on a control. Sometimes adding the handlers is too easy, since accidentally double-clicking on a control will create an empty event handler method for that control, if it does not already have one. You may notice some of these empty methods in your code. These empty methods impose a small performance penalty on your program and clutter your code.

If you simply delete the empty methods, your program will not compile. Remember that the event handler was added to the event delegate in the "hidden" code contained in the InitializeComponent method in the Windows Form Designer generated code region. To manually delete the empty method from your code, you must also delete any references to the method where it is added to an event delegate.

A simple way to delete an event handler is to use the Properties window: highlight the offending event and delete the event handler. This will remove both the method and the registration code.

By the same token, if you rename an event handler method manually, you must find the relevant line in InitializeComponent and rename the method reference there as well, but doing so on the Properties window will update the reference for you.

In the unusual case that you want more than one handler for a single event, you can add as many event handler methods to an event as you wish simply by implementing the event handler methods, and then adding those events to the delegate with additional += statements. (Even in Visual Studio .NET, this requires inserting the lines of code yourself.)

Similarly, you can remove an event handler method by using the -= operator. For example, the following code snippet adds three methods to a delegate for handling the Click event, then removes one of the methods from the delegate. In this way, it is possible to add and remove event handler methods dynamically and thereby start and stop event handling for specific events anywhere in your program:

image with no caption

btn.Click += new System.EventHandler(GenericEventHandler);
btn.Click += new System.EventHandler(SpecialEventHandler);
btn.Click += new System.EventHandler(ClickEventHandler);
btn.Click -= new System.EventHandler(GenericEventHandler);

In VB.NET

Example 4-3, reproduced from Example 2-6, demonstrates the basic principles of implementing an event handler in VB.NET, using a text editor.

Example 4-3. Hello World Windows application with button control in VB.NET (HelloWorld-win-button.vb)

image with no caption

imports System
imports System.Drawing
imports System.Windows.Forms
   
namespace ProgrammingWinApps
   public class HelloWorld : inherits System.Windows.Forms.Form
   
      Private WithEvents btn as Button
      public sub New(  )
         Text = "Hello World"
   
         btn = new Button(  )
         btn.Location = new Point(50,50)
         btn.Text = "Goodbye"
         Controls.Add(btn)
      end sub
   
      public shared sub Main(  ) 
         Application.Run(new HelloWorld(  ))
      end sub
   
      private sub btn_Click(ByVal sender as object, _
                     ByVal e as EventArgs) _
                     Handles btn.Click
                     Application.Exit(  )
                     end sub
   end class
end namespace

As with the C# example shown in Example 4-1, there are two steps to handling an event in VB.NET. However the syntax used in VB.NET is somewhat different than that in C#.

Implement the event handler method

The event handler method, highlighted in Example 4-3, is called btn_Click. It has the required signature (two objects: sender of type object and e of type EventArgs). Since this method is a subroutine, denoted by the sub keyword, it does not return a value. Event handlers never return a value.

The Handles keyword specifies which event this method will handle. The identifier following the keyword indicates that this method will handle the Click event for the Button called btn.

The code in the body of the event handler method performs whatever programming chore is required. In this example, it closes the application with the static method Application.Exit( ).

Instantiate the control using the WithEvents keyword

Unlike in C#, there is no code here to explicitly add the method to the delegate. Instead, a Button is declared as a private member variable using the keyword WithEvents. This keyword tells the compiler that this object will raise events:

image with no caption

Private WithEvents btn as Button

The compiler automatically creates delegates for any events referred to by a Handles clause and adds the event handler methods to the appropriate delegate.

Implementing events in VB.NET is made even easier when using Visual Studio .NET. To demonstrate this, open Visual Studio .NET and create a new VB.NET Windows application project called vbEvents.

Put a Label control and a Button control on the form. Using the Properties window, set the control properties to the values listed in Table 4-4 (the same values used in the C# example).

You can use VB.NET in several different ways to implement events in Visual Studio .NET.

Double-click the control

Double-click the Button control. You will be brought immediately to the code-editing window for the control, ready to enter code for the default event. The following code skeleton for the event handler method will be in place, with the cursor properly placed for you to commence typing the body of the method:

image with no caption

Private Sub btnTest_Click(ByVal sender As System.Object, _
                  ByVal e As System.EventArgs) Handles btnTest.Click
End Sub

Tip

In Visual Studio .NET, the method declaration will be on a single line, not wrapped with a line-continuation character, as printed here.

This method declaration exactly follows the event handler method prototype for VB.NET. The method name defaults to the name of the control with an underscore character and the default event name concatenated on the end. You can, however, use any name you wish, since the actual relationship between the method and the event it handles is dictated by the Handles keyword.

Tip

Each control has a default event (whichever event is most commonly used with that control). For many controls, it is the Click event. The default event for the TreeView control, however, is AfterSelect, although that control does have a Click event. The default event for each control will be detailed when the control is covered.

Enter a line of code to pop up a message box so that the btnTest_Click method now looks like:

image with no caption

Private Sub btnTest_Click(ByVal sender As System.Object,
                  ByVal e As System.EventArgs) Handles btnTest.Click
   MessageBox.Show("Click event just handled.","Event Demo")
End Sub

Running the form will produce the same application previously developed in C#, shown in Figure 4-2. Clicking the button will pop up a message dialog with the words "Click event just handled," as shown in Figure 4-3.

In Example 4-3, which was developed in a text editor, you saw that in addition to implementing the event handler method, you must also instantiate the control using the WithEvents keyword. In Example 4-3, that was done with the following line of code:

image with no caption

Private WithEvents btn as Button

When using Visual Studio .NET, this step is done for you automatically. You can see it by going to the code window for the form, finding and expanding out the region of code labeled Windows Form Designer generated code, and looking for the following line of code:

image with no caption

Friend WithEvents btnTest As System.Windows.Forms.Button

In Example 4-3, the Private access modifier was used for the object, which restricts access to the object to the class of which it is a member, i.e., HelloWorld. Visual Studio .NET uses the Friend access modifier, which is somewhat more expansive, allowing access from any class within the project in which it is defined.

Warning

It is very easy to add event handlers to a form by double-clicking on a control. Sometimes it is too easy, since accidentally double-clicking on a control will create an empty event-handler method for that control, if it does not already have one. You may notice some of these empty methods in your code. In VB.NET applications, they do not usually cause any problems other than clutter.

If you simply delete the empty methods, the VB.NET program will still compile (unlike with C#). This is because there is no explicit connection between the event-handler method and the event in the code. The compiler makes the connection at compile time only if both halves of the connection are present.

If the control itself is deleted from the form designer, the lines of code instantiating the control with the WithEvents keyword will be removed, but the event-handler method will remain. You might want to delete these methods manually if they are no longer used, to minimize clutter and confusion in your code.

Use the drop-down lists at the top of the code window

Double-clicking on the control allows you to add code to the default event only by using the default event-handler method name. You can also create event handlers for any of a control's events. To do so in VB.NET, view the code window. At the top of the window are two drop-down lists, as shown in Figure 4-6. The drop-down on the left lists all the controls on the form, while the drop-down on the right lists all the possible events for each control (plus (Declarations), which moves the cursor to the top of the code window).

VB.NET Object and event drop-down lists

Figure 4-6. VB.NET Object and event drop-down lists

First select the control whose events you wish to handle from the left drop-down. Select the event to handle from the right drop-down. If the event handler already exists, the cursor will move to the subroutine. If the event handler subroutine does not exist, then it will be created, with the cursor located inside the code skeleton, ready to enter your code.

Tip

It is a good idea to use meaningful, nondefault names for all the controls on your form that will be referenced elsewhere in the code to enhance readability and maintainability. Default names of controls are always the name of the control with a number appended, such as Button23, and default event-handler names are the name of the control, with an underscore and the event name, such as Button23_Click. This becomes especially important when assigning events to controls.

An event handler method can easily be renamed simply by editing the method declaration. There is no need to edit any other line of code, since the method name is not associated with the method until compile time.

It is not possible to directly assign the same event handler method to multiple events on a form using this syntax, as can be done in C#. This is because each event-handler method uses the Handles keyword to directly associate the method with a specific event. One way to accomplish this indirectly would be to call the same method from within multiple event handlers.

It is possible, however, to have multiple event-handler methods respond to the same event. This is accomplished by having the Handles keyword on each of the event-handler methods refer to the same event. In this situation, the order in which the multiple events will fire is not defined. If the order is important, then you need to use the dynamic event implementation and the AddHandler statement, described next.

Note

The drop-down at the top of the code window is somewhat different for the Form than for the controls contained within the Form. When a control other than the Form is selected in the left drop-down, the right drop-down lists all the possible events for that control.

If the Form is selected in the left drop-down menu, the only thing listed besides (Declarations) are existing methods in the Form class plus the protected method Finalize.

There are two other entries in the left drop-down menu that are not objects: (Overrides) and (Base Class Events). (Overrides) causes the right drop-down to display all the virtual methods and properties of the Form class that can be overridden. (Base Class Events) causes the right drop-down to display all the events for the Form class that can be implemented. In either case, selecting an entry from the right drop-down causes Visual Studio .NET to insert the skeleton code for the appropriate property, method, or event to be inserted in the code window. If an event is selected, Visual Studio .NET will also hook the event-handler method to the event.

Dynamic event implementation

In the VB.NET syntax described so far, there is no sign of delegates in the code. The compiler automatically creates the necessary delegates at compile time and adds the methods marked with the Handles keyword to the appropriate delegate for each event.

You can explicitly add the event handler methods to the delegate in VB.NET, as is done in C#, even though it is not the default way in which Visual Studio .NET handles events in VB.NET programs. This is done with the AddHandler statement. AddHandler does not provide any significant performance benefits over the Handles keyword, but does provide greater flexibility. AddHandler and RemoveHandler allow you to add, remove, and change the event handler associated with an event dynamically (in your code at runtime). You can also add multiple event handlers to a single event using AddHandler.

The listing in Example 4-4 shows the modifications to Example 4-3 necessary to use the AddHandler statement rather than the Handles keyword for implementing events. The new or modified lines are highlighted.

Example 4-4. Using AddHandler to implement events

image with no caption

imports System
imports System.Drawing
imports System.Windows.Forms
   
namespace ProgrammingWinApps
   public class EventsDemo : inherits System.Windows.Forms.Form
   
      Private btn as Button
      public sub New(  )
            Text = "Events Demonstration - AddHandler"
   
         btn = new Button(  )
         btn.Location = new Point(50,50)
         btn.Text = "Test"
   
         Controls.Add(btn)
         AddHandler btn.Click, AddressOf btn_Click
      end sub
   
      public shared sub Main(  ) 
         Application.Run(new EventsDemo(  ))
      end sub
   
      private sub btn_Click(ByVal sender as object, _
                        ByVal e as EventArgs)
         MessageBox.Show("btn_Click method","Events Demonstration")
      end sub
   end class
end namespace

Three code changes are necessary to dynamically add event handlers in a VB.NET program.

  • The line instantiating the control no longer includes the WithEvents keyword. (This is not mandatory, but the keyword is no longer needed.)

  • The event handler method declaration no longer uses the Handles keyword.

  • An AddHandler statement is included to add the event-handler method to the delegate that handles the event.

    The AddHandler statement takes two comma-separated arguments. The first argument is the name of the event to be handled, using dot notation. It is a two-part name consisting of the name of the object and the name of the event. The second argument is the AddressOf keyword followed by the name of the method that handles the event.

The RemoveHandler statement uses the same syntax as the AddHandler statement. Together they let you start or stop event handling for any specific event anywhere in the program.

It is possible to use both techniques for event handling in the same program, even for the same event in the same control. Consider the program in Example 4-5, which uses both techniques to handle the Click event for the button.

Example 4-5. Using both AddHandler and Handles to handle events

image with no caption

imports System
imports System.Drawing
imports System.Windows.Forms
   
namespace ProgrammingWinApps
   public class EventsDemo : inherits System.Windows.Forms.Form
   
      Private WithEvents btn as Button
      public sub New(  )
            Text = "Events Demonstration - AddHandler"
   
         btn = new Button(  )
         btn.Location = new Point(50,50)
         btn.Text = "Test"
   
         Controls.Add(btn)
         AddHandler btn.Click, AddressOf btn_Click
      end sub
   
      public shared sub Main(  ) 
         Application.Run(new EventsDemo(  ))
      end sub
   
      private sub btn_Click(ByVal sender as object, _
            ByVal e as EventArgs)
         MessageBox.Show("btn_Click method","Events Demonstration")
      end sub
   
      private sub btn_ClickHandles(ByVal sender as object, _
                        ByVal e as EventArgs) _
                        Handles btn.Click
                        MessageBox.Show("btn_ClickHandles method","Events Demonstration")
                        end sub
   end class
end namespace

The WithEvents keyword was added back to the line instantiating the Button object. It has no effect on the AddHandler statement functionality. However, it does enable the btn_ClickHandles method, which utilizes the Handles keyword, to also handle the button Click event.

When the program is compiled and run, the btnClickHandles method, which the compiler adds to the delegate behind the scenes, is called first. It is followed by the btn_Click method, which is added to the delegate by the AddHandler statement.

The following two statements are equivalent:

image with no caption

btn.Click += new System.EventHandler(GenericEventHandler);

image with no caption

AddHandler btn.Click, AddressOf GenericEventHandler

Get Programming .NET Windows Applications now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.