Saturday, December 19, 2009

Referencing Assemblies and Importing Namespaces

I often see people confused about the difference between referencing an assembly and importing a namespace, and why we need to do either. I’ll try to address that confusion here.

First, what is an assembly? An assembly is a .NET executable file, i.e. EXE or DLL. Whenever you build a .NET project, you are compiling your source files into an assembly.

Think of an assembly as a physical collection of .NET types. A type is a class, structure, enumeration, delegate, etc. An assembly is a physical file that contains the definitions of one or more types. You can move that assembly around and thereby move those types around. I can create an assembly here and then pass you the file and you can then use the types it contains.

Now, by default, each project pretty much only knows about the types for which it contains the definitions. Such a project would be useless though. There are certain parts of the .NET Framework that are pretty much essential to all projects. The assemblies containing these essential types, e.g. String and Int32, are referenced by default in every project.

To see this, create a new project in Visual Studio. Under your preferred language in the New Project dialogue, choose Windows and then select the Empty Project template. Once the project is created, open the Object Browser window and select My Solution in the Browse field at the top. Each root node in the tree is an assembly that your project knows about. You’ll see your project itself and mscorlib at least. If you’re targeting .NET 3.5 or later you will also see System.Core and if you’re language is VB then you will also see System and Microsoft.VisualBasic. These are the assemblies that your project references by default. Basically, there’s a line written in a file in your project that says “mscorlib.dll exists and this is where to find it”.

At this point, your project knows about ALL those assemblies and ONLY those assemblies, so it knows about ALL the types declared in those assemblies and ONLY the types declared in those assemblies. Any type that isn’t declared in one of those assemblies is unknown to your project and therefore unusable. Let’s say, for instance, that you wanted to connect to a SQL Server database. For that you would need to use the SqlConnection class. That class is not declared in any of the assemblies your project references by default so your project doesn’t know that the class exists. The SqlConnection class is declared in the System.Data.dll assembly, so we must reference this assembly in our project in order to use the SqlConnection class. Let’s now look at how to do that and how the process differs between VB and C# projects.

In C#, references are displayed in the Solution Explorer by default. Open the Solution Explorer and expand the node for your C# project. Try to expand the References node and notice that you can’t, which is because it contains no references. The default references of mscorlib.dll and System.Core.dll are considered so fundamental that they are not listed with other references, so that you cannot remove them. Right-click the References node and select Add Reference to open the Add Reference dialogue. Alternatively, select Project -> Add Reference from the main menu.

In VB, references are not displayed in the Solution Explorer by default. You must first click the Show All Files button in the Solution Explorer to reveal the References node, at which point you can right-click it as mentioned above. VB also supports adding a reference from the Project menu, as well as a third option that is not available to C# developers. You can first open the project properties, by double-clicking the My Project node in the Solution Explorer or any of a variety of other ways, and then select the References page. The top half of the page lists the assemblies referenced by the project. Click the Add button under the list to open the Add Reference dialogue.

Once you have opened the Add Reference dialogue, you have several options. The dialogue layout changed considerably between VS 2008 and 2010 but the options available are basically the same. The most common option will be to reference an assembly installed in the Global Assembly Cache (GAC). This includes any assemblies from the .NET Framework itself and any other .NET components that you have installed. The GAC is a location for common assemblies, so that there is only one copy for all projects that use them. You also have the option to browse for an assembly. You would do this for libraries that you have previously created in other projects as well as .NET components that you have downloaded but have no installation routine. Such assemblies will be copied into the output folder of each project that references them. A third option is to reference other projects in the same solution. Only projects that output a library, i.e. DLL, can be referenced. Referencing a project is like referencing the assembly that it outputs except that it allows you to update the assembly without having to recreate the reference.

That’s all there is to referencing assemblies: open the Add Reference dialogue and navigate to the desired assembly. That’s all that’s required to use a type declared in an external assembly. If you ever need to use a type from the .NET Framework but you don’t know where it’s declared, simply open the MSDN documentation for that type. At the very top of every type’s overview topic is the assembly that it’s declared in. Reference that assembly and you’re good to go.

Immediately under the assembly is the namespace that the type is a member of. You never actually need to import a namespace to use a type. Importing namespaces is simply a convenience that makes code easier to write and read. Let’s now look at what a namespace is, as well as how and why you might import one.

Unlike an assembly, which is a physical collection of types, a namespace is a logical collection of types. That means that, while every type is a member of a namespace, a namespace has no physical boundary. You can point to a DLL file and say “there’s the SomeAssembly.dll assembly” but you can never point to a namespace because it doesn’t physically exist.

The reason a namespace is called a namespace is because it is a logical space within which every type name is unique. For instance, there are several classes named TextBox in the .NET Framework alone, not to mention all those that developers around the world have created. When you refer to the TextBox type in code, how do you know which one you’re referring to? You know because each type is qualified by its namespace. For instance, there is the System.Windows.Forms.TextBox class and there is the System.Web.UI.WebControls.TextBox class, plus others besides. It’s similar to there being two people named John at your work place but you know which one someone is talking about because they qualify the name, e.g. John Smith and John Williams or Big John and Little John.

Now, it’s not strictly true that every type in the same namespace must be unique. There’s nothing to stop me declaring the MyControl class as a member of the MyControls namespace and someone else doing the same on the other side of the world. In fact, there’s nothing to stop me doing it in two different projects on the same machine, or even in the same solution. What is true is that you can never have two types with the same name in the same namespace in the same project. That will cause a compilation error.

We don’t actually have to import any namespace to use its member types. You can happily use the fully qualified name of every type in your code, e.g.

C#
string str = ((System.Windows.Forms.TextBox) sender).Text;
VB
Dim str As String = DirectCast(sender, System.Windows.Forms.TextBox).Text
If you do that though, your code will tend to look rather cluttered and will be less comfortable to read. Generally speaking, you should import the namespaces containing the types you use and use the unqualified type names in your code. So, if you import the System.Windows.Forms namespace, the code above can be replaced with the following:

C#
string str = ((TextBox) sender).Text;
VB
Dim str As String = DirectCast(sender, TextBox).Text
Note that, even though the String class is a member of the System namespace, there’s never a need to use System.String, even if you haven’t imported the System namespace. That’s because string (C#) and String (VB) are native data types that you can use to alias the .NET System.String class. The same goes for the int (C#) and Integer (VB) data types and the System.Int32 structure, amongst others.

Getting back to namespaces, how exactly do we import one? There is only one way in C# and that is with the using directive. At the top of a code file you specify a namespace preceded by the using keyword. For instance, to be able to use the WinForms TextBox class unqualified, as was done above, we must import the System.Windows.Forms namespace:

C#
using System.Windows.Forms;
When you create a new code file in C#, you’ll normally find a few using directives added by default. Which ones they are depends on the item template used to create the file. When you create a form in a WinForms project, the System.Windows.Forms namespace is one of those imported by default, because its use is expected to be very common. Note that a using directive applies only to the file it appears in.

Importing a namespace in VB can be similar, except that you use the Imports keyword. The equivalent of the C# using directive above would be:

VB
Imports System.Windows.Forms
When you create a code file in VB, notice that there are no Imports statements added by default, yet you are still able to refer to many types unqualified. This is because VB provides a second option for importing namespaces and it’s that method that’s used by default. Going back to the References page of the project properties, notice that the bottom half of the page contains a list of namespaces. That list contains all the namespaces that have members declared in your project and the assemblies referenced by your project. You simply have to check the box next to a namespace to import it, and some are already checked by default.

Importing a namespace on the References page differs from using the Imports keyword because it is project-wide. Just like the using directive in C#, the VB Imports keyword affects only the code file it appears in, while the References page imports namespaces for every code file in a project.

So, what are my recommendations for importing namespaces? I used the following rules when I was writing all my code myself:
  1. If a type appears only once in code, use the fully-qualified name.
  2. In VB, import a namespace project-wide by default.
  3. In VB, choose file-level imports over project-wide imports if you want to refer to two like-named types from different namespaces in two different code files.
  4. In the case of a name clash, i.e. using two like-named types in the same code file, use the fully qualified name of both types.
Now that I use ReSharper, those rules have changed a little bit because, when an unqualified name is typed, ReSharper will offer to create a file-level import of the appropriate namespace in both C# and VB. Other code-generation tools may be similar.

Another notable difference between VB and C# is support for partial qualifying namespaces. For instance, let’s say that you have imported the System namespace. In C#, if you want to refer to a type in a child namespace, you must still use the fully-qualified name, e.g.

C#
var field = new System.Windows.Forms.TextBox();
In VB, on the other hand, you can use a partially-qualified name, omitting the imported System namespace:

VB
Dim field As New Windows.Forms.TextBox
So, in summary, you must reference an assembly, i.e. a DLL, if you want to use any type declared within it. Importing a namespace is never necessary and simply allows you to use types that are members of that namespace without qualifying the name. If you ever want to use a type but you don’t know what assembly it’s declared in or what namespace it’s a member of, simply consult the MSDN documentation for that type. It will display both that the very top of the page.

Sunday, November 1, 2009

Defining and Raising Custom Events

We’ve all handled events before, e.g. the Load of a Form, the Click of a Button or the TextChanged of a TextBox. Many, even relatively experienced, developers aren’t too sure on how to raise events from their own classes though.

The .NET Framework provides a fairly simple mechanism for events but, more than that, a convention is used throughout the Framework for employing that mechanism. While you don’t have to, it’s a good idea to stick to that convention in your own code. Doing so means that your interface will be consistent with the rest of the Framework, and consistency is a good thing. Using your types will feel familiar to yourself and others because they will behave just like the types you’re used to using from the Framework.

First up, let’s define exactly what an event is. In conceptual terms, an event is a notification that something has happened. Just like your microwave oven makes a “beep” or “ding” sound to notify you that it has finished cooking your food, so a .NET object raises an event to notify any other objects that are listening that it has done something or something has been done to it.

Technically, an event is a member of a type, just like properties and methods, except its type is a delegate rather than a class or structure. A delegate, or at least an instance of a delegate, is an object that contains a reference to a method. In the case of an event, the method the delegate refers to is the event handler. As an example, the Button class has a Click event defined as type EventHandler. The EventHandler delegate is defined like so:

C#

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

VB

Public Delegate Sub EventHandler(ByVal sender As Object, ByVal e As EventArgs)

That method signature should look familiar because it looks a lot like the signature of an event handler method, e.g.

C#

private void button1_Click(object sender, EventArgs e)
{
    // ...
}

VB

Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
    '...
End Sub

When you create a handler for the Click event of a Button in your form, what you’re actually doing is creating an instance of the EventHandler delegate, providing it a reference to your method and then assigning it to the Click member of the Button. When the Button is clicked, it goes to its Click event and invokes the delegate it finds there, which in turn invokes your method.

This post isn’t about delegates though, so if you want more information on their inner workings you should look for that elsewhere. This post is about defining and raising custom events, so let’s get on with that. The first thing we need is a class that will raise an event.

C#

public class Person
{
    private string _firstName;
    private string _lastName;
 
    public string FirstName
    {
        get
        {
            return this._firstName;
        }
        set
        {
            this._firstName = value;
        }
    }
 
    public string LastName
    {
        get
        {
            return this._lastName;
        }
        set
        {
            this._lastName = value;
        }
    }
}

VB

Public Class Person
 
    Private _firstName As String
    Private _lastName As String
 
    Public Property FirstName() As String
        Get
            Return Me._firstName
        End Get
        Set(ByVal value As String)
            Me._firstName = value
        End Set
    End Property
 
    Public Property LastName() As String
        Get
            Return Me._lastName
        End Get
        Set(ByVal value As String)
            Me._lastName = value
        End Set
    End Property
 
End Class

So, we have a class with two properties, which we can get and set. It might be convenient for us to receive a notification from an instance of this class when those property values change. For instance, if you are displaying the person’s name in some TextBoxes and the name gets changed in code, you’d want to know about it so that you could update the TextBoxes, right? For that we need an event to be raised when the property value changes.

The first thing to do is to declare the events as members in the Person class. Considering that these events are notifications of the FirstName and LastName property values being changed, it’s most appropriate that they be named “FirstNameChanged” and “LastNameChanged”, which is convention throughout the Framework.

C#

public event EventHandler FirstNameChanged;
public event EventHandler LastNameChanged;

VB

Public Event FirstNameChanged As EventHandler
Public Event LastNameChanged As EventHandler

Once the events are declared, you’ll find that the Person class now has events you can handle. That doesn’t do us any good if the events are never raised though, so let’s add some basic code to raise those events when the corresponding property values change.

C#

public string FirstName
{
    get
    {
        return this._firstName;
    }
    set
    {
        this._firstName = value;
 
        if (this.FirstNameChanged != null)
        {
            this.FirstNameChanged(this, EventArgs.Empty);
        }
    }
}
 
public string LastName
{
    get
    {
        return this._lastName;
    }
    set
    {
        this._lastName = value;
 
        if (this.LastNameChanged != null)
        {
            this.LastNameChanged(this, EventArgs.Empty);
        }
    }
}

VB

Public Property FirstName() As String
    Get
        Return Me._firstName
    End Get
    Set(ByVal value As String)
        Me._firstName = value
 
        RaiseEvent FirstNameChanged(Me, EventArgs.Empty)
    End Set
End Property
 
Public Property LastName() As String
    Get
        Return Me._lastName
    End Get
    Set(ByVal value As String)
        Me._lastName = value
 
        RaiseEvent LastNameChanged(Me, EventArgs.Empty)
    End Set
End Property

Note that, when the event is raised, the object passes itself as the first argument. The first argument is passed to the sender parameter, so the object is identifying itself as the sender, i.e. the object that raised the event.

The second argument is EventArgs.Empty, which is part of the standard pattern. You should be fairly used to your event handlers having a sender parameter of type Object and an e parameter of type EventArgs or something like it. The second parameter is the event’s data. The EventArgs class acts as a place-holder for events that don’t have any data and as a base class for the types used by events that do have data. Rather than create a new EventArgs object each time, we use the static/Shared Empty field, which returns an empty EventArgs object.

Now, the code we have will do the job but we can make it better. First of all, the event will be raised every time the corresponding property is set, whether the value actually changes or not. We should actually test the new value and only raise the event if it’s different to the old value.

C#

public string FirstName
{
    get
    {
        return this._firstName;
    }
    set
    {
        if (value != this._firstName)
        {
            this._firstName = value;
 
            if (this.FirstNameChanged != null)
            {
                this.FirstNameChanged(this, EventArgs.Empty);
            }
        }
    }
}
 
public string LastName
{
    get
    {
        return this._lastName;
    }
    set
    {
        if (value != this._lastName)
        {
            this._lastName = value;
 
            if (this.LastNameChanged != null)
            {
                this.LastNameChanged(this, EventArgs.Empty);
            }
        }
    }
}

VB

Public Property FirstName() As String
    Get
        Return Me._firstName
    End Get
    Set(ByVal value As String)
        If value <> Me._firstName Then
            Me._firstName = value
 
            RaiseEvent FirstNameChanged(Me, EventArgs.Empty)
        End If
    End Set
End Property
 
Public Property LastName() As String
    Get
        Return Me._lastName
    End Get
    Set(ByVal value As String)
        If value <> Me._lastName Then
            Me._lastName = value
 
            RaiseEvent LastNameChanged(Me, EventArgs.Empty)
        End If
    End Set
End Property

The next step is to implement the common pattern that is used throughout the Framework for raising events. That involves declaring a method whose purpose in life it is to raise the event.

C#

protected virtual void OnFirstNameChanged(EventArgs e)
{
    if (this.FirstNameChanged != null)
    {
        this.FirstNameChanged(this, e);
    }
}
 
protected virtual void OnLastNameChanged(EventArgs e)
{
    if (this.LastNameChanged != null)
    {
        this.LastNameChanged(this, e);
    }
}

VB

Protected Overridable Sub OnFirstNameChanged(ByVal e As EventArgs)
    RaiseEvent FirstNameChanged(Me, e)
End Sub
 
Protected Overridable Sub OnLastNameChanged(ByVal e As EventArgs)
    RaiseEvent LastNameChanged(Me, e)
End Sub

Such a method is used basically so that the event is only ever raised in one place. If you ever want to raise the event you simply call this method.

C#

public string FirstName
{
    get
    {
        return this._firstName;
    }
    set
    {
        if (value != this._firstName)
        {
            this._firstName = value;
            this.OnFirstNameChanged(EventArgs.Empty);
        }
    }
}
 
public string LastName
{
    get
    {
        return this._lastName;
    }
    set
    {
        if (value != this._lastName)
        {
            this._lastName = value;
            this.OnLastNameChanged(EventArgs.Empty);
        }
    }
}

VB

Public Property FirstName() As String
    Get
        Return Me._firstName
    End Get
    Set(ByVal value As String)
        If value <> Me._firstName Then
            Me._firstName = value
            Me.OnFirstNameChanged(EventArgs.Empty)
        End If
    End Set
End Property
 
Public Property LastName() As String
    Get
        Return Me._lastName
    End Get
    Set(ByVal value As String)
        If value <> Me._lastName Then
            Me._lastName = value
            Me.OnLastNameChanged(EventArgs.Empty)
        End If
    End Set
End Property

The primary advantage of this is linked to the way the method is declared. Notice that the methods above are declared protected/Protected and virtual/Overridable. This means that any derived classes can override these methods and change the type’s behaviour when the events are raised. The derived class simply calls the base implementation to raise the event, so code can be added before that call or after it to add new behaviour. This new behaviour will be invoked even if the method is called from the base class, such is the behaviour of overridden members. If the event was raised directly in the property setters of the base class then derived classes wouldn’t be able to add new behaviour because the properties are not declared virtual/Overridable.

It’s also worth noting that another part of the pattern is naming the method that raises an event the same as the event it raises, but with the “On” prefix added. Note that in .NET there is no OnLoad or OnClick event. The events are named Load and Click and the methods that raise them are name OnLoad and OnClick.

So, we now have a full, working implementation that follows the standard .NET pattern. We’ve declared the events, declared methods to raise them and then called those methods when the event notification is required. But wait; there’s more!

I said earlier that events may or may not have data associated with them. In the case of our FirstNameChanged and LastNameChanged events there is no data. Listeners are simply being notified that a property value has changed. If they want to know the new value they can simply get the property. As such an EventArgs object is used as a place-holder for the event handlers. Now let’s consider a situation where the event handlers will require some information that they cannot otherwise access themselves.

In that situation the object raising the event needs to pass that data to the event handler. It does this through the e parameter. In such cases the e parameter cannot be type EventArgs because the EventArgs class has no members that can store such data. As a result, we need to use a class that inherits EventArgs and then adds the required members. The Framework already contains numerous such class, e.g. MouseEventArgs and PaintEventArgs. You should use one of those existing classes if it’s appropriate to your event, otherwise you should define your own class.

For this example, let’s consider the situation where we want to notify listeners that the property value is going to change before it happens, in addition to notifying them that the property value has changed after it happens. If the property value hasn’t actually changed yet then there’s no way an event handler can get the new value, unless that data is passed to the event handler explicitly. For this we’ll define our own derived EventArgs class. We could define one for each event but they are both notifying about a String property changing so we can use the same class for both.

C#

public class StringPropertyChangingEventArgs : EventArgs
{
    private readonly string _proposedValue;
 
    public string ProposedValue
    {
        get
        {
            return this._proposedValue;
        }
    }
 
    public StringPropertyChangingEventArgs(string proposedValue)
    {
        this._proposedValue = proposedValue;
    }
}

VB

Public Class StringPropertyChangingEventArgs
    Inherits EventArgs
 
    Private ReadOnly _proposedValue As String
 
    Public ReadOnly Property ProposedValue() As String
        Get
            Return Me._proposedValue
        End Get
    End Property
 
    Public Sub New(ByVal proposedValue As String)
        Me._proposedValue = proposedValue
    End Sub
 
End Class

Note that the StringPropertyChangingEventArgs class inherits the EventArgs class and then adds a property for the new data we need: the proposed value of the property. There’s no need to include the current value of the property because the event handler can get that itself.

There are two more points to note here. The name of the class ends with “EventArgs”, which is part of the convention. Another is using the term “Changing” for an event related to a proposed change to a property value. This goes along with the convention of using “Changed” for an event related to a property that has changed already. For the events you would normally prefix the “Changing” with the name of the property. We can’t do that for this class though, because it’s to be used by more than one property/event.

Now that we have a type to pass the event data to the event handler, the next thing we need is an event. In the case of the FirstNameChanged and LastNameChanged events, we declared them as type EventHandler. That’s not possible for our FirstNameChanging and LastNameChanging events though because they will require a StringPropertyChangingEventArgs parameter rather than an EventArgs parameter, so their signatures will not match that of the EventHandler delegate. We need a different delegate. For this we have two choices. Firstly, we could define our own delegate with a signature that matches that of our event handlers. This is good practice if you’re exposing an event outside the current assembly. We’ll look at that option later but, if the event is only going to be used within the current project, it’s considered good practice to use the generic EventHandler(TEventArgs) delegate.

C#

public event EventHandler<StringPropertyChangingEventArgs> FirstNameChanging;
public event EventHandler<StringPropertyChangingEventArgs> LastNameChanging;

VB

Public Event FirstNameChanging As EventHandler(Of StringPropertyChangingEventArgs)
Public Event LastNameChanging As EventHandler(Of StringPropertyChangingEventArgs)

In this case the generic type of the delegate specifies the type of the second parameter of the event handler. Note that this type must be EventArgs or derived from EventArgs.

The next step is to declare methods to raise the events. They will be much as were those for the other events but with a different parameter type.

C#

protected virtual void OnFirstNameChanging(StringPropertyChangingEventArgs e)
{
    if (this.FirstNameChanging != null)
    {
        this.FirstNameChanging(this, e);
    }
}
 
protected virtual void OnLastNameChanging(StringPropertyChangingEventArgs e)
{
    if (this.LastNameChanging != null)
    {
        this.LastNameChanging(this, e);
    }
}

VB

Protected Overridable Sub OnFirstNameChanging(ByVal e As StringPropertyChangingEventArgs)
    RaiseEvent FirstNameChanging(Me, e)
End Sub
 
Protected Overridable Sub OnLastNameChanging(ByVal e As StringPropertyChangingEventArgs)
    RaiseEvent LastNameChanging(Me, e)
End Sub

All that remains is for us to call the methods to actually raise the events. Remember that these events are intended to notify our listeners that a property value is going to change, so they must be raised before the actual value changes.

C#

public string FirstName
{
    get
    {
        return this._firstName;
    }
    set
    {
        if (value != this._firstName)
        {
            this.OnFirstNameChanging(new StringPropertyChangingEventArgs(value));
            this._firstName = value;
            this.OnFirstNameChanged(EventArgs.Empty);
        }
    }
}
 
public string LastName
{
    get
    {
        return this._lastName;
    }
    set
    {
        if (value != this._lastName)
        {
            this.OnLastNameChanging(new StringPropertyChangingEventArgs(value));
            this._lastName = value;
            this.OnLastNameChanged(EventArgs.Empty);
        }
    }
}

VB

Public Property FirstName() As String
    Get
        Return Me._firstName
    End Get
    Set(ByVal value As String)
        If value <> Me._firstName Then
            Me.OnFirstNameChanging(New StringPropertyChangingEventArgs(value))
            Me._firstName = value
            Me.OnFirstNameChanged(EventArgs.Empty)
        End If
    End Set
End Property
 
Public Property LastName() As String
    Get
        Return Me._lastName
    End Get
    Set(ByVal value As String)
        If value <> Me._lastName Then
            Me.OnLastNameChanging(New StringPropertyChangingEventArgs(value))
            Me._lastName = value
            Me.OnLastNameChanged(EventArgs.Empty)
        End If
    End Set
End Property

That’s done but, really, what use is that event? It tells our listeners that the property value is about to change and what it’s about to change to, but to what use can that information be put? Normally, the reason you want to know that a property is about to change is so that you can abort the change if the value is unacceptable for some reason. That can’t be done in this case though, because the property value changes after the event has been handled no matter what.

The ability to cancel an action from an event handler already exists in the Framework. Consider the FormClosing event. You can ask the user for confirmation at that stage and, if they decide they don’t want to close the form, you simply set the e.Cancel property to True. This property is available because the e parameter is type CancelEventArgs. We can’t use CancelEventArgs for our methods though, because we need to provide the extra data consisting of the proposed property value. The solution is to have our StringPropertyChangingEventArgs class inherit CancelEventArgs instead of EventArgs. That way we get the Cancel property and we can add our own data to that.

C#

public class StringPropertyChangingEventArgs : CancelEventArgs
{
    private readonly string _proposedValue;
 
    public string ProposedValue
    {
        get
        {
            return this._proposedValue;
        }
    }
 
    public StringPropertyChangingEventArgs(string proposedValue)
    {
        this._proposedValue = proposedValue;
    }
}

VB

Public Class StringPropertyChangingEventArgs
    Inherits CancelEventArgs
 
    Private ReadOnly _proposedValue As String
 
    Public ReadOnly Property ProposedValue() As String
        Get
            Return Me._proposedValue
        End Get
    End Property
 
    Public Sub New(ByVal proposedValue As String)
        Me._proposedValue = proposedValue
    End Sub
 
End Class

The class name hasn’t changed so we don’t need to change any of the event and method declarations. We do, however, have to change the code in the property to handle the situation where the listener cancels the action. In that case the property value shouldn’t change.

C#

public string FirstName
{
    get
    {
        return this._firstName;
    }
    set
    {
        if (value != this._firstName)
        {
            StringPropertyChangingEventArgs e = new StringPropertyChangingEventArgs(value);
 
            this.OnFirstNameChanging(e);
 
            if (!e.Cancel)
            {
                this._firstName = value;
                this.OnFirstNameChanged(EventArgs.Empty);
            }
        }
    }
}
 
public string LastName
{
    get
    {
        return this._lastName;
    }
    set
    {
        if (value != this._lastName)
        {
            StringPropertyChangingEventArgs e = new StringPropertyChangingEventArgs(value);
 
            this.OnLastNameChanging(e);
 
            if (!e.Cancel)
            {
                this._lastName = value;
                this.OnLastNameChanged(EventArgs.Empty);
            }
        }
    }
}

VB

Public Property FirstName() As String
    Get
        Return Me._firstName
    End Get
    Set(ByVal value As String)
        If value <> Me._firstName Then
            Dim e As New StringPropertyChangingEventArgs(value)
 
            Me.OnFirstNameChanging(e)
 
            If Not e.Cancel Then
                Me._firstName = value
                Me.OnFirstNameChanged(EventArgs.Empty)
            End If
        End If
    End Set
End Property
 
Public Property LastName() As String
    Get
        Return Me._lastName
    End Get
    Set(ByVal value As String)
        If value <> Me._lastName Then
            Dim e As New StringPropertyChangingEventArgs(value)
 
            Me.OnLastNameChanging(e)
 
            If Not e.Cancel Then
                Me._lastName = value
                Me.OnLastNameChanged(EventArgs.Empty)
            End If
        End If
    End Set
End Property

This is as much as most people will usually need to do when it comes to custom events. There are a couple more points to consider though. As I said earlier, if your event is only going to be handled within your own project then it’s considered good practice to use the generic EventHandler(TEventArgs) delegate as your event’s type. If you’re exposing your event outside your assembly though, it’s considered good practice to declare your own delegate and declare your event as that type. This is in much the same vein as declaring properties that expose a collection. In that case, properties that will be accessible only within the assembly can use the generic List(T) class while, for properties exposed outside the assembly, you should declare your own custom collection type.

In this case we have two choices if we want to declare our own custom delegate. We can either declare a single delegate, just as we’ve declared a single EventArgs class, or declare a delegate for each event. Good practice dictates that we choose the latter.

C#

public delegate void FirstNameChangingEventHandler(object sender, StringPropertyChangingEventArgs e);
public delegate void LastNameChangingEventHandler(object sender, StringPropertyChangingEventArgs e);

VB

Public Delegate Sub FirstNameChangingEventHandler(ByVal sender As Object, ByVal e As StringPropertyChangingEventArgs)
Public Delegate Sub LastNameChangingEventHandler(ByVal sender As Object, ByVal e As StringPropertyChangingEventArgs)

Note that our delegates’ signatures match those of the methods that will be used to handle the events. Notice, also, that the names follow convention: the name of the event with an “EventHandler” suffix. Now we simply declare our events as these types instead of type Eventhandler(TEventArgs).

C#

public event FirstNameChangingEventHandler FirstNameChanging;
public event LastNameChangingEventHandler LastNameChanging;

VB

Public Event FirstNameChanging As FirstNameChangingEventHandler
Public Event LastNameChanging As LastNameChangingEventHandler

Finally, consider how our new cancellable event works. The caller sets the property and the appropriate “Changing” event is raised. The caller can either cancel the event, in which case the new property value is never committed and the corresponding “Changed” event is never raised, or else the event can be accepted, in which case the new property value is committed and the “Changed” event is raised.

That’s just what we want, but consider what happens if we have two event handlers for the same “Changing” event. Let’s say that the property is set and the “Changing” event is raised, invoking the first event handler. If e.Cancel is set to True in that event handler, what should happen? If the event has been cancelled then that should be the end of it, right? That’s not what will happen though. As it stands, all event handlers will be invoked no matter what. That means that the first event handler to get invoked might set e.Cancel to True, but then the second event handler can set it back to False again. That would mean that the property value change would be committed, even though it was cancelled by the first listener. It would depend on the circumstances but, more often than not, I would think that this would be undesirable behaviour.

To remedy this we need to create truly custom events, which means providing our own implementation to handle an event handler being added, an event handler being removed and the event being raised. The first step is to create a collection for our delegates so that we can loop through them and invoke each one individually rather than invoking them all as a group.

C#

private List<FirstNameChangingEventHandler> firstNameChangingHandlers = new List<FirstNameChangingEventHandler>();
private List<LastNameChangingEventHandler> lastNameChangingHandlers = new List<LastNameChangingEventHandler>();

VB

Private firstNameChangingHandlers As New List(Of FirstNameChangingEventHandler)
Private lastNameChangingHandlers As New List(Of LastNameChangingEventHandler)

Next we need to provide a custom implementation for our events that handles adding and removing event handlers.

C#

public event FirstNameChangingEventHandler FirstNameChanging
{
    add
    {
        this.firstNameChangingHandlers.Add(value);
    }
    remove
    {
        this.firstNameChangingHandlers.Remove(value);
    }
}
 
public event LastNameChangingEventHandler LastNameChanging
{
    add
    {
        this.lastNameChangingHandlers.Add(value);
    }
    remove
    {
        this.lastNameChangingHandlers.Remove(value);
    }
}

VB

Public Custom Event FirstNameChanging As FirstNameChangingEventHandler
    AddHandler(ByVal value As FirstNameChangingEventHandler)
        Me.firstNameChangingHandlers.Add(value)
    End AddHandler
 
    RemoveHandler(ByVal value As FirstNameChangingEventHandler)
        Me.firstNameChangingHandlers.Remove(value)
    End RemoveHandler
 
    RaiseEvent(ByVal sender As Object, ByVal e As StringPropertyChangingEventArgs)
        For Each handler As FirstNameChangingEventHandler In Me.firstNameChangingHandlers
            handler(sender, e)
            If e.Cancel Then Exit For
        Next
    End RaiseEvent
End Event
 
Public Custom Event LastNameChanging As LastNameChangingEventHandler
    AddHandler(ByVal value As LastNameChangingEventHandler)
        Me.lastNameChangingHandlers.Add(value)
    End AddHandler
 
    RemoveHandler(ByVal value As LastNameChangingEventHandler)
        Me.lastNameChangingHandlers.Remove(value)
    End RemoveHandler
 
    RaiseEvent(ByVal sender As Object, ByVal e As StringPropertyChangingEventArgs)
        For Each handler As LastNameChangingEventHandler In Me.lastNameChangingHandlers
            handler(sender, e)
            If e.Cancel Then Exit For
        Next
    End RaiseEvent
End Event

Notice that now, when a handler is added for the event, we store it in our own collection. We remove the handler from the collection when it’s removed from the event as well. In the case of VB, we also add extra code to control what happens when we call RaiseEvent. That allows the VB code that raises the event to remain unchanged, while the C# code that raises the event must provide the extra functionality for allowing the event to be cancelled before all event handlers have been executed.

C#

protected virtual void OnFirstNameChanging(StringPropertyChangingEventArgs e)
{
    foreach (FirstNameChangingEventHandler handler in this.firstNameChangingHandlers)
    {
        handler(this, e);
        if (e.Cancel) break;
    }
}
 
protected virtual void OnLastNameChanging(StringPropertyChangingEventArgs e)
{
    foreach (LastNameChangingEventHandler handler in this.lastNameChangingHandlers)
    {
        handler(this, e);
        if (e.Cancel) break;
    }
}

VB

Protected Overridable Sub OnFirstNameChanging(ByVal e As StringPropertyChangingEventArgs)
    RaiseEvent FirstNameChanging(Me, e)
End Sub
 
Protected Overridable Sub OnLastNameChanging(ByVal e As StringPropertyChangingEventArgs)
    RaiseEvent LastNameChanging(Me, e)
End Sub

In both cases, raising the event now consists of looping through the registered event handlers one by one. As soon as one of the event handlers cancels the event, no more event handlers are executed.

That’s everything. We’ve covered declaring our own events that have no data, defining our own custom EventArgs class, declaring events that use that custom class using the generic EventHandler(TEventArgs) delegate as well as our own custom delegates, passing data to the event handler and back again and, finally, defining custom events that provide their own implementation for adding and removing event handlers as well as raising the event itself. There’s now nothing you can’t do with events of your own. Here’s hoping you have an eventful future. ;-)