Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles

We take a look at planned support for parallel programming for both managed and native code in the next version of Visual Studio.

Stephen Toub and Hazim Shafi

MSDN Magazine October 2008

...

Read more!

This month we take a look at FxCop and other tools that enforce your design rules, along with jQuery.

Scott Mitchell

MSDN Magazine December 2008

...

Read more!

Building a data access layer using LINQ to SQL and the ADO.NET Entity Framework allows you to decouple your application from the persistence technology you're using.

Anthony Sneed

MSDN Magazine December 2008

...

Read more!

Here we introduce Microsoft Code Name “Geneva,” the new framework for building claims-based applications and services, and federated security scenarios.

Michele Leroux Bustamante

MSDN Magazine December 2008

...

Read more!

The heart of Windows Workflow Foundation is its declarative programming model. Here are some best practices to consider when using WF to realize software solutions in the real world.

Josh Lane

MSDN Magazine December 2008

...

Read more!

Also by this Author

Ted Pattison shows how to use a new STSDEV utility to set up and deploy SharePoint development projects in Visual Studio in an easy and repeatable manner.

Ted Pattison

MSDN Magazine March 2008

...

Read more!

This month, Office Open XML, which allows ASP.NET and SharePoint developers to read, write, and generate Word, Excel, and PowerPoint documents on the server without running an Office desktop application there.

Ted Pattison

MSDN Magazine November 2006

...

Read more!

When you begin to work with the Microsoft® . NET Framework 2. 0 and ASP. NET, you discover that the new Web Parts infrastructure adds some very powerful functionality to the underlying platform. In the September 2005 issue of MSDN®Magazine, Fritz Onion and I have an article on programming Web Parts titled "ASP.

Ted Pattison

MSDN Magazine February 2006

...

Read more!

In this installment of Advanced Basics Ted Pattison discusses the localization of Web sites in ASP.NET 2.0.

Ted Pattison

MSDN Magazine August 2006

...

Read more!

One of the most powerful aspects of the Microsoft® . NET Framework is its support for attributes. Attribute-based programming is extremely powerful because it adds a declarative dimension to designing and writing software.

Ted Pattison

MSDN Magazine May 2005

...

Read more!

Popular Articles

This article presents an overview of the motivation behind new techniques that decompose problems into independent pieces for optimal use of parallel programming.

David Callahan

MSDN Magazine October 2008

...

Read more!

One-time passwords offer solutions to dictionary attacks, phishing, interception, and lots of other security breaches. Here's how it all works.

Dan Griffin

MSDN Magazine May 2008

...

Read more!

Jeff Prosise explains when it's better to use UpdatePanel and when it's better to use asynchronous calls to WebMethods or page methods instead.

Jeff Prosise

MSDN Magazine June 2007

...

Read more!

Here we describe some of the more common challenges to concurrent programming and present advice for coping with them in your software.

Joe Duffy

MSDN Magazine October 2008

...

Read more!

James Avery does it again with his popular list of developer tools. This time he covers the best Visual Studio add-ins available today that you can download for free.

James Avery

MSDN Magazine December 2005

...

Read more!

Our Blog

A team project is simply a bucket that stores and partitions all of the artifacts you track and use within a Team Foundation Server (TFS) project.

In the December 2008 issue of MSDN Magazine, Brian A. Randell explains how you can use and customize the MSF Agile and MFS CMMI process templates to get the most out of them for your ...

Read more!

Every month, the CLR team gives us insight into the core of managed code, .NET programming best practices, technologies underlying the CLR and .NET Framework, and other tips and suggestions.

In the December 2008 issue of MSDN Magazine, Erika Fuentes and Eric Eilebrecht cover some common issues developers encounter when tuning ...

Read more!

We're currently in the process of stepping back and taking a critical look at our Web site to see how you all are using it - and how we can redesign parts of it (big or small) to make that experience better.  We are continuously receiving your feedback on existing frustrations and we are working hard to remedy those (as a general fyi, most of the frustrations have to do with navigation).  However, in order to get a sense of whether we need to look at some of the more fundamental ...

Read more!

Because Windows Workflow Foundation (WF) is based on a runtime that manages the execution of workflows and activities, testing must, in almost all cases, involve the use of the runtime – and this can introduce some interesting challenges.

In the November 2008 issue of MSDN Magazine, Matt Milner presents some techniques for unit testing ...

Read more!

Visual Studio 2008 Team Foundation Server Build (better known as Team Build) is a core feature of Team Foundation Server 2008. Microsoft designed Team Build to be an industrial-strength build automation tool.

In the November 2008 issue of MSDN Magazine, Brian A. Randell introduces you to Team Build 2008 and walks you through the process ...

Read more!

Basic Instincts
Static Event Binding Using WithEvents
Ted Pattison

This month's Basic Instincts column builds upon my last three columns in which I introduced and explained the fundamental concepts and syntax associated with delegates and events. Last month I showed you how to design and write a simple class that defines and raises events. You also saw how to dynamically bind an event handler to an event using the AddHandler keyword. This month I am going to discuss static event binding, an alternative technique for registering an event handler.

Using the WithEvents Keyword
Programmers with previous experience in Visual Basic® may find that static event binding is fairly easy because of its familiar syntax using the WithEvents keyword. Let me revisit the example of the BankAccount class that I used in last month's column. This class defines an event as a public member and raises the event from within a method definition, as shown here:
Delegate Sub LargeWithdrawHandler(ByVal Amount As Decimal)

Class BankAccount
  Public Event LargeWithdraw As LargeWithdrawHandler
  Sub Withdraw(ByVal Amount As Decimal)
    '*** send notifications if required
    If (Amount > 5000) Then
      RaiseEvent LargeWithdraw(Amount)
    End If
    '*** perform withdrawal
  End Sub
End Class
Objects created from the BankAccount class expose the LargeWithdraw event. You can see that a BankAccount object contains the logic to raise the LargeWithdraw event whenever a withdrawal is made for a value greater than $5,000.
Imagine that you want to create a new class named AccountAuditor1 to act as an event listener using static event binding. You can define a class as an event listener by adding one or more fields defined with the WithEvents keyword:
Class AccountAuditor1
  Private WithEvents account As BankAccount
  '*** other members omitted
End Class
You should note that a field defined using the WithEvents keyword must be based on a class that defines one or more instance events; otherwise it will cause a compile-time error. In this example, the field that is named account can be defined using the WithEvents keyword because the BankAccount class defines an instance event named LargeWithdraw.
The point of defining the account field with the WithEvents keyword is to allow methods defined within the AccountAuditor1 class to be registered as event handlers for events raised by a BankAccount object. Now that I've defined a WithEvents field, I'll create a method that will act as an event handler for the LargeWithdraw event.
When you define a method that's going to be an event handler, it must have the appropriate calling signature. For example, a method that's going to act as an event handler for the LargeWithdraw event must have a calling signature that matches LargeWithdrawHandler. Look at the following class definition:
Class AccountAuditor1
  Private WithEvents account As BankAccount
  Sub Handler1(ByVal amount As Decimal) Handles account.LargeWithdraw
    '*** handler method implementation
  End Sub
End Class
In this example, I've added a new method named Handler1. As you can see, this handler method has a calling signature that matches the delegate type LargeWithdrawHandler. Also notice that the definition of the Handler1 method contains a Handles clause:
Sub Handler1(ByVal amount As Decimal) Handles account.LargeWithdraw
You should see that a Handles clause is based on a WithEvents field and an event. The Handles clause is important because it provides a hint to the Visual Basic .NET compiler. More specifically, the presence of a Handles clause triggers the Visual Basic .NET compiler to generate extra code that will create and register an event handler. In this particular case, the compiler generates code to create an event handler of type LargeWithdrawHandler that's bound to the Handler1 method. The compiler will then generate code to register it with the BankAccount object that has been assigned to the account field.
Note that the Handles keyword is new in Visual Basic .NET. Earlier versions of Visual Basic required you to use a specific naming convention for event handler methods. For example, Visual Basic 6.0 would require the handler method in the previous example to be defined using the name account_LargeWithdraw. However, in Visual Basic .NET the name of the handler method doesn't matter; what does matter is that the handler method is defined with a Handles clause.
The real magic is performed by the Visual Basic .NET compiler when you assign an event source object to a WithEvents field. I'm going to defer the discussion of how the compiler performs this magic until later. For now, I'm going to make one more addition to AccountAuditor1 so the class can be used as an event listener.
A listener object needs an event source object. For example, it doesn't make sense to create an AccountAuditor1 object unless you have a BankAccount object that's going to act as an event source. Therefore, I'm going to add a constructor so that every AccountAuditor1 object is initialized with a BankAccount object as its event source, like this:
Class AccountAuditor1
  Private WithEvents account As BankAccount
  Sub Handler1(ByVal amount As Decimal) Handles account.LargeWithdraw
    '*** handler method implementation
  End Sub
  Sub New(ByVal SourceAccount As BankAccount)
    Me.account = SourceAccount '*** triggers binding of event handler
  End Sub
End Class
Note that this constructor assigns the SourceAccount parameter to the WithEvents field named account. This is the line of code where the automatic binding of the event handler takes place. Now the AccountAuditor1 class can be used to create a listener object. For example, imagine you wrote the following application:
'*** create event source
Dim account1 As New BankAccount()

'*** create listener object and bind event handler
Dim listener1 As New AccountAuditor1(account1)

'*** do something that triggers event
account1.Withdraw(5001)
When the constructor of the AccountAuditor1 class executes, the Visual Basic .NET compiler has generated the code to create an event handler object that's bound to the Handler1 method. The compiler also generates code to register the event handler with the BankAccount object by calling the registration method add_LargeWithdraw. Therefore, the Handler1 method will execute whenever the Withdraw method raises the LargeWithdraw event.
You have just seen the fundamentals of how to create a listener object using static event binding. Figure 1 shows a complete application that uses static event binding to implement a callback design that is similar to the other techniques you have seen over the last three Basic Instincts columns. You should note that static and dynamic event binding are two different approaches that can often be used to achieve similar goals.
Delegate Sub LargeWithdrawHandler(ByVal Amount As Decimal)

Class BankAccount
  Public Event LargeWithdraw As LargeWithdrawHandler
  Sub Withdraw(ByVal Amount As Decimal)
    '*** send notifications if required
    If (Amount > 5000) Then
      RaiseEvent LargeWithdraw(Amount)
    End If
    '*** perform withdrawal
  End Sub
End Class

Class AccountAuditor1
  Private WithEvents account As BankAccount
  Sub Handler1(ByVal amount As Decimal) Handles account.LargeWithdraw
    '*** handler method implementation
    MsgBox("Handler1")
  End Sub
  Sub New(ByVal SourceAccount As BankAccount)
    Me.account = SourceAccount ''*** triggers binding of event handler
  End Sub
End Class

Class AccountAuditor2
  Private WithEvents account As BankAccount
  Sub Handler2(ByVal amount As Decimal) Handles account.LargeWithdraw
    '*** handler method implementation
    MsgBox("Handler2")
  End Sub
  Sub New(ByVal SourceAccount As BankAccount)
    Me.account = SourceAccount '*** triggers binding of event handler
  End Sub
End Class

Module MyApp
  Sub Main()
    '*** create bank account object
    Dim account1 As New BankAccount()
    '*** register event handlers
    Dim listener1 As New AccountAuditor1(account1)
    Dim listener2 As New AccountAuditor2(account1)
    '*** do something that triggers callback
    account1.Withdraw(5001)
  End Sub
End Module
Now that you have seen both static and dynamic event binding, you might be wondering which one you should use. Ideally, you should learn how to use both. The Visual Studio® .NET IDE uses static event binding when you ask it to generate skeleton definitions for your event handler methods. Therefore, your understanding of static event binding will be important when you are working with an event-driven application framework such as Windows® Forms or ASP.NET Web Forms.
However, it's also important that you know how to use dynamic event binding, which I showed last month, because of its flexibility. For example, static event binding can only be used in cases where the event source is an object. It cannot be used when the event source is a class. In other words, static event binding can be used with instance events but cannot be used with shared events. Dynamic event binding, on the other hand, will allow you to bind to either shared events or instance events.
There are circumstances when the Visual Studio .NET IDE is not able to generate all the event handling code you need for an application and you'll have to write the code to create and register event handlers by hand. In cases like these, you will find that dynamic event binding is more straightforward and easier to use. After all, it only takes a single AddHandler statement to create an event handler from a method and bind it to an event. The only requirement is that the handler method have the calling signature that's required for the event in question.
One more interesting thing to note is that static event binding is a special programming feature that is unique to Visual Basic .NET. Other managed languages, such as C#, support dynamic event binding but do not support the equivalent of static event binding. If you are going to switch between managed languages or you need to port code from C# to Visual Basic .NET, you should consider relying on dynamic event binding.

Static Event-binding Magic
Now I'd like to discuss the low-level details of how the Visual Basic .NET compiler supports static event binding. You can use static event binding without understanding all the details I am about to explain, but I'll provide the details about what the compiler is doing to satisfy any curiosity you may have about how things work behind the scenes.
Let's start by looking at what happens when you compile a class definition with a WithEvents field. Assume that you have written the following class definition:
Class AccountAuditor
  Private WithEvents account As BankAccount
  '*** other members omitted
End Class
What happens when you compile this class? The Visual Basic .NET compiler generates the class definition shown in ILDasm (see Figure 2). As you can see, the class definition generated by the compiler is very different from the one you wrote. After it has been compiled, there is no longer a field named account. Instead, there is a private field named _account and a read/write property named account. The compiler has also generated special implementations for the account property's Set and Get methods. All the magic is in the Set method implementation.
Figure 2 The Compiler's Class Definition 
So what really happens when you assign an event source object to a WithEvents field? For example, what happens when you assign a BankAccount object to the account field in the constructor of the AccountAuditor class?
Class AccountAuditor
  Private WithEvents account As BankAccount
  Sub Handler1(ByVal amount As Decimal) Handles account.LargeWithdraw
    *** handler method implementation
  End Sub
  Sub New(ByVal source As BankAccount)
    Me.account = source '*** triggers call to add_LargeWithdraw
  End Sub
End Class
It's important to see that you are really not assigning the event source object to a field. Instead, you are assigning the event source object to a property which causes the Set method named set_account to execute. The implementation for set_account is generated to create and register an event handler for each method defined with the Handles clause. Figure 3 shows how the code you write gets translated by the compiler.
'*******************************************
'**** the code you write looks like this ***
'*******************************************
Class AccountAuditor
  Private WithEvents account As BankAccount
  Sub Handler1(ByVal amount As Decimal) Handles account.LargeWithdraw
    *** handler method implementation
  End Sub
  Sub New(ByVal SourceAccount As BankAccount)
    Me.account = SourceAccount
  End Sub
End Class

'***************************************************
'*** the code that gets compiled looks like this ***
'***************************************************
Class AccountAuditor
  Private _account As BankAccount
  Private Property account() As BankAccount
    Get
      Return _account
    End Get
    '*** magic code starts here ***************************************
    Set(ByVal Value As BankAccount)
      If (Not _account Is Nothing) Then
        _account.remove_LargeWithdraw(AddressOf Me.Handler1)
      End If
      If (Not Value Is Nothing) Then
        _account = Value
        _account.add_LargeWithdraw(AddressOf Me.Handler1)
      End If
    End Set
    '*** magic code ends here *****************************************
  End Property
  Sub Handler1(ByVal amount As Decimal) Handles account.LargeWithdraw
    '*** handler implementation
  End Sub
  Sub New(ByVal source As BankAccount)
    Me.account = source '*** triggers call Set method
  End Sub
End Class
Examine the code inside the Set method of the AccountAuditor class shown in Figure 3. This code checks to see if there is an existing event source object being referenced by the _account field. If there is, the code removes it by calling the unregistration method remove_LargeWithdraw. Next, the Set method checks the Value parameter to see whether the assigned value is a valid reference to an event source object or a value of Nothing. If the Value parameter holds a valid reference to an event source object, the Set method creates an event handler bound to the Handler1 method and registers it by calling add_LargeWithdraw.
So, there's really no magic involved. When you assign a valid object reference to a WithEvents field, the compiler generates code to create delegate objects and bind them to each method that has been defined with a Handles clause. The compiler also generates the code to register these delegate objects by calling the event registration methods defined by the notification source.
Note that you are not required to bind a listener object to an event source during initialization. You can assign a value to a WithEvents field any time during the lifetime of a listener object. Consider the class definition in Figure 4. With this new design, an AccountAuditor object will not be bound to an event source after it has been initialized. However, you can call StartListening at any time to bind a listener object to an event source object. Note however that a call to StartListening will disconnect the listener object from an existing event source so that it can connect to the new one. A call to StopListening will disconnect the listener object from an existing event source and, in addition, leave it in an unbound state.
Class AccountAuditor
  Private WithEvents account As BankAccount
  Sub Handler1(ByVal amount As Decimal) Handles account.LargeWithdraw
    '*** handler method implementation
  End Sub
  Sub StartListening(ByVal source As BankAccount)
    Me.account = source '*** triggers call to add_LargeWithdraw
  End Sub
  Sub StopListening()
    Me.account = Nothing '*** triggers call to remove_LargeWithdraw
  End Sub
End Class
At this point it's fair to say that the Visual Basic .NET compiler is doing a good deal of work for you behind the scenes when you use static event binding. Furthermore, this example only involved a single handler method. In practice, you will often have many handler methods associated with a single WithEvents field.

Using WithEvents Fields in Inherited Classes
In the Microsoft® .NET Framework, it is common for a base class to raise events that are designed to be handled by derived classes. The idea is that a derived class author can customize behavior by adding event handlers to respond to events raised by a base class. This design technique provides an alternative to using overridable methods. It's also an important technique for you to understand because it is used extensively by application frameworks such as Windows Forms and ASP.NET.
Let's look at a simple example to see how this works. You have already seen that a Handles clause can be written in terms of a WithEvents field. You can also write a Handles clause using the MyBase keyword to handle an event defined within a base class, as shown in the following code:
Class CheckingAccount : Inherits BankAccount
  Sub Handler1(ByVal amount As Decimal) Handles MyBase.LargeWithdraw
    '*** handler method implementation
  End Sub
End Class
Once again, the Visual Basic .NET compiler generates the code that's needed to create an event handler and register it. However, the code for a base class event is generated in a slightly different fashion than the code for an event associated with a WithEvents field. In this example, the Visual Basic .NET compiler adds the code for binding to the LargeWithdraw method into the constructor of the CheckingAccount class.
There is one other thing you should keep in mind when working with a base class that exposes events. While a derived class can handle a base class event, it cannot raise a base class event. Take a look at the following example:
Class CheckingAccount : Inherits BankAccount
  Sub SomeOtherMethod()
    RaiseEvent LargeWithdraw(5001) '*** compile error
  End Sub
End Class
Since the implementation for an event involves a private field, the event can only be raised from within the class in which it is defined. Therefore, you will experience a compile-time error if you try to raise a base class event using a RaiseEvent statement or if you attempt to access the private field by name.

Summing it Up
I've shown you the two different techniques for registering event handlers for events: dynamic event binding and static event binding. To improve your knowledge of Visual Basic .NET, you must become comfortable with both techniques.
In the next installment of this column I will continue my discussion of events by exploring some of the most common delegate types and events in the Framework Class Libraries. In particular, I am going to focus on a delegate type named System.EventHandler and show how it provides a foundation for most of the events you will use in both Windows Forms and ASP.NET.

Send your questions and comments for Ted to instinct@microsoft.com.


Ted Pattisonis an instructor and researcher at DevelopMentor (http://www.develop.com), where he co-manages the Visual Basic curriculum. He is the author of Programming Distributed Applications with COM and Microsoft Visual Basic 6.0 (Microsoft Press, 2000).

Page view tracker