Events in C#

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 60

C# Event Implementation Fundamentals, Best Practices

and Conventions

Introduction

Everything you need to define, implement, and understand custom events using C# is
presented in this article. Toward accomplishing these objectives the fundamental building
blocks that must or should be utilized are presented, in addition to event implementation
best practices and conventions. This article presents both .NET 1.x and 2.0+ alternatives for
publishing and subscribing to events.

While support for the implementation of custom events has been available since the 1.0
version of the .NET Framework, additional event-related support and capabilities have been
added since then. Some of the new capabilities (e.g., generic System.EventHandler,
anonymous methods, delegate inference, etc.) comprise shortcuts intended to make the
implementation of events easier. While such techniques do facilitate quicker event
implementations, presenting them before or in place of the fundamental building blocks
would yield a less explicit presentation. Consequently, this article avoids such shortcuts until
after the fundamental building blocks have been introduced.

Contents

1. Assumptions About the Audience


2. Terminology and Definitions
3. Delegates
o 3.1 Definition and Usage of Delegates
o 3.2 Why Delegates?
o 3.3 Delegate Internals
o 3.4 Delegates Are All The Same (there are no fundamentally differing types of
delegates)
4. The Relationship Between Delegates and Events
o 4.1 Event Handlers (in general)
o 4.2 The Non Generic System.EventHandler Delegate
o 4.3 The Generic System.EventHandler<TEventArgs> Delegate
5. Event Arguments (EventArgs)
o 5.1 The Role of System.EventArgs
o 5.2 Extending System.EventArgs
o 5.3 The role of System.ComponentModel.CancelEventArgs
6. Event Declaration Syntax
o 6.1 Event Declaration Syntax Alternatives
o 6.2 Considerations For Choosing Between Field-like Syntax and Property-like
Syntax
o 6.3 Publish/Subscribe mechanism Using Delegates Without Events (never do
this)
7. Event Raising Code
8. Event Subscriber Registration and Unregistration
o 8.1 Registering A Subscriber
o 8.2 Unregistering A Subscriber
9. Event Handling Method
10. .NET 1.x vs. 2.0+ Considerations
o 10.1 Generics
o 10.2 Delegate Inference
o 10.3 Anonymous Methods
o 10.4 Partial Classes
11. Conventions
o 11.1 Event Publisher Conventions
o 11.2 Event Subscriber Conventions
o 11.3 Naming Conventions
12. Steps to Creating Custom Events
o 12.1 Prepare the Event Publisher
o 12.2 Prepare the Event Subscriber
13. Sample Event Implementation
o 13.1 Sample Event Publisher Code
o 13.2 Sample Event Subscriber Code
14. Handling Events Raised by .NET Framework Components - Walkthrough and Example
o 14.1 FileSystemWatcher 'Deleted' Event Implementation
o 14.2 Handling the FileSystemWatcher.Deleted Event
15. Windows Forms Events
o 15.1 NET 1.x and 2.0+ Difference - Partial Classes
o 15.2 Partial Classes and Windows Forms Designer Considerations For Events
o 15.3 Walkthrough - Handling a Windows Forms Event
o 15.4 Windows Forms and Threading Considerations
16. Cancellable Events
17. ASP.NET Web Forms Events
18. Sources
19. History

1. Assumptions About the Audience

This article assumes a working knowledge of .NET programming with C#, in addition to an
understanding of Generics, which were introduced in the 2.0 version of the .NET
Framework. If you do not understand Generics, this article can still be helpful as there are
ways to implement events that do not rely on Generics. Both generic and non-generic event
implementation techniques are presented in this article.

2. Terminology and Definitions

The literature presenting events and related concepts frequently makes use of multiple
words or expressions to describe any given concept. The following list catalogs much of this
terminology with brief explanations of the concepts behind the expressions.

event, pre-event, post-event, and state, change of state, and expected change of
state

The term, event, typically means that a change in state of an object has occurred or is
about to occur. The term is also used in reference to some activity taking place within an
object or application � activity like the processing of a gesture from an end user (e.g.,
button clicked), or the reporting of progress during a long-running task.

The term, "state," refers to the current set of values of one or more variables in an object or
application. A change in state means the value of one or more variables within an object has
changed. In the event notification process, changes in state, or expected changes in state,
are primary motivations for raising events. So, we have two ways to define an event
relative to a change in state: immediately prior to a change in state, or immediately after a
change in state. While the former are referred to as pre-events, the latter are referred to as
post-events.

Post-events announce that the change in state has already occurred, and pre-events
announce the fact that a change in state is about to occur. Pre-events can be implemented
as cancellable � meaning that the subscriber may cancel the event before the change in
state occurs, thereby preventing the change in state from occurring, or preventing the
further processing of a long-running task.

event publisher, event source, subject

These are the classes or objects of which their state is of interest to other classes or
objects. Event publishers maintain their internal state, and notify other classes (subscribers)
through the raising of events or similar notification mechanisms.

event subscriber, sink, listener, observer

These are the classes or objects that are interested in changes in state (or expected
changes in state) of the event publishers. These terms refer to the classes or objects that
typically perform some action in response to the occurrence of an event.

raise, fire, or trigger an event; notification, or event notification


Event notifications (frequently expressed as, "fire an event" or "raise an event" or "trigger
an event") are generally in the form of the event publisher calling a method in one or more
subscribers. Consequently, the raising of an event ultimately means that code in the event
publisher causes code in one or more subscribers to run.

In cases where no subscribers [to an event] have registered with the publisher, the event
would not be raised.

Please note that in this article, events are described as "raised" (not "fired" or "triggered").
This convention comes from the team of developers who authored much of the .NET
Framework (Cwalina and Abrams, 2006). They prefer the term, "raise," because it doesn't
have the negative connotations of the expressions, "fire" or "trigger."

event data, event-related data, and event arguments ("event args")

When an event is raised, the publisher will frequently include data that gets sent to the
subscribers through the event notification process. This data is presumably relevant to the
particular event that was raised, and would be of interest to the event subscribers.

For example, an event can be raised when a file gets renamed. Data relevant to that
particular "file renamed" event could include (1) the name of the file before the name was
changed, and (2) the name of the file after the name was changed. Those file names could
comprise the event data that are sent to the subscribers during the raising of the "file
renamed" event.

Delegate Type, Delegates

A clear understanding of the .NET Delegates type is crucial to the understanding of events
as implemented in the .NET Framework. Consequently, much of this article is dedicated to
explaining the relationship between delegates and events.

Two Meanings of "Event Handler"

The literature outside of this article frequently uses the term, "event handler," in reference
to either (1) the delegate upon which an event is defined (in the publisher), or (2) any
method registered with the event (in the subscriber). Furthermore, Intellisense in Visual
Studio refers to an event handling method (in the subscriber) as simply, "handler." For
purposes of clarity, this article uses the expression, "event handler," in reference to the
delegate, while using the expression,"event handling method," in reference to any method
registered with an event.

To summarize; an "event handler" is the delegate upon which an event is based, while an
"event handling method" is a method called in the subscriber when an event is raised.
Event handlers are delegates, although delegates are not necessarily event handlers (there
are many uses of delegates beyond supporting events). Delegates are presented in more
detail later in this article, but only to the extent that they are relevant to events.

.NET events and the GoF Observer pattern

Events, as implemented in the .NET Framework and as described in this article, constitute
a .NET optimized implementation of the Observer Pattern that was documented by the
"Gang of Four" or "GoF" (Gamma et al.1995). The .NET mechanisms used to implement
events (delegates in particular) substantially reduce the amount of work required to
implement the Observer pattern in .NET applications.

3. Delegates

In order to understand events, as implemented in .NET applications, one must have a clear
understanding of the .NETdelegate type and the role it plays in the implementation of
events.

3.1 Definition and Usage of Delegates

Delegates can be understood as intelligent containers that hold references to methods, as


opposed to containers that hold references to objects. Delegates can contain references to
zero, one, or many methods. In order for a method to be called by a particular delegate
instance, that method must be registered with the delegate instance. When registered, the
method is added to the delegate's internal collection of method references (the delegate's
"invocation list"). Delegates can hold references to static methods or instance methods in
any class visible to the delegate instance. Delegate instances can call their referenced
methods either synchronously, or asynchronously. When called asynchronously, the
methods execute on a separate thread pool thread. When a delegate instance is invoked
("called"), then all methods referenced by the delegate are called automatically by the
delegate.

Delegates cannot contain references to just any method. Delegates can hold
references only to methods defined with a method signature that exactly matches the
signature of the delegate.

Consider the following delegate declaration:

 Collapse
public delegate void MyDelegate(string myString);

Notice that the delegate declaration looks like a method declaration, but with no method
body.
The signature of the delegate determines the signature of methods that can be referenced
by the delegate. So, the sample delegate above (MyDelegate) can hold references only to
methods that return void while accepting a single string argument. Consequently, the
following method can be registered with an instance of MyDelegate:

 Collapse
private void MyMethod(string someString)
{
// method body here.

The following methods, however, cannot be referenced by a MyDelegate instance because


their signatures do not match that of MyDelegate.

 Collapse
private string MyOtherMethod(string someString)
{
// method body here.

private void YetAnotherMethod(string someString, int someInt)


{
// method body here.

After a new delegate type is declared, an instance of that delegate must be created so that
methods can be registered with, and ultimately invoked by, the delegate instance.

 Collapse
// instantiate the delegate and register a method with the new instance.

MyDelegate del = new MyDelegate(MyMethod);

After a delegate is instantiated, additional methods can be registered with the delegate
instance, like this:

 Collapse
del += new MyDelegate(MyOtherMethod);

At this point, the delegate can be invoked, like this:

 Collapse
del("my string value");

And, because both MyMethod and MyOtherMethod are registered with


the MyDelegate instance (named del), that instance will invoke
both MyMethod and MyOtherMethod when the above line executes, passing each the string
value, "my string value."
Delegates and Overloaded Methods

In the case of an overloaded method, only the particular overload having a signature that
exactly matches the signature of the delegate can be referenced by (or registered with) the
delegate. When you write code that registers an overloaded method with a delegate
instance, the C# compiler will automatically select and register the particular overload with
a matching signature.

So, for example, if your application declared the following delegate type...

 Collapse
public delegate int MyOtherDelegate(); // returns int, no parameters

... and you registered an overloaded method named MyOverloadedMethod with an


instance ofMyOtherDelegate, like this...

 Collapse
anotherDel += new MyOtherDelegate(MyOverloadedMethod);

... the C# compiler will register only the particular overload with a matching signature. Of
the following two overloads, only the first would be registered with
the anotherDel instance of the MyOtherDelegate type:

 Collapse
// requires no parameters - so can be registered with a MyOtherDelegate

// instance.

private int MyOverloadedMethod()


{
// method body here.

}
// requires a string parameter - so cannot be registered with a MyOtherDelegate
instance.

private int MyOverloadedMethod(string someString)


{
// method body here.

A single delegate cannot selectively register or call both (multiple) overloads. If you need to
call both (multiple) overloads, then you would need additional delegate types � one
delegate type per signature. Your application-specific logic would then determine which
delegate to invoke, and therefore which overload is called (by the delegate with the
corresponding signature).

3.2 Why Delegates?


If this is your first introduction to delegates, you may be wondering, "Why bother? It's just
simpler to call the method directly � so what is the benefit of going through a delegate?"

Necessary Indirection

A brief answer (to the "why bother?" question above) is that the code we write or
components we use cannot always "know" which specific method to call at a particular point
in time. So, one important perspective of delegates is that they provide a way for .NET
components to call your code � without the .NET component having to know anything
about your code beyond the method signature (as mandated by the delegate type). For
example, .NET Framework components, like the Timer component, frequently need to
execute code that you write. Because the Timer component cannot possibly know which
specific method to call, it specifies a delegate type (and therefore signature of a method) to
be invoked. Then you connect your method � with the requisite signature � to the Timer
component by registering your method with a delegate instance of the delegate type
expected by the Timer component. The Timer component can then run your code by
invoking the delegate which, in turn, calls your method. Note that the Timer component still
knows nothing about your specific method. All the Timer component knows about is the
delegate. The delegate, in turn, knows about your method because you registered your
method with that delegate. The end result is that the Timer component causes your method
to run, but without knowing anything about your specific method.

Just like the Timer component example above, we can make use of delegates in a way that
enables us to write our code without our code having to "know" the specific method that will
ultimately be called at a specific point. Rather than calling a method at that point, our code
can invoke a delegate instance � which, in turn, calls any methods that are registered with
the delegate instance. The end result is that a compatible method is called even though the
specific method to be called was not written directly into our code.

Synchronous and Asynchronous Method Invocation

All delegates inherently provide for both synchronous and asynchronous method invocation.
So, another common reason to call methods via delegate instances is to invoke methods
asynchronously � in which case the called method runs on a separate thread pool thread.

Event Foundation

As you'll see later in this article, delegates play an integral role in the implementation of
events in the .NET Framework. In brief, delegates provide a necessary layer of indirection
between event publishers and their subscribers. This indirection is necessary in order to
maintain a clean separation between the publisher and subscriber(s) � meaning that
subscribers can be added and removed without the publisher needing to be modified in any
way. In the case of event publication, the use of a delegate makes it possible for an event
publisher to know nothing about any of its subscribers while still broadcasting events and
associated event data to any/all subscribers.
Other Uses

Delegates serve important roles in .NET applications beyond those already listed. Those
other roles will not be further presented here because the intent of this article is to focus
only on the foundational role that delegates serve in the implementation of events in .NET
applications.

3.3 Delegate Internals

Declaring a Delegate Results in A New Class Being Created

A delegate declaration that you write is sufficient to define an entire and new delegate class.
The C# compiler takes your delegate declaration and inserts a new delegate class in the
output assembly. The name of that new class is the name of the delegate type you supply in
your delegate declaration. The signature you specify in your delegate declaration becomes
the signature of the methods in the new class used to call any/all of the delegate's
referenced methods (specifically the Invoke and BeginInvoke methods). This new class
extends (inherits)System.MulticastDelegate. So most of the methods and properties
available in your new delegate class come from System.MulticastDelegate.
The Invoke, BeginInvoke, and EndInvoke methods are inserted by the C# compiler
when it creates the new class in the output assembly (these are the methods you can call to
cause the delegate to invoke any/all referenced methods � Invoke for synchronous
invocation, and BeginInvoke andEndInvoke used in asynchronous invocations).

The new class created from your delegate declaration can be understood as being a
completed and full-blownMulticastDelegate implementation that has the type name you
supplied in your delegate declaration, and is capable of calling methods with the specific
signature that you also supplied in your delegate declaration.

As an example, when the C# compiler encounters the following delegate declaration...

 Collapse
public delegate string MyFabulousDelegate(int myIntParm);

... the compiler inserts a new class named MyFabulousDelegate into the output


assembly. The Invoke,BeginInvoke, and EndInvoke methods of
the MyFabulousDelegate class include the int parameter and returned string value in
their respective method signatures.

It should be noted that MulticastDelegate is a special class in that compilers can derive


from it, but you cannot derive from it explicitly. Your use of the C# delegate keyword and
associated syntax is how you instruct the C# compiler to extend MulticastDelegate for
your purposes.

Meaning of Multicast
The meaning of "multicast" in System.MulticastDelegate is that the delegate is capable
of holding references to multiple methods � not just one method. In the case of delegate
instances that hold references to multiple methods, all referenced methods are called when
the delegate instance is invoked.

Delegates are Immutable

Delegate instances are immutable � meaning that once a delegate instance is created, it
cannot be modified. So, when you register a method with a delegate, what is happening is
that a new delegate instance is created that includes the additional method in its invocation
list. If you unregister a method from a delegate instance, a new delegate instance is
returned that has the unregistered method omitted from its invocation list. If you were to
create a new object variable of a particular delegate type, then set it equal to an existing
delegate instance (of that particular type), you would get a complete and separate copy of
the delegate. Modifications to the copy (e.g., registering an additional method) would affect
only the copy. The invocation list of the original instance would remain unchanged.

Delegates are not Function Pointers

Finally, C and C++ programmers will recognize that delegates are similar to C-style function
pointers. An important difference, though, is that a delegate is not simply a pointer to a raw
memory address. Instead, delegate instances are type-safe objects that are managed by
the .NET CLR and that specifically reference one or more "methods" (as opposed to memory
addresses).

3.4 Delegates Are All The Same (there are no fundamentally differing types
of delegates)

These statements are all true:


"If you've seen one delegate, you've seen them all."
or
"All delegates are created equal."
or
"A delegate is a delegate is a delegate."

When you read about different "types" of delegates, you should understand that, internally,
all delegates are the same. This is true for delegates provided by the .NET Framework and
for delegates you create for your own purposes. To say "they are all the same" specifically
means that all delegates (1) inherit fromSystem.MulticastDelegate, which in turn
inherits from System.Delegate; and (2) provide the same set of members, including
the Invoke, BeginInvoke, and EndInvoke() methods, etc.

What differentiates delegate types is nothing more than:

1. The type name of the delegate.


2. The signature of the delegate � including return type and number and types of
parameters.
3. Intended usage or role of the delegate.

Take, for example, the generic Predicate delegate (System.Predicate<T>). Here is what
makes it a "Predicate delegate":

1. The type name: Predicate
2. The signature: returns bool, accepts a single object typed parameter for which
the type, being generic, can be specified at design time.
3. Intended usage or role: this delegate will reference a method that defines a set of
criteria and determines whether the specified object meets those criteria.

Beyond the type name, signature, and intended usage, the Predicate<T> delegate has the
same set of members that any other delegate has, including Invoke, BeginInvoke, etc.
Consequently, this is what is meant by the statement, "delegates are all the same."

To be clear, it is not the case that the Predicate<T> delegate has any additional methods


or properties that help it to fulfill its intended role. If some delegates had properties or
methods that other delegates do not have, then those delegates would have different or
unique capabilities and we therefore would not be able to say they are all the same.

Regarding the intended usage perspective; you are free to use any delegate for
purposes not intended by the delegate's creators � as delegates are not tied to any
particular usage. You could, for example, use aPredicate<T> delegate to call any method
that returns bool and accepts a single object typed parameter � even if those methods
do not determine whether the specified object meets any criteria (which is the intended
usage of the Predicate<T> delegate). Granted, you should not use delegates for
purposes other than those which they are intended to serve, as much of the value in
the .NET Framework providing pre-built delegates (like Predicate<T>) is that we can
understand the role they play without having to dig through a bunch of code to find out
what they are actually doing.

The name of a delegate type communicates its intended role in your code. So be sure to use
the appropriate delegate type, or create your own with an informative type name, even if
another delegate with the requisite signature � but a potentially misleading name given
your particular usage � is available.

4. The Relationship Between Delegates and Events

Events in .NET programming are based on delegates. Specifically, an event can be


understood as providing a conceptual wrapper around a particular delegate. The event then
controls access to that underlying delegate. When a client subscribes to an event, the event
ultimately registers the subscribing method with the underlying delegate. Then, when the
event is raised, the underlying delegate invokes each method that is registered with it (the
delegate). In the context of events, then, delegates act as intermediaries between the code
that raises events and the code that executes in response � thereby decoupling event
publishers from their subscribers.

Events do not, by themselves, maintain a list of subscribers. Instead, events control access
to some underlying list of subscribers � and that list is typically implemented as a delegate
(although other list-type objects or collections can serve in place of a delegate).

4.1 Event Handlers (in general)

A delegate that exists in support of an event is referred to as an "event handler". To be


clear, an "event handler" is a delegate, although delegates are frequently not event
handlers.

Unfortunately, many authors writing about events use the term, "event handler", in
reference to both (1) the delegate upon which an event is based, and (2) a method called
by the delegate when the event is raised. In order to avoid confusion resulting from this
state of affairs, this article uses the expression, "event handler," only in reference to the
delegate, while using the expression, "event handling method," in reference to any method
registered with the delegate.

Custom Event Handlers

You can define your own event handlers (delegates), or you can use one of the event
handlers provided by the .NET Framework (i.e., System.EventHandler, or the
generic System.EventHandler<TEventArgs>). The following sample event declaration
make use of a custom event handler rather than using a Framework-provided event
handler.

Consider the following:

 Collapse
Line 1: public delegate void MyDelegate(string whatHappened);
Line 2: public event MyDelegate MyEvent;

Line 1 declares a delegate type for which any method can be assigned � provided that the
method returns void and accepts a single string argument:

 public � scope specifying that objects outside of our class can reference the
delegate. If the delegate type is declared within the event publishing class, then it
will need to be publicly scoped so that event subscribers can see it and declare
instances of it with which to register their event handling methods (more on this
later).
 delegate � keyword used to declare custom delegates in the .NET Framework.
 void � return type. This is part of the delegate signature, and therefore the return
type that registering methods must specify.
 MyDelegate � type name of the delegate.
 (string whatHappened) � the rest of the signature. Any method that registers
with the event must accept a single string argument (in addition to
returning void).

Line 2 declares an event in terms of the delegate type. Notice that the event (which is
named MyEvent) is declared very much like a method declaration � but with its data type
specified as the delegate type:

 public � scope specifying that objects outside of our class can subscribe to the
event.
 event � keyword used to define the event.
 MyDelegate � data type of the event (this is the custom delegate type defined in
Line 1.)
 MyEvent � name of the event.

The delegate declared in Line 1 is just an ordinary delegate (as are all delegates), and can
be used for any purpose delegates can fulfill. Line 2 (i.e., the usage of the delegate type) is
what turns that delegate into an event handler. In order to communicate that a particular
delegate type is being used as an event handler, a naming convention has emerged
whereby the delegate type name ends with "Handler" (more on this later).

Standardized Event Handlers

While you can create your own event handlers (and sometimes you might need to), you
should use one of theEventHandler delegates provided by the .NET Framework in cases
where one of the Framework's event handlers would work with your particular event
implementation. Many events make use of event handlers that can have common or
identical signatures. So, rather than clutter your source code with many delegates that
differ only by type name, you can/should make use of the built-in event handlers, as doing
so reduces the amount of code you would need to write and maintain, and makes your code
more easily understood. If someone reading your code sees you are basing an event on
the System.EventHandler delegate, for example, then they automatically know a lot
about your event implementation without having to look further.

4.2 The Non Generic System.EventHandler Delegate

Available in version 1.x of the .NET Framework, the non


generic System.EventHandler delegate enforces the convention (described in greater
detail below) of event handlers returning no value while accepting two parameters, the first
being an object-typed parameter (to hold a reference to the class raising the event) and a
second parameter of type System.EventArgs or a subclass thereof (to hold any event
data). System.EventArgs is presented later.
This is how the .NET Framework declares the System.EventHandler delegate.

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

4.3 The Generic System.EventHandler<TEventArgs> Delegate

Available since the 2.0 version of the .NET Framework, the


generic System.EventHandler delegate enforces the same signature convention as
enforced by the non generic version � but accepts a generic type parameter for the
second, System.EventArgs, parameter.

The declaration of this built-in delegate enforces the constraint that the type, TEventArgs,
be of typeSystem.EventArgs (including, of course, subclasses thereof):

 Collapse
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e)
where TEventArgs : EventArgs;

Now suppose you want to strongly type the sender, rather than having it typed as object.
You can leverage generics to create your own generic event handler:

 Collapse
public delegate void MyGenericEventHandler<T, U>(T sender,
U u) where U : EventArgs;

You can then use this custom generic event handler to additionally specify a type-
safe sender parameter (i.e., thereby limiting the type of object that can be communicated
as having raised the event):

 Collapse
public event MyGenericEventHandler<MyPublisher, MyEventArgs> MyEvent;

The intent here would be that this event will only be raised by objects of
type MyPublisher. Subscribers to the event would therefore be able to subscribe only to
events published by the MyPublisher class.

5. Event Arguments (EventArgs)

Event arguments � sometimes referred to as "event args" � constitute the data sent by
the publisher of an event to the subscribers during the raising of the event. Presumably this
data is relevant to the occurrence of the event. For example, when a "file was just deleted"
event is raised, the event arguments would likely include the name of the file before the
name change, as well as the name of the file after the name was changed. The event
handling methods can read the event arguments (referred to as "event data") to learn more
about the event occurrence.
5.1 The Role of System.EventArgs

You have two basic alternatives for including event arguments with your events.

1. You can encapsulate all event arguments as properties of a class that derives
from System.EventArgs. At runtime, an instance of that class is then sent to the
event subscribers when the event is raised. The event subscribers read the event
arguments as properties of that class.
2. You can avoid the use of System.EventArgs and, instead, declare individual event
arguments � much as you would include arguments in a method declaration. This
approach is discouraged for reasons described in section 5.2.

The first alternative listed above is strongly encouraged, and support for it is built into
the .NET Framework through the System.EventArgs class. Events implemented in .NET
Framework components, by convention, provide their event arguments as
instances System.EventArgs, or as event-specific subclasses of System.EventArgs.

Some events carry no data. In these cases, System.EventArgs is used as a placeholder,


primarily for purposes of keeping one consistent event handler signature across all events
regardless of whether the events carry data or carry no data. In cases of events with no
data, the event publisher sends the value, System.EventArgs.Empty, during the raising
of the event.

5.2 Extending System.EventArgs

The existence of System.EventArgs and its recommended uses are in support of the


event implementation conventions. It is certainly possible for a publisher of an event to
specify event data without making use ofSystem.EventArgs or any subclass thereof. In
such cases, the delegate signature could specify each parameter type and name. The
problem with this approach, however, is that such a signature ties the event publisher with
all subscribers. If you want to modify the parameters in the future, then all subscribers
would have to be modified as well. It is therefore recommended to encapsulate all event
data in a subclass of System.EventArgs, as doing so reduces the amount of work required
to subsequently change the number and types of values sent to event subscribers.

To illustrate the tradeoffs involved in sending a System.EventArgs subclass instance vs.


sending individual event arguments, consider a scenario in which you want to add a
single string value to the event data. If you were to specify your event data as individual
parameters in the delegate signature (rather than subclassingSystem.EventArgs), then all
subscribers to your event would have to be modified to accept the
additionalstring parameter. Even subscribers that do not care about this
additional string value would have to be modified to accept it, as the custom event
handler signature would be changed. If you had, instead, subclassed System.EventArgs,
then all you would have to do is add a new string property to your class. The event
signature would not change, and therefore neither would the event handler signature used
in any of the existing subscribers. Subscribers that do not care about the
new string property would not have to be touched because the event handler signature
would not be changed � and they could simply ignore the additional string property.
Subscribers that do care about the new string value would be able to read it as a property
of the EventArgssubclass.

Here is an example of an EventArgs subclass that encapsulates a single string value:

 Collapse
public class FileDeletedEventArgs : System.EventArgs
{
// Field

string m_FileName = string.empty;

// Constructor

FileDeletedEventArgs(string fileName)
{
m_FileName = fileName;
}

// Property

public string FileName


{
get { return m_FileName; }
}
}

5.3 The Role of System.ComponentModel.CancelEventArgs

System.ComponentModel.CancelEventArgs is derived from System.EventArgs, and


exists in support of cancellable events. Beyond the members provided
by EventArgs, CancelEventArgs provides the BooleanCancel property that, when set
to true by an event subscriber, is used by the event publisher to cancel the event.

Section 16 of this article presents cancellable events in greater detail (click here to go there
now).

6. Event Declaration Syntax

6.1 Event Declaration Syntax Alternatives

The event keyword is used to formally declare an event. There are two valid event
declaration syntax alternatives. Regardless of the syntax you write, the C# compiler will
translate both property declarations into the following three components in the output
assembly.
1. Privately scoped event handler (or a functionally equivalent data structure). The
delegate is privately scoped in order to prevent external code from invoking the
event, and thereby preserving encapsulation.
2. publicly scoped Add method; used to add subscribers to the private event handler.
3. publicly scoped Remove method used to remove subscribers from the private event
handler.

1. Field-like syntax
 Collapse
public event TheEventHandler MyEvent;

The field-like syntax declares the event in one or two lines of code (one line for the event,
another for the associated event handler � if/when not using a built-
in EventHandler delegate).

2. Property-like syntax
 Collapse
public event TheEventHandler MyEvent
{
add
{
// code here adds the incoming delegate instance to underlying list of

// event handlers

}
remove
{
// code here removes the delegate instance from the underlying list of

// event handlers

}
}

The property-like syntax appears very similar to a typical property declaration, but with
explicit add and removeblocks in place of "getter" and "setter" blocks. Instead of retrieving
or setting the value of a private member variable, they add and remove incoming delegate
instances to/from the underlying event handler or other data structure that servers a similar
role.

Threading Considerations

The field-like syntax is automatically thread safe:

 Collapse
public event FileDeletedHandler FileDeleted;

The property-like syntax will be as thread safe as you make it. The following is a thread safe
version:
 Collapse
private readonly object padLock = new object();

public event System.EventHandler<filedeletedeventargs />FileDeleted


{
add
{
lock (padLock)
{
FileDeleted += value;
}
}
remove
{
lock (padLock)
{
FileDeleted -= value;
}
}
}

You can omit the lock{} blocks and padLock variable declaration if thread safety is of no


concern.

6. 2 Considerations For Choosing Between Field-like Syntax and Property-


like Syntax

When choosing amongst the syntax alternatives, consider that the property-like syntax
gives you more control over your event implementation than is available with the field-like
syntax. While the field-like syntax will be compiled into IL that very much resembles the IL
generated for the property-like syntax, the field-like syntax does not afford you the same
opportunities to explicitly control the event implementation.

Using the property-like syntax enables you to more carefully control the registration and
unregistration of subscribers with the event handler (delegate). It also enables you to more
easily and explicitly implement the specific locking mechanisms of your choice to address
thread safety concerns. The property-like syntax additionally enables you to implement a
custom event handler mechanism other than a delegate. You might want to do this in
scenarios where you want to support many possible events, only a few of which would have
any subscribers at any given point in time. In such a scenario, your event implementation
would use a hash table or similar data structure, rather than an individual delegate, to
maintain a list of all possible events and any associated listeners.

6.3 Publish/Subscribe Mechanism Using Delegates Without Events (never


do this)

It should be clearly understood that events are not delegates � even though events are
very much dependent upon delegates, and in some ways can be seen as a form of a
delegate implementation. Events are also not delegate instances, even if they can be used
in very similar ways.
While you can omit the event keyword (and therefore the formal declaration of the event)
and simply use a public delegate to provide a publish-and-subscribe notification mechanism,
you should never do so. The problem with public delegates (as compared to event
declaration), is that methods outside of the publishing class can cause publicly scoped
delegates to invoke their referenced methods. This violates basic encapsulation principles
and can be the source of major problems (race conditions, etc) that may be difficult to
debug. Consequently you should implement events only through the use of
the event keyword. When delegates are implemented in support of events, the delegate �
even if declared as a public member of the defining class � can only be invoked from within
the defining class (via the raising of the event), and other classes can only subscribe to and
unsubscribe from the underlying delegate via the event.

7. Event Raising Code

For each event, the publisher should include a protected virtual method that is responsible
for raising the event. This will allow subclasses to [more] easily access base class events. Of
course the recommendation to make this method protected and virtual applies only to non
static events in unsealed classes.

 Collapse
protected virtual void OnMailArrived(MailArrivedEventArgs)
{
// Raise event here

Once an event and any associated delegate and publishing method have been defined, the
publisher will need to raise the event. Raising the event should generally be a two step
process. The first step would be to check to see if there are any subscribers. The second
step is to raise the event, but only if there are any subscribers.

If there are no subscribers, then the delegate will test to null. The following logic raises
the event, but only if the event has any subscribers.

 Collapse
if (MyEvent != null)
{
MyEvent(this, EventArgs.Empty);
}

There is a possibility that the event could be cleared (by code executing in another thread)
between the test fornull and the line that actually raises the event. This scenario
constitutes a race condition. So it is recommended to create, test, and raise a copy of the
event's event handler (delegate), like this:

 Collapse
MyEventHandler handler = MyEvent;
if (handler != null)
{
handler (this, EventArgs.Empty)
}

Any unhandled exceptions raised in the event handling methods in subscribers will be
propagated to the event publisher. The raising of the event should therefore be attempted
only within a try/catch block:

 Collapse
public void RaiseTheEvent(MyEventArgs eventArgs)
{
try
{
MyEventHandler handler = MyEvent;
if (handler != null)
{
handler (this, eventArgs)
}
}
catch
{
// Handle exceptions here

}
}

Events can have multiple subscribers � each of which is called, in turn, by the event
handler (delegate) when the event handler is invoked by the [handler (this,
eventArgs)] line. The event handler used in the above block of code would stop iterating
over it's invocation list (of subscribed event handling methods) when the first unhandled
exception is raised by a subscriber. So, if there were 3 subscribers, for example, and the
2nd one threw an unhandled exception when invoked by the delegate, then the 3rd
subscriber would never receive the event notification. If you want for every subscriber to
receive the event notification even if other subscribers throw unhandled exceptions, then
you could use the following logic which explicitly loops through the event handler's
invocation list:

 Collapse
public void RaiseTheEvent(MyEventArgs eventArgs)
{
MyEventHandler handler = MyEvent;
if (handler != null)
{
Delegate[] eventHandlers = handler.GetInvocationList();
foreach (Delegate currentHandler in eventHandlers)
{
MyEventHandler currentSubscriber = (MyEventHandler)currentHandler;
try
{
currentSubscriber(this, eventArgs);
}
catch (Exception ex)
{
// Handle exception here.
}
}
}
}

8. Event Subscriber Registration and Unregistration

By design, the publisher of an event has absolutely no knowledge of any of the subscribers.
Consequently, it is the job of subscribers to register or unregister themselves with the
publisher of an event.

8.1 Registering A Subscriber

In order to subscribe to an event, the subscriber needs three things:

1. a reference to the object publishing the event of interest


2. an instance of the delegate upon which the event is defined
3. a method that will be called by the publisher when it raises the event

The subscriber then registers its event handler (delegate) instance with the publisher, like
this:

 Collapse
thePublisher.EventName += new
MyEventHandlerDelegate(EventHandlingMethodName);

In the above line...

 thePublisher is the reference to the object that will raise the event of interest.
Notice how the event,EventName, is accessed as if it were a public property
of thePublisher.
 The += operator is used to add the delegate instance to the invocation list of the
event handler in the publisher. Remember, multiple subscribers may register with
the event. Use the += operator to append the current subscriber to the underlying
delegate's invocation list.
 MyEventHandlerDelegate is a reference the particular event hander delegate to
be used (if not one of the built-in EventHandler delegates).
 Finally EventHandlingMethodName supplies the name of the method in the
subscribing class that is to be called upon the raising of the event.

WARNING: Do not use the = operator when registering an event subscriber with a


publisher. Doing so would replace any/all currently registered event subscribers with the
current subscriber. Instead, be sure to use the += operator to cause the current subscriber
to be appended to the event handler's invocation list.

8.2 Unregistering A Subscriber


A subscriber can unregister from the publisher, like this:

 Collapse
thePublisher.EventName -=
EventHandlerDelegate(EventHandlingMethodName);

The -= operator is used to remove the delegate instance from the invocation list in the
publisher.

Subscribers are automatically unregistered when an object is disposed � if the subscriber


was not already explicitly unregistered from the event.

9. Event Handling Method

An event handling method is the method in an event subscriber that is executed by the
event publisher upon the raising of an event. Be aware that some literature describing
events in .NET refers to these methods as "event handlers" even though, to be technically
precise, an "event handler" is a delegate upon which an event is based � and not any
method referenced by a such a delegate.

The important requirement of the event handling method is that its signature must match
the signature of the event handler (delegate) upon which the event is defined.

You should also carefully consider the consequences of any exceptions that may be thrown
or caught in the event handling method. Exceptions not caught in the event handling
method will propagate to the event publisher.

10. .NET 1.x vs. 2.0+ Considerations

The concepts and features presented in this section were introduced in the 2.0 version of
the .NET Framework. These newer features amount to shortcuts and, possibly, the
simplification of your code when used smartly.

There is a risk, however, that the improper use of some of these features could make your
event implementation code more difficult to understand. For example, if you made use of an
"anonymous method" (presented below) that was comprised of 30+ lines of code, your
event implementation would likely be far more difficult to read than an equivalent
implementation that, instead, places those 30+ lines in a named method.

It is important to understand that these 2.0+ concepts and features do not present any
fundamental change to the way events are implemented in .NET Framework applications.
Instead, they are mostly intended to simplify the way we go about implementing events.

10.1 Generics
In addition to the Generic-specific features presented elsewhere in this article
(e.g., System.EventHandler<T>), it should be noted that any event implementation
techniques that rely in any way on Generics will not be available in .NET 1.x applications, as
Generics were first introduced in the 2.0 version of the .NET Framework.

10.2 Delegate Inference

The C# 2.0 (and newer) compiler is smart enough to determine the type of delegate with
which a particular event is implemented. This "delegate inference" capability enables you to
omit the declaration of the requisite delegate in the code that registers an event handling
method with an event.

Consider the following 1.x code that registers an event handling method with an event. This
code explicitly instantiates the event handler (delegate) in order to register the associated
method with the event.

 Collapse
thePublisher.EventName += new MyEventHandlerDelegate(EventHandlingMethodName);

The following 2.0+ code uses delegate inference to register the same method with the
event. Notice the following code appears to register the event handling method directly with
the event.

 Collapse
thePublisher.EventName += EventHandlingMethodName;

When you assign the method name directly to the event like that, the C# compiler ensures
that the method signature matches the signature of the event handler upon which the event
is based. The C# compiler then inserts the requisite delegate registration code (i.e., ...
+= newMyEventHandlerDelegate(EventHandlingMethodName);) in the output
assembly.

This simplified syntax is made possible by the C# compiler, and not by any change to the
fundamental ways that events are implemented in the .NET Framework. To be clear, it
is not the case that events in C# 2.0 (and newer) can directly reference methods. What the
compiler is doing for us is supplying the [still] requisite delegate syntax in the output
assembly � as if we had explicitly instantiated the delegate.

10.3 Anonymous Methods

An anonymous method is a block of code that you pass to a delegate (rather than passing
the name of a method to be referenced by the delegate). When the C# compiler encounters
an anonymous method, it creates a complete method in the output assembly that contains
the code block you supplied. The compiler supplies a name for the method, then references
that [new] method from the associated delegate instance (all this happens in the output
assembly). The method is said to be "anonymous" because you are making use of it without
knowing its name (it has no name in your source code).

Anonymous methods present an opportunity for you to write simpler code. Consider the
following code that registers a short event handling method with an event:

 Collapse
static void EventHandlingMethod(object sender, EventArgs e)
{
Console.WriteLine("Handled by a named method");
}

thePublisher.EventName += new MyEventHandlerDelegate(EventHandlingMethod);

The above logic can be rewritten with an anonymous method, like this:

 Collapse
thePublisher.EventName += delegate {
Console.WriteLine("Handled by anonymous method");
};

Anonymous methods are intended to simplify our code. This simplification can happen when
the code block is relatively short. In the above example, the version of the logic that uses
the anonymous method syntax is easy to read because we don't have to locate any
separate event handling method in order to understand how the subscriber will respond
when the event is raised. The anonymous method syntax can, however, be more
cumbersome to read (more so than logic that references a named method) in cases where
the code block is comprised of many lines of code. Some authors suggest that code blocks
containing more than three or four lines of code should not be implemented as anonymous
methods. These more lengthy code blocks should, instead, go into named methods in order
to improve readability.

To summarize the alternatives presented so far, the following code demonstrates three
options for registering an event handling method with an event. The first demonstrates the
explicit approach that works with all versions of the .NET Framework. The second
demonstrates delegate inference. The third demonstrates the use of an anonymous method:

 Collapse
// Option 1 - explicit delegate creation with a named method

thePublisher.EventName += new MyEventHandlerDelegate(EventHandlingMethod);

// Option 2 - delegate inference

thePublisher.EventName += EventHandlingMethod;

// Option 3 - anonymous method

thePublisher.EventName += delegate(object sender, EventArgs e) {


Console.WriteLine("handled by anonymous method");
// You can access the sender and e parameters here if necessary
};

// Event handling method used in options 1 and 2

static void EventHandlingMethod(object sender, EventArgs e)


{
Console.WriteLine("Handled by a named method");
}

10.4 Partial Classes

Partial classes are relevant to the implementation of events in that Visual Studio will place
event registration code and event handling method stubs in the partial class files associated
with a given Windows Form class. Click here to go to section 15.1 which presents the
implementation of events in partial classes in greater detail.

11. Conventions

The following conventions were gleaned from a number of resources, including the authors
of the .NET Framework and other well-known industry experts (see reference list at the end
of this article for the complete list).

11.1 Event Publisher Conventions

Event Name

 Choose a name that clearly communicates the state change the event represents.
 Events can be categorized as (1) events raised before a state change occurs; and (2)
events raised after a state change occurs. Consequently, the event name should be
chosen to reflect the before or after [state change] aspect of the event.

Examples � for events raised before state change:

 FileDownloading
 TemperatureChanging
 MailArriving

Examples � for events raised after state change:

 FileDownloadCompleted
 TemperatureChanged
 MailArrived

System.EventArgs Subclass (where applicable)

 In the case of events that must or may [someday] carry custom event data, you
should create a new class that (1) extends System.EventArgs, and (2) implements
the members (e.g., properties) required to contain and expose your custom event
data
 The only time you should not subclass EventArgs is when you are certain that your
event will never carry event data
 The name of your EventArgs subclass should be the name of the event, with
'EventArgs' appended

Example EventArgs subclass names:

 DownloadCompletedEventArgs
 TemperatureChangedEventArgs
 MailArrivedEventArgs

For events that contain no data, and never will, it is recommended to


pass System.EventArgs.Empty. This recommended practice serves to maintain the event
implementation conventions even for events that have no event data. If there is a possibility
that your event may someday carry event data, even if not at initial implementation, then
you should create a subclass of System.EventArgs, and use that in your events. The
primary benefit of this recommendation is that you will someday be able to add data
(properties) to your subclass without breaking compatibility with existing subscribers.

Event Handler (delegate) Name

 If you are using .NET Framework 1.x, then you should use the built-
in System.EventHandler delegate
 If you are using .NET Framework 2.0 or newer (for 'both' publishers and
subscribers), then you can make use of the
generic System.EventHandler<TEventArgs> delegate
 If you create your own delegate, then the delegate name should be comprised of the
event name, with the word, 'Handler' appended

Example custom event handler (delegate) names:

 DownloadCompletedHandler
 TemperatureChangedHandler
 MailArrivedHandler

Event Handler (delegate) Signature

As stated above, under "Delegate Name," you should use one of


the System.EventHandler delegates provided by the .NET Framework. In these cases,
the delegate signature is, of course, determined for you and automatically conforms to the
recommended conventions.
The following recommendations are implemented in the System.EventHandler delegates
provided by the .NET Framework. If you create your own event handlers, then you should
follow these recommendations in order to remain consistent with the .NET Framework's
implementation.

 The delegate should always return void.

In the case of event handlers, it simply makes no sense to return a value to the
event publisher. Remember, event publishers, by design, have no knowledge of their
subscribers. In fact the delegate, by design, acts as an intermediary between the
event publisher and its subscribers. Consequently, publishers aren't supposed to
know anything about their subscribers � including the possibility of receiving
returned values. It is the delegate that is calling each subscriber, so any return value
would make it only as far as the delegate and would never get to the publisher
anyway. This rationale holds true for avoiding output parameters that use either
the outor ref parameter modifiers. The output parameters of the subscribers would
never propagate to the publisher.

 The first parameter should be of the object type and should be named sender.

This first parameter is to hold a reference to the object that raises the event. Passing
a reference to the event publisher enables the event subscriber to distinguish
amongst multiple possible publishers of a given event. Without a reference to the
publisher, the event handling method would have no way to identify or act on the
specific publisher that raised a particular event.

The data type for sender is object because practically any class can raise events.
Avoiding a strongly-typedsender parameter allows for one consistent event handler
signature to be employed across all events. Where necessary, the event handling
method can cast the sender parameter to the specific event publisher type.

Static events should pass null as the value of sender, rather than omitting
the sender parameter.

 The second parameter should be named 'e' and should be of


the System.EventArgs type or your custom subclass
of System.EventArgs (e.g., MailArrivedEventArgs).

In the case of cancellable events, the second parameter is either of


theSystem.ComponentModel.CancelEventArgs type or your custom subclass
thereof. In the case of events that carry no event data, you should
specify System.EventArgs as the second parameter type. In such
cases System.EventArgs.Empty is specified as the value of this parameter when
the event is raised. This practice is recommended to maintain conformance to the
convention � so that all event handler signatures include an EventArgs parameter
� even for events that do not have EventArgs. Apparently, according to the
convention, having one consistent signature is more important than having multiple
event handler signatures � even in cases where one of the parameters will never be
used.

Examples (with no custom data sent with the event):

 Collapse
delegate void DownloadCompletedHandler(object sender, EventArgs e);
delegate void TemperatureChangedHandler (object sender, EventArgs e);
delegate void MailArrivedHandler (object sender, EventArgs e);

Examples (with custom data sent with the event):

 Collapse
delegate void DownloadCompletedHandler(object sender,
DownloadCompletedEventArgs e);
delegate void TemperatureChangedHandler (object sender,
TemperatureChangedEventArgs e);
delegate void MailArrivedHandler (object sender,
MailArrivedEventArgs e);
Event Declaration

 Assuming the event is to be made available to code outside of the publishing class,
the event would be declared with the public keyword (to make it accessible to code
outside of the publishing class).
 The event handler upon which the event is based is specified as the type of the event
� in a similar fashion with which a data type is specified in a typical property or
method declaration.

Example (uses built-in generic System.EventHandler<TEventArgs> delegate):

 Collapse
public event System.EventHandler<mailarrivedeventargs> MailArrived;

Example (makes use of a custom event handler):

 Collapse
public delegate void MailArrivedHandler (object sender,
MailArrivedEventArgs e);

public event MailArrivedHandler<mailarrivedeventargs> MailArrived;


Method That Raises the Event

 Rather than raising an event inline throughout your code, it is recommended to


create a separate method that is responsible for raising the event. You then call that
method throughout your code as necessary.
 The name of this method should be the word On with the event name appended.
 If your event makes use of a custom EventArgs subclass, then the method that
raises the event should accept at least one parameter that is of the
particular EventArgs subclass defined for the custom event data.
 For non-static classes that are not sealed, the method should be implemented
as virtual with accessibility specified as protected so that derived classes can
easily notify clients registered with the base class.
 For sealed classes the accessibility of the method should of course be set
to private, as the raising of events should not be initiated from outside of the
class.

Examples (each takes a custom EventArgs subclass type as an argument):

 Collapse
OnDownloadCompleted(DownloadCompletedEventArgs)
{
// Raise event here

private OnTemperatureChanged(TemperatureChangedEventArgs)
{
// Raise event here

virtual OnMailArrived(MailArrivedEventArgs)
{
// Raise event here

11.2 Event Subscriber Conventions

Event Handling Method Name

 The convention implemented by Visual Studio, when it automatically creates an


event handling method stub, is to name the method as (1) the name of the object
raising the event; followed by (2) an underscore character; with (3) the event name
appended.

Examples:

 downloader_DownloadCompleted
 weatherStation_TemperatureChanged
 mailManager_OnMailArrived

 Another convention for determining the name of the event handling method is the
same as that described above for specifying name of the method that raises the
event in the publisher. Specifically, the name of the method should be the
word On with the event name appended.
Examples:

 OnDownloadCompleted
 OnTemperatureChanged
 OnMailArrived

Event Handling Method Signature

 The signature of the event handling method must exactly match the delegate
signature. According to the event handling conventions, as well as
the EventHandler delegates provided by the .NET Framework, the event handling
method must return void, while accepting exactly two parameters: an object-
typed variable named sender, and an EventArgs (or derived class) instance named
'e'.

Examples:

 Collapse
void DownloadManager_DownloadCompleted(object sender,
DownloadCompletedEventArgs e)
{
// event handling code goes here

void WeatherStation_TemperatureChanged(object sender,


TemperatureChangedEventArgs e)
{
// event handling code goes here

void MailMonitor_MailArrived(object sender, MailArrivedEventArgs e)


{
// event handling code goes here

}
Subscribing to the Event (code that registers the Event Handling Method with the
Event)

 To register a method with an event, use the += syntax, according to this pattern:

EventPublisherObject.EventName += newEventHandlerDelegateName(Nam
eOfMethodToCall);

Example:

 Collapse
m_MailMonitor.MailArrived += new EventHandler(
this.MailMonitor_MailArrived);
WARNING: Do not use the = operator when registering an event subscriber with a
publisher. Doing so would replace any/all currently registered event subscribers with the
current subscriber. Instead, be sure to use the += operator to cause the current subscriber
to be appended to the event handler's invocation list.

Unsubscribing from the Event (Code that Unregisters the Event Handling Method
from the Event)

 To unregister a method with an event, use the -= syntax, according to this pattern:

EventPublisherObject.EventName -= ne
wEventHandlerDelegateName(NameOfMethodToCall);

Example:

 Collapse
m_MailMonitor.MailArrived -= new EventHandler(
this.MailMonitor_MailArrived);

11.3 Naming Conventions

Camel Casing

Camel casing is a naming convention whereby the first letter is lower case, with each
subsequent "word part" starting with an upper case letter. By convention, variables names
are camel cased.

Camel cased examples: someStringToWrite, ovenTemperature, latitude

Pascal Casing

Pascal casing is naming convention whereby every "word part" of a name starts with an
upper case letter, with other letters lower case, and no underscores. By convention, names
of classes, events, delegates, methods, and properties are to be Pascal cased.

Pascal cased examples: MailArrivedEventHandler, AppClosing, MyClassName

12. Steps to Creating Custom Events

In order to keep the following steps as brief as possible, little or no explanation of any given
step is provided. Explanations, examples, and conventions for each step are presented
elsewhere throughout this article.

12.1 Prepare the Event Publisher


Step 1: EventArgs - Decide how your event will account for EventArgs.

 Including EventArgs with custom events is required to conform to the event


publishing standards.EventArgs, however, are not a technical requirement � you
can create, raise, and handle custom events that do not make any use
of EventArgs.
 If your event will never communicate custom event data, then you can satisfy this
step by deciding to use the built-in System.EventArgs class. You would later
specify the value, EventArgs.Empty, when raising the event.
 If your event is not cancellable and includes custom event data, then you should
create a class that extendsSystem.EventArgs. Your custom EventArgs subclass
would include any additional properties that contain the event data.
 If your event is cancellable then you can
use System.ComponentModel.CancelEventArgs � which includes the
Boolean Cancel property that clients can set to true to cancel the event. You can
create a subclass of CancelEventArgs that has properties for any additional event-
specific data.

Step 2: Event Handler - Decide which event handler your event will use.

 You have two basic alternatives � create your own event handler (delegate) or use
one of theEventHandler delegates provided by the .NET Framework. If you use one
of the built-in event handlers, then you will have less code to maintain and your
event handler signature will automatically conform to the convention of
returning void, while accepting the parameters, object sender, and EventArgs
e
 If using .NET 1.x, consider using the built-in System.EventHandler delegate.
 If using .NET 2.0, consider using the built-in
generic System.EventHandler<TEventArgs> delegate.

Step 3: Declare the Event � Decide which syntax to use: field-like syntax or
property-like syntax.

 The field-like syntax will suffice for many custom event implementations.
 Consider going with the property-like syntax when your class exposes a large
number of events, only a few of which are expected to be subscribed to at any given
time.

Step 4: Event-Raising Method � Decide whether you will raise the event from a
method, or raise it inline.

 It is generally recommended to raise events from a method that is dedicated to that


task, rather than raising events inline and throughout your code.

Step 5: Raise the event.


 Either raise the event inline, or call the method that raises the event.
 Prior to raising the event you will need to have an instance of
your EventArgs subclass populated with event-specific data. If not making use of
any EventArgs subclass, then you should includeSystem.EventArgs.Empty in
place of a custom EventArgs class when you raise the method.

12.2 Prepare the Event Subscriber

Because this article presents the event pattern (object sender, EventArgs e)


implemented throughout the .NET Framework classes, the following steps will help you wire
up event handling methods that work with practically all .NET Framework events, in addition
to custom events you create according to the same pattern.

Step 1: Write the Event Handling Method.

 Define an event handling method with a signature that exactly matches the delegate
upon which the method is defined.
 When using either the built-in non generic System.EventArgs, or the
genericSystem.EventHandler<TEventArgs> delegate in the event declaration,
the resulting signature automatically matches the convention of returning void and
accepting the parameters (object sender, EventArgs e).

Step 2: Instantiate the Event Publisher.

 Declare class-level member variable that references the class or object that
publishes the event of interest.

Step 3: Instantiate the event handler (if necessary).

 If the event of interest is based on a custom event handler, then create an instance
of that event handler, passing in the name of the event handling method.
 This step may be combined with Step 4 (next) by using the new keyword to
instantiate the delegate in the same line in which the delegate is registered with the
event

Step 4: Register the subscriber (event handling method) with the event.

 Any .NET version: Use the += syntax to register the event handler with the event.
 .NET 2.0+: Alternatively, through delegate inference, you can simply assign the
method name directly to the event.
 .NET 2.0+: Alternatively, if the event handling method is very brief (3 or so lines of
code), your implementation may be easier to read if you register the event handling
logic via an "anonymous method."

Step 5: Unregister the subscriber (event handling method) from the event.
 When the subscriber should no longer receive event notifications from the publisher,
then you can unregister the subscriber from the event.
 This step can be considered as optional, given that subscribers are automatically
unregistered from publishers when the subscriber is disposed.

13. Sample Event Implementation

This example makes use of a custom event handler, carries event data in a
custom EventArgs subclass, declares an event with field-like syntax, and otherwise
conforms to recommended event implementation standards and guidelines. None of the
.NET 2.0+ features (anonymous methods, etc) are used in order to keep the presentation
as explicit as possible.

This sample event is raised when a file is moved by a "file mover" utility (in the sample
project). The event data includes (1) the name of the file moved, (2) the source folder path,
and (3) the destination file path.

13.1 Sample Event Publisher Code

Step 1: Subclass EventArgs

Here we derive a new class, MoveFileEventArgs, from EventArgs in order encapsulate


the event data sent to the subscriber.

 Collapse
public class MoveFileEventArgs : EventArgs
{
// Fields

private string m_FileName = string.Empty;


private string m_SourceFolder = string.Empty;
private string m_DestinationFolder = string.Empty;

// Constructor

public MoveFileEventArgs(string fileName, string sourceFolder,


string destinationFolder)
{
m_FileName = fileName;
m_SourceFolder = sourceFolder;
m_DestinationFolder = destinationFolder;
}
// Properties (read-only)

public string FileName


{
get { return m_FileName; }
}

public string SourceFolder


{
get { return m_SourceFolder; }
}
public string DestinationFolder
{
get { return m_DestinationFolder; }
}
}
Step 2: Event Handler (delegate)

Here we declare a new delegate that conforms to the event conventions, returns void and
accepts two parameters; an object named 'sender' and EventArgs named 'e'.

 Collapse
public delegate void MoveFileEventHandler(object sender,
MoveFileEventArgs e);
Step 3: Declare the Event

Here we declare the event using the field-like syntax.

 Collapse
public event MoveFileEventHandler MoveFile;
Step 4: Event-Raising Method

Here we declare the method that raises the event.

 Collapse
private void OnMoveFile()
{
if (MoveFile != null) // will be null if no subscribers

{
MoveFile(this, new MoveFileEventArgs("SomeFileName.txt",
@"C:\TempSource", @"C:\TempDestination"));
}
}
Step 5: Raise the Event

Here we have a method that would do the work of interest (move the file). Once the work is
completed, the method that raises the event is called.

 Collapse
public void UserInitiatesFileMove()
{
// code here moves the file.

// Then we call the method that raises the MoveFile event

OnMoveFile();
}

13.2 Sample Event Subscriber Code

Step 1: Write the Event Handling Method


This method is called when the fileMover instance raises the MoveFile event. It's
signature exactly matches the event handler's signature.

 Collapse
void fileMover_MoveFile(object sender, MoveFileEventArgs e)
{
MessageBox.Show(sender.ToString() + " moved the file, " +
e.FileName + ", from " +
e.SourceFolder + " to " + e.DestinationFolder);
}
Step 2: Instantiate the Event Publisher
 Collapse
FileMover fileMover = new FileMover();
Step 3: Instantiate the event handler as we register the subscriber (event
handling method) with the event

This approach combines steps 3 and 4 from the steps listed in the section 14.2.

 Collapse
fileMover.MoveFile += new FileMover.MoveFileEventHandler(
fileMover_MoveFile);

14. Handling Events Raised by .NET Framework Components -


Walkthrough and Example

The event implementation conventions described in this article can be found throughout
the .NET Framework's own event implementations. The purpose of this walkthrough is to
show how one .NET Framework component exposes its events, and how you can write code
that runs when the event is raised. You will see that the steps required are simply a subset
of the recommended steps required to implement your own events and event handling
methods.

This section points out how the Framework makes use of these conventions by walking you
through theFileSystemWatcher component's Deleted event implementation.
The FileSystemWatcher is a class provided in the .NET
Framework's System.IO namespace. This class can be used to notify your application when
specific disk IO activity takes place in a specific folder (e.g., a new file is created, a file is
modified, or deleted, etc).

14.1 File SystemWatcher 'Deleted' Event Implementation

The System.IO.FileSystemWatcher class exposes the Deleted event � which is raised


by aFileSystemWatcher instance when it detects that a file has been deleted in a
watched folder.

Event Declaration
This is how the .NET Framework's FileSystemWatcher class declares its Deleted event:

 Collapse
public event FileSystemEventHandler Deleted
Delegate Declaration

The delegate used is FileSystemEventHandler, which is declared in the .NET Framework


as:

 Collapse
public delegate void FileSystemEventHandler (Object sender,
FileSystemEventArgs e)

Notice that the FileSystemEventHandler delegate conforms to the convention of


accepting two parameters � the first named sender is of type System.Object, and the
second parameter is named 'e' and is of typeSystem.EventArgs or a descendent thereof.
In this case, the FileSystemEventArgs specified is a descendent (as further described
below).

Custom EventArgs

The Deleted event communicates information about the file or directory that was deleted
through a subclass ofSystem.EventArgs:

 Collapse
public class FileSystemEventArgs : EventArgs {}

The FileSystemEventArgs class extends System.EventArgs by adding the following


properties:

 ChangedType � Gets the type of directory change that occurred (communicated as


a WatcherChangeTypeenumeration value of Created, Deleted, Changed, etc.)
 FullPath � Gets the fully qualified path to the affected file or directory
 Name � Gets the name of the affected file or directory

When the Deleted event is raised, the FileSystemWatcher instance sends the path, file


name, etc. to the event subscribers. This means that each subscriber not only learns that a
file was deleted, but specifically which file was deleted.

Notice that the name of the event handler, FileSystemEventHandler, does not conform
exactly to the naming convention that suggests the event handler name should be the
name of the event followed by the word, Handler. Remember, the conventions are not laws
or rules. Instead, they are just suggestions for making your code more easily understood.
In the case of the FileSystemWatcher class, one event handler was implemented to
support a number of events, including Deleted, Created and Changed � thus the minor
break from a strict interpretation of the naming convention. A strict adherence to the
convention would have resulted in the creation of 3 delegates that are identical except for
the name (e.g., DeletedHandler, CreatedHandler, etc.) Alternatively the name chosen
could have been something like DeletedOrCreatedOrChangedHandler � which would
have been ridiculous. In this case a reasonable deviation from the convention was
[thankfully!] chosen.

14.2 Handling the FileSystemWatcher.Deleted Event

Given the above event implementation provided by the .NET Framework, the following is an
example of code you could write in your class that would subscribe to the Deleted event of
a FileSystemWatcher instance. This code would enable your application to respond to
files being deleted in the C:\Temp directory.

 Collapse
// Import the namespace

using System.IO;

// Declare fsWatcher variable - likely at the class level.

FileSystemWatcher fsWatcher = new FileSystemWatcher();

//Initialize the instance - possibly in the constructor or method called from

constructor.
fsWatcher.Path = "C:\Temp";
fsWatcher.Deleted += new FileSystemEventHandler(fsWatcher_Deleted);

// this Boolean property enables or disables the raising of events by the

FileSystemWatcher instance.
fsWatcher.EnableRaisingEvents = true;
// include if updating Windows Forms components

fsWatcher.SynchronizingObject = this;

// event handling method

void fsWatcher_Deleted(object sender, FileSystemEventArgs e)


{
MessageBox.Show(e.Name + " was deleted" );
}

15. Windows Forms Events

Windows Forms implement events of their own, as well as facilitate the handling of events
raised by controls contained within the form or within container controls in the form class.
Additionally, the implementation and handling of events in Windows Forms classes require
careful consideration and even unique steps be taken given that Windows Forms and the
controls they contain exhibit "thread affinity" � meaning that their properties can be
updated only by code running in the same thread that created the Form or controls.
These and other Windows Forms-specific considerations are presented in this section.

15.1 NET 1.x and 2.0+ Difference - Partial Classes

The concept of "partial class" was introduced in the .NET Framework version 2.0. A partial
class is a class declared with the partial keyword, and with parts of the class defined in
two or more source code files. The compiler retrieves all source code defining the partial
class from all files containing the partial class, and outputs one [compiled] class. That is, a
partial class may exist in two or more source code files, but when the application is
compiled, the "class fragments" are compiled into one class in the output assembly.

The benefits of partial classes include (1) multiple developers can work on different parts of
the same class at the same time, by working with different source code files; and (2)
automated code generating tools can write to one source code file, while human developers
can maintain their code in a separate file, without concern that their changes may
eventually be overwritten by the automated code generating tool. This second benefit is
realized in Windows Forms projects starting with Visual Studio 2005. When you add a new
form to a Windows Forms project, Visual Studio automatically creates the form as a partial
class that is defined in two source code files. The file that contains code generated by Visual
studio is named FormName.Designer.cs, while the file intended for developer code is
named FormName.cs. So, for example, if you have Visual Studio 2005 create a form named
MainForm, then the following two source code files will be created:

MainForm.cs � contains the partial class definition:

 Collapse
public partial class MainForm : Form
{
// developers write code here

MainForm.Designer.cs � contains the partial class definition:

 Collapse
partial class MainForm
{
// Windows Forms designer writes code here

When you add controls to the form by using the Visual Studio Windows Forms designer, the
designer will add the necessary code to the FormName.Designer.cs file.

Developers should not directly modify the source code in the FormName.Designer.cs file


because it is possible that the designer would overwrite such changes. As a general rule, all
developer code should be written in theFormName.cs file.
.NET 1.x simply does not have partial classes. All source code � whether written by Visual
Studio or a developer � goes into a single source code file. While the Visual Studio Windows
Forms designer attempts to write code in just one section of that file, it is possible to have
developer code and generated code in the same sections, with the possibility that the
Windows Forms designer would overwrite developer-written code.

15.2 Partial Classes and Windows Forms Designer Considerations For


Events

When Visual Studio 2005 creates an event handling implementation for you, the event
handler/registration code is written to the FormName.Designer.cs file, with only an event
handling method stub automatically written to theFormName.cs file. The intent behind this
arrangement is for the Windows Forms designer to write all of the event-related code that
can be automated (wiring up the event handling method with the event handler, etc.). The
only part that the designer cannot create for you is the particular programming logic that is
to take place inside of the event handling method. So, when Visual Studio is finished doing
everything it can for you, what you have is (1) all of the event-related "plumbing" code
tucked away in the FormName.Designer.cs file; with (2) an event handling method stub
waiting for you in the FormName.cs file. All that you must do to finish the event handling
implementation is write the necessary code in the event handling method stub.

15.3 Walkthrough - Handling a Windows Forms Event

The following steps walk you through the implementation of a FormClosing event handling
method in a Windows Form named MainForm.

1. Using Visual Studio .NET, create a new Windows Forms project, and add a new Form
named MainForm.
2. With MainForm opened in design view, right-click over an exposed portion of the
form (not over a control), and select Properties from the popup menu. The Properties
dialog that appears can display either form properties or events. If it is not already
selected, click on the Events button (it has the lightening bolt icon) in to toolbar at
the top of the properties dialog.
3. In the Events dialog, locate the event for which you want your application to
respond. In our case, this is theFormClosing event. Double-click anywhere in the
row where FormClosing is listed.

Two things happen at this point. First, the Windows Forms designer inserts the following line
to theMainForm.Designer.cs file.*

 Collapse
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(
this.MainForm_FormClosing);
Second, the Windows Forms designer inserts the following method stub into to
the MainForm.cs file.*

 Collapse
private void MainForm_FormClosing(object sender,
FormClosingEventArgs e)
{
// your event handling code goes here

* When using .NET 1.x (which does not have partial classes), the same code is generated,
but goes into the singleMainForm.cs file.

You can see that nothing has been hidden from you. All code required to implement the
event handling logic is present and available for you to review and possibly modify as
necessary. All that the Windows Forms designer does is write the boilerplate code in a way
that conforms to the recommended event standards, and places that code in the partial
class files where you can then extend it for your application-specific purposes.

If you want to change the name of the event handling method that the Windows Forms
designer generates for you, you are free to do so. Just be sure to change the method name
in both the MainForm.cs file, and where it is registered with the event handler
in MainForm.Designer.cs.

On a separate note, the FormClosing event is a "before event" that is cancellable. This


means that the event is raised before the form is closed, and the event handling procedure
can cancel the event, thereby preventing the form from being closed.

What specifically makes this event cancellable is the FormClosingEventArgs parameter,


which is of a class type that extends System.ComponentModel.CancelEventArgs. To
cancel the FormClosing event, you would set the Boolean Cancel property
of FormClosingEventArgs to true, like this:

 Collapse
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true; // prevents the form from closing

15.4 Windows Forms and Threading Considerations

Windows forms and the controls they contain can have their properties and methods called
only by code running on the same thread that created the control (i.e. Windows forms and
controls exhibit thread affinity). You may, therefore, run into unexpected behavior or
runtime exceptions when code from a non UI thread attempts to execute code that modifies
UI components.
Beware that you might run into threading issues related to this UI thread affinity even if you
are not explicitly or knowingly employing multiple threads in your application. For example,
the FileSystemWatcher class automatically spawns an additional background thread for
its own purposes. Consequently, you may encounter threading issues if your code makes
use of the FileSystemWatcher, and any of the related event handling methods ultimately
cause an update to a UI control. Separately, any 3rd party components you are using may
spawn additional background threads of which you are initially unaware.

There are a few ways to mitigate these threading issues:

 Set the SynchronizingObject property (where available and relevant)


 Make use of Control.InvokeRequired and Control.Invoke() and to call the
code that updates the UI.
 Component developers can make use of
the SynchronizationContext, AsyncOperation,
andAsyncOperationManager classes.

SynchronizingObject

Some .NET components provide a SynchronizingObject property. Examples of these


components includeFileSystemWatcher, Timer, and the Process classes. Setting
the SynchronizingObject enables event handling methods to be called on the same
thread that created the UI component that is to be updated. So, for example, a
Timer's Elapsed event is raised from a thread pool thread. When the Timer
component'sSynchronizingObject is set to a UI component, the event handling method
for the Elapsed event is called on the same thread on which the UI component is running.
The UI component can then be updated from the Elapsedevent handling method.

It should be noted that Visual Studio may automatically set


the SynchronizingObject property to the control that contains the component.
Consequently, you may never encounter the need to explicitly set
theSynchronizingObject property.

There are scenarios, however, where the SynchronizingObject property may need to be


set explicitly. For example, when you have a class library that is instantiated within a
Windows Form, and that class library contains aFileSystemWatcher instance.
The FileSystemWatcher spawns an additional background thread from which its events
are raised. These events would then be handled within the class library. So far so good. The
class library can handle the events because it lacks the thread affinity found with Windows
Forms controls. The class library may, in response to receiving
a FileSystemWatcher event, raise a new event that is then handled in the containing
Windows Form instance. The following exception will occur if
the SynchronizingObject has not been set to that Form (or relevant control on it), or
the UI updating code is not invoked via Control.Invoke(), as described next.
 Collapse
System.InvalidOperationException was unhandled:
Cross-thread operation not valid: Control 'ControlNameHere' accessed from a
thread other than the thread it was created on.
Control.Invoke() and InvokeRequired

There are two relevant exceptions to the rule that states that Windows Forms controls
cannot be accessed from a thread other than the thread on which they were created. Every
control inherits the Invoke() method andInvokeRequired property, which can be
accessed from other threads. Invoke() takes a single argument, which is of type delegate.
When called, Invoke() causes the delegate to call any methods registered with it.
Obviously then, any code called via Invoke() will be executed on the same thread in which
the control exists. So, to update UI controls running on one thread from code in another
thread, simply (1) break out the code that updates the UI control into its own method; then
(2) register that method with the delegate that is then (3) passed to the UI
control's Invoke() method.

InvokeRequired returns true when the current code is running on a thread other than the
thread on which the Control was created. You can query the value of InvokeRequired to
determine whether your code can directly update the Control, or if such updates must be
routed through the Invoke() method.

SynchronizationContext, AsyncOperation, and AsyncOperationManager

New to the 2.0 version of the .NET Framework, these classes provide another option for
developers of components that raise events asynchronously to address the threading issues
described above. An important benefit of
usingSystem.ComponentModel.AsyncOperation is that it provides the solution to the
threading issues (presented above) in the event publisher (component), whereas the two
alternatives presented above (Control.Invoke andSynchronizingObject) place the
solution with the subscribers.

16. Cancellable Events

Cancellable events are generally raised by a component that is about to perform some
action that can be cancelled or otherwise prevented from happening. The Windows Form
class's FormClosing event is an example of a cancellable event. A typical scenario in which
you'd want to prevent a form from closing would be where the user has not saved changes.
In this scenario, your FormClosing event handling method could implement logic that
detects the presence of unsaved changes. If there are any, the logic could prompt the user
to save their changes. If the user elects to save their changes, your logic would cancel
the FormClosing event. This would prevent the form from closing, thereby giving the user
the opportunity to review their changes and possibly save them before attempting to close
the form again.
The inner workings of a cancellable event can be quite straight-forward. Considering that
events frequently signal to subscribers that a change in state or some other activity is
"about to" place, this "pre-event" represents an ideal opportunity for the event publisher to
determine if it (the publisher) should allow for the change in state (or activity) to take
place. If the activity is allowed to take place (i.e., nothing tells the publisher to abort the
operation), the publisher then allows for the change in state to happen and
subsequently/optionally raises the post-event.

In sum, a cancellable event is really two events and some activity that takes place between
those events. The "pre-event" happens before the activity. The activity then takes place (or
not). If the activity takes place, then the "post-event" is typically raised. So, to be precise,
no event is being cancelled even though we say we have a cancellable event. Rather, the
activity that takes place between the two events is what is cancelled � and likely prevented
from starting at all.

In support of the above notion of cancellable event, the .NET Framework provides us with
theSystem.ComponentModel.CancelEventArgs class that we can use directly or extend
for our application-specific purposes. CancelEventArgs extends System.EventArgs by
providing the Boolean Cancel property that, when set to true by an event subscriber, is
used by the event publisher to cancel the event. The event publisher code creates an
instance of CancelEventArgs that is sent to subscribers when the pre-event is raised. By
default, event handling methods (in any/all subscribers) are run synchronously.
Consequently, the change in state (or activity) signaled by the pre-event cannot happen
until after all event handling methods have run to completion. Of course the event publisher
retains its reference to the CancelEventArgs instance after the event is raised. So, if any
event handling methods set the Cancel property to true, the event publisher will see this
before attempting to proceed with the change in state, and can therefore respond
accordingly.

The sequence of activity could be something like this:

1. the event publisher instantiates System.ComponentModel.CancelEventArgs (or


subclass thereof) with the name 'e'
2. the event raising method then raises the event, passing 'e' to the event subscribers
(defaulting Cancel tofalse)
3. the event handling method (in the event subscriber, of course) then sets the value
of e.Cancel to true, possibly by prompting the user
4. the event raising method then gets the value of e.Cancel, and responds
accordingly. In the case wheree.Cancel = true, the logic would then prevent the
change in state or activity from taking place (e.g., form closing).

In the case where the event has multiple subscribers, the event will be cancelled if any of
the event handling methods set e.Cancel = true. More specifically, the event publisher
will see that e.Cancel = true when the last of the event handling methods returns (they
are called synchronously).
At the end of the day, all that CancelEventArgs does for us is provide a mechanism for an
event subscriber to communicate a true | false value to the event publisher. The actual
work and meaning of "cancelling the event" is entirely up to you, as you would write the
logic that responds to the value of e.Cancel.

Cancelling a Long-Running Operation

The event cancellation scenario described above offers no mechanism for stopping some
activity (or change in state) from occurring once the activity has begun. This is because the
event publishing activities all happen synchronously (or sequentially). The pre-event can be
used to prevent the activity from starting, but once it has started, it will run to completion
because the subscriber's pre-event event handling methods have run to completion and can
therefore no longer communicate with the event publisher.

If you need to enable subscribers to cancel an operation after it has begun (e.g., after the
pre-event has been raised and handled), the basic event publishing mechanism described
above will not be sufficient. You would need to employ a more robust event publishing
mechanism in which the event publisher conducts its activity asynchronously (e.g., on a
background thread). The basic idea is that the client code (in the subscriber/observer)
would request some activity to take place in the event publisher. The event publisher would
then initiate its activity on a background thread. While the background task is running, the
client code is free to proceed with other work, perhaps processing gestures from the user �
which could include a request to cancel the asynchronous operation that is in progress. The
publisher would need to implement logic that periodically checks to see if the client has
requested cancellation and, if so, stop doing its work.

A relatively easy and safe way to get started with asynchronous processing is to become
familiar with theSystem.ComponentModel.BackgroundWorker component.
The BackgroundWorker component enables you to run a task asynchronously, report
progress of the task (percentage towards completion), cancel the task after it has started,
and report task completion (with return value). It is beyond the scope of this article to
further present asynchronous processing models and multithreading alternatives and related
issues.

17. ASP.NET Web Forms Events

There is nothing fundamentally unique about the core concepts involved in the creation of
events, event handlers, and event handling methods in ASP.NET Web applications.
Everything presented in this article about creating custom events and event handlers
applies equally well to ASP.NET Web applications as it does to Windows Forms applications
and C# code libraries. What is fundamentally different about ASP.NET Web applications is
the context in which events are defined, raised, and handled. The stateless nature of HTTP
and its request/response model, the role of the ASP.NET HTTP request pipeline, the role of
ViewState, etc. all come into play � and with implications for the raising and handling of
events. Beyond event fundamentals, as presented in this article, there are ASP.NET-specific
event-related concepts such as client-side events (written in ECMA Script, VBScript, or
JavaScript) postbacks, and event bubbling.

This article does not attempt to address events in the context of ASP.NET Web applications
because a reasonable treatment would more than double the length of this article (and this
article is already long enough!). It should be noted, however, that the fundamentals
presented in this article will provide the beginning Web application developer with a solid
foundation upon which to build.

Events Programming in C#

In the early days of computing, a program would begin execution and then
proceed through its steps until it completed. If the user was involved, the
interaction was strictly controlled and limited to filling in fields. 
  
Today's Graphical User Interface (GUI) programming model requires a
different approach, known as event-driven programming. A modern program
presents the user interface and waits for the user to take an action. The user
might take many different actions, such as choosing among menu selections,
pushing buttons, updating text fields, clicking icons, and so forth. Each
action causes an event to be raised. Other events can be raised without
direct user action, such as events that correspond to timer ticks of the
internal clock, email being received, file-copy operations completing, etc. In
programming, you are often faced with situations where you need to execute
a particular action, but you don't know in advance which method, or even
which object, you'll want to call upon to execute it. For example, a button
might know that it must notify some object when it is pushed, but it might
not know which object or objects need to be notified.

C# adds on value to the often mentioned world of event driven


programming by adding support through events anddelegates. 

Events

An event in is a way for a class to provide notifications to clients of that class


when some interesting thing happens to an object. The most familiar use for
events is in graphical user interfaces; typically, the classes that represent
controls in the interface have events that are notified when the user does
something to the control (for example, click a button). An event is the
outcome of an action. There are two important terms with respect to events.
The event source and the event receiver. The object that raises the event is
called event source and the object that responds to the event is called event
receiver. The communication channel between an event source and an event
receiver is the delegate.  

Delegate

Delegates act as an intermediary between an event source and an event


destination. Technically, a delegate is a reference type used to encapsulate a
method with a specific signature and return type. You can encapsulate any
matching method in that delegate. To be even precise delegates are similar
to function pointers. They can be called as type safe function pointers. Unlike
function pointers, delegates are object-oriented and type safe. A delegate
class used as an intermediary between an event source and event receiver is
called an event handler.  

There are three steps in defining and using delegates:

 Declaration
 Instantiation
 Invocation

Declaration

A delegate is created with the delegate keyword, followed by a return type


and the signature of the methods that can be delegated to it, as in the
following:  

public delegate int MyDelegate(object obj1, object obj2);

This declaration defines a delegate named MyDelegate, which will


encapsulate any method that takes two objects as parameters and that
returns an int. Once the delegate is defined, you can encapsulate a member
method with that delegate by instantiating the delegate, passing in a
method that matches the return type and signature. The delegate can then
be used to invoke that encapsulated method.  

Instantiation

In order to make use of this delegate, you need to instantiate it, to specify
the method that needs to be called. 

public void MyMethod()
{
MyDelegate a = new MyDelegate(MyDelegateMethod);

Here MyDelegateMethod is a method that has a signature similar to that of
MyDelegate.

If signatures of method and delegate do not match, the C# compiler reports


an error such as:

td.cs(38,46): error CS0123: Method


DelegateDemo.SampleClient.MyEvent()' 
does not match delegate 'void DelegateDemo.CustomTextEvent()'

Invocation

A delegate is used to invoke a method similar to how a method call is made.

For example:  MyDelegateMethod("This is a test invocation"); 

Events and Delegates

Declaring an event is directly tied to a delegate. A delegate object


encapsulates a method so that it can be called anonymously. An event is a
mechanism by which a client class can pass in delegates to methods that
need to be invoked whenever "something happens". When it does, the
delegate(s) given to it by its clients are invoked.

To declare an event in C#, use the following syntax: 

public delegate void testDelegate(int a);
public event testDelegate MyEvent;

Once an event is declared, it must be associated with one or more event


handlers before it can be raised. An event handler is nothing but a method
that is called using a delegate. Use the += operator to associate an event
with an instance of a delegate that already exists.

For example:

Myform.MyEvent += new testEvent(MyMethod); 

An event handler may also be detached as follows:

MyForm.MyEvent -= new testEvent(MyMethod);

In C#, events may be raised by just calling them by name similar to method
invocation, say MyEvent(10). 
How Event works?

Whenever an event is defined for a class, the compiler generates three


methods that are used to manage the underlying delegate: 

add_<EventName> :
this is a public method that calls the static Combine method of
System.Delegate in order to add another method to its internal invocation
list. This method is however not used explicitly. The same effect is achieved
by using the += operator as specified before.  

remove_<EventName> :
this is also a public method that calls the static Remove method of
System.Delegate in order to remove a receiver from the event's invocation
list. This method is also not called directly. Its job is done by the "-="
operator. 

raise_<EventName> :
a protected method that calls the compiler generated Invoke method of the
delegate, in order to call each method in the invocation list. 

Types of Delegates 

There are basically two types of delegates. Single Cast


delegate and Multi-Cast delegate. A single cast delegate can call only one
function. A multi-cast delegate is one that can be part of a linked list. The
multi-cast delegate points to the head of such a linked list. This means that
when the multi-cast delegate is invoked it can call all the functions that form
a part of the linked list. Assume that one has several clients who would like
to receive notification when a particular event occurs. Putting all of them in a
multi-cast delegate can help call all the clients when a particular event
occurs.  

To support a single cast delegate the base class library includes a special
class type called System.Delegate. To support multi-cast delegates the base
class library includes a special class type called SystemMultiCastDelegate. 

Single Cast Delegate 

The signature of a single cast delegate is shown below: 

public delegate Boolean DelegateName (parm1, parm2)

The names param1 and param2 can be replaced with one's own names and
parameters. When the compiler compiles the statement above, it internally
generates a new class type. This class is called DelegateName and derives
from System.Delegate. 

public delegate Boolean MyDelegate(Object sendingobj, Int32 x); 


public class TestDelegateClass

Boolean MyFunction(Object sendingobj, Int32 x) 

//Perform some processing 
return (true); 

public static void main(String [] args)
{
//Instantiate the delegate passing the method to invoke in its 
//constructor
MyDelegate mdg = new MyDelegate(MyFunction); 
// Construct an instance of this class
TestDelegateClass tdc = new TestDelegateClass(); 
// The following line will call MyFunction
Boolean f = mdg(this, 1);
}

Lest see how the above code works.

The System.Delegate contains a few fields. The most important of them


are Target and Method.

The Target field identifies an object context. In the scenario above this
would point to the TestDelegateClass being created. If the method to be
invoked is a static method then this field will be null.

The Method field identifies the method to be called. This field always has
some value. It is never null.

In our case we are passing only one parameter. The natural question comes:
How this is done? Actually it gets done internally by the compiler. The
compiler resolves the call above to a call passing in the two parameters
required. In C++ with managed extension, the object and the address of the
function have to be passed explicitly. But in VB and C# we have done away
with this with the compiler filling in the necessary details.

In the code sample above we saw:

Boolean f = mdg(this, 1);

When the compiler comes across this line, this is what happens,
1. The compiler identifies that mdg is a delegate object.
2. The delegate object has the target and method fields as described
above.
3. The compiler generates code that calls the Invoke method of the
System.Delegate derived class i.e MyDelegate.
4. The Invoke method internally uses the MethodInfo type identified by
the delegate's Method field and calls the MethoidInfo's invoke method.
5. The MethodInfo's invoke method is passed the delegates Target field
which identifies the method and an array of variants which contains
the parameters to the method to be called. In our case, the array of
variants would be an Object and an Int32 value. 

Multi-Cast Delegates

The signature of a mutli cast delegate is shown below. The letters in italics
can be replaced with your own names and parameters.

public delegate void DelegateName (parm1, parm2)

Whatever has been written with respect to Single cast delegate holds good
even in the context of a Multi-Cast Delegate. There is a small addition here.
Since a multi-cast delegate represents a linked list, there is an additional
field called prev which refers to another Multi-Cast Delegate. This is how the
linked list is maintained.

The return type here has changed from Boolean to void. The reason is that
since several multi-cast delegates get called consecutively we cannot wait to
get the return value from each of these methods being called.

The code shown in the example for single cast delegate will work in the case
of multi-cast delegate too. The only difference is that the delegate signature
needs to be changed, the method signature needs to be changed to return
void instead of Boolean.

The power of multi-cast delegates comes in when we combine delegates to


form a linked list. The Combine method is available in the System.Delegate
class as a static method. The function signature as follows:

public static Delegate Combine(Delegate a, Delegate b); 

Code Sample: 

class MyDelegate1
{
public void dispMyDelegate1(string s)
{
Console.WriteLine("MyDelegate1");
}
}  
class MyDelegate2

public void dispMyDelegate2(string s)
{
Console.WriteLine("MyDelegate2");
}

public delegate void OnMsgArrived(string s); 
class TestMultiCastUsingDelegates
{
public static void Main(string [] args)
{
MyDelegate1 mcd1=new MyDelegate1();
MyDelegate2 mcd2=new MyDelegate2();
// Create a delegate to point to dispMyDelegate1 of mcd1 object
OnMsgArrived objOnMsgArrived1=new OnMsgArrived(mcd1.dispMyDelegate1); 
// Create a delegate to point to dispMyDelegate2 of mcd2 object
OnMsgArrived objOnMsgArrived2=new OnMsgArrived(mcd2.dispMyDelegate2); 
OnMsgArrived omc;
// Combine the two created delegates.Now omc would point to the head of a 
// linked list of delegates
omc=(OnMsgArrived)Delegate.Combine(objOnMsgArrived1,objOnMsgArrived2); 
Delegate [] arrayObjOnMsgArrived;
// Obtain the array of delegate references by invoking GetInvocationList()
arrayObjOnMsgArrived = omc.GetInvocationList(); 
OnMsgArrived objOnMsgArrived3;
// Now navigate through the array and call each delegate which in turn would 
// call each of the methods
for(int i=0; i<arrayObjOnMsgArrived.Length; i++)
{
// Now call each of the delegates
objOnMsgArrived3 = (OnMsgArrived)arrayObjOnMsgArrived[i];
objOnMsgArrived3("string");
}
}
}
The C# Station Tutorial
by Joe Mayo, 8/25/02, updated 3/12/03, 2/22/08, 1/12/09

Lesson 14: Introduction to Delegates and Events

This lesson introduces delegates and events. Our objectives are as follows:

 Understand What a Delegate Is


 Understand What an Event Is
 Implement Delegates
 Fire Events

Delegates

During previous lessons, you learned how to implement reference types


using language constructs such as classes and interfaces. These reference
types allowed you to create instances of objects and use them in special
ways to accomplish your software development goals. Classes allow you to
create objects that contained members with attributes or behavior.
Interfaces allow you to declare a set of attributes and behavior that all
objects implementing them would publicly expose. Today, I'm going to
introduce a new reference type called a delegate.

A delegate is a C# language element that allows you to reference a method.


If you were a C or C++ programmer, this would sound familiar because
a delegate is basically a function pointer. However, developers who have
used other languages are probably wondering, "Why do I need a reference
to a method?". The answer boils down to giving you maximum flexibility to
implement any functionality you want at runtime.

Think about how you use methods right now. You write an algorithm that
does its thing by manipulating the values of variables and calling methods
directly by name. What if you wanted an algorithm that was very flexible,
reusable, and allowed you to implement different functionality as the need
arises? Furthermore, let's say that this was an algorithm that supported
some type of data structure that you wanted to have sorted, but you also
want to enable this data structure to hold different types. If you don't know
what the types are, how could you decide an appropriate comparison
routine?  Perhaps you could implement an if/then/else or switch statement
to handle well-known types, but this would still be limiting and require
overhead to determine the type. Another alternative would be for all the
types to implement an interface that declared a common method your
algorithm would call, which is actually a nice solution. However, since this
lesson is about delegates, we'll apply a delegate solution, which is quite
elegant.

You could solve this problem by passing a delegate to your algorithm and
letting the contained method, which the delegaterefers to, perform the
comparison operation. Such an operation is performed in Listing 14-1.

Lesson 14: Introduction to Delegates and Events

Listing 14-1. Declaring and Implementing a Delegate: SimpleDelegate.cs

using System;

// this is the delegate declaration


public delegate int Comparer(object obj1, object obj2);

public class Name
{
    public string FirstName = null;
    public string LastName = null;

    public Name(string first, string last)
    {
        FirstName = first;
        LastName = last;
    }

    // this is the delegate method handler


    public static int CompareFirstNames(object name1, object name2)
    {
        string n1 = ((Name)name1).FirstName;
        string n2 = ((Name)name2).FirstName;

        if (String.Compare(n1, n2) > 0)


        {
            return 1;
        }
        else if (String.Compare(n1, n2) < 0)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }
    public override string ToString()
    {
        return FirstName + " " + LastName;
    }
}

class SimpleDelegate
{
    Name[] names = new Name[5];

    public SimpleDelegate()
    {
        names[0] = new Name("Joe", "Mayo");
        names[1] = new Name("John", "Hancock");
        names[2] = new Name("Jane", "Doe");
        names[3] = new Name("John", "Doe");
        names[4] = new Name("Jack", "Smith");
    }

    static void Main(string[] args)


    {
        SimpleDelegate sd = new SimpleDelegate();

        // this is the delegate instantiation


        Comparer cmp = new Comparer(Name.CompareFirstNames);

        Console.WriteLine("\nBefore Sort: \n");

        sd.PrintNames();

        // observe the delegate argument


        sd.Sort(cmp);

        Console.WriteLine("\nAfter Sort: \n");

        sd.PrintNames();
    }

    // observe  the delegate parameter


    public void Sort(Comparer compare)
    {
        object temp;

        for (int i=0; i < names.Length; i++)


        {
            for (int j=i; j < names.Length; j++)
            {
                // using delegate "compare" just like
                // a normal method
                if ( compare(names[i], names[j]) > 0 )
                {
                    temp = names[i];
                    names[i] = names[j];
                    names[j] = (Name)temp;
                }
            }
        }
    }

    public void PrintNames()
    {
        Console.WriteLine("Names: \n");

        foreach (Name name in names)


        {
            Console.WriteLine(name.ToString());
        }
    }
}

The first thing the program in Listing 14-1 does is declare


a delegate. Delegate declarations look somewhat like methods, except they
have the delegate modifier, are terminated with a semi-colon (;), and have
no implementation. Below, is thedelegate declaration from Listing 14-1.

public delegate int Comparer(object obj1, object obj2);

This delegate declaration defines the signature of a delegate handler method


that this delegate can refer to. The delegate handler method, for
the Comparer delegate, can have any name, but must have a first
parameter of type object, a second parameter of type object, and return
an int type. The following method from Listing 14-1 shows a delegate
handler method that conforms to the signature of the Comparer delegate.

    public static int CompareFirstNames(object name1, object name2)
    {
        ...
    }

Note: The CompareFirstNames method calls String.Compare to compare


the FirstName properties of the two Nameinstances. The String class has
many convenience methods, such as Compare, for working with strings.
Please don't allow the implementation of this method to interfere with
learning how delegates work. What you should concentrate on is
thatCompareFirstNames is a handler method that a delegate can refer to,
regardless of the code inside of that method.

To use a delegate, you must create an instance of it. The instance is created,
similar to a class instance, with a single parameter identifying the
appropriate delegate handler method, as shown below.
        Comparer cmp = new Comparer(Name.CompareFirstNames);

The delegate, cmp, is then used as a parameter to the Sort() method, which


uses it just like a normal method. Observe the way the delegate is passed to
the Sort() method as a parameter in the code below.

        sd.Sort(cmp);

Using this technique, any delegate handler method may be passed to


the Sort() method at run-time. i.e. You could define a method handler
named CompareLastNames(), instantiate a new Comparer delegate instance
with it, and pass the newdelegate to the Sort() method.

Events

Traditional Console applications operate by waiting for a user to press a key


or type a command and press the Enter key. Then they perform some pre-
defined operation and either quit or return to the original prompt that they
started from. This works, but is inflexible in that everything is hard-wired
and follows a rigid path of execution. In stark contrast, modern GUI
programs operate on an event-based model. That is, some event in the
system occurs and interested modules are notified so they can react
appropriately. With Windows Forms, there is not a polling mechanism taking
up resources and you don't have to code a loop that sits waiting for input. It
is all built into the system with events.

A C# event is a class member that is activated whenever the event it was


designed for occurs. I like to use the term "fires" when the event is
activated. Anyone interested in the event can register and be notified as
soon as the event fires. At the time an event fires, registered methods will
be invoked.

Events and delegates work hand-in-hand to provide a program's


functionality. It starts with a class that declares anevent. Any class,
including the same class that the event is declared in, may register one of its
methods for the event. This occurs through a delegate, which specifies the
signature of the method that is registered for the event. The delegatemay be
one of the pre-defined .NET delegates or one you declare yourself.
Whichever is appropriate, you assign thedelegate to the event, which
effectively registers the method that will be called when the event fires.
Listing 14-2 shows a couple different ways to implement events.
Listing 14-2. Declaring and Implementing Events: Eventdemo.cs

using System;
using System.Drawing;
using System.Windows.Forms;

// custom delegate
public delegate void Startdelegate();

class Eventdemo : Form
{
    // custom event
    public event Startdelegate StartEvent;

    public Eventdemo()
    {
        Button clickMe = new Button();

        clickMe.Parent = this;


        clickMe.Text = "Click Me";
        clickMe.Location = new Point(
            (ClientSize.Width - clickMe.Width) /2,
            (ClientSize.Height - clickMe.Height)/2);

        // an EventHandler delegate is assigned


        // to the button's Click event
        clickMe.Click += new EventHandler(OnClickMeClicked);

        // our custom "Startdelegate" delegate is assigned


        // to our custom "StartEvent" event.
        StartEvent += new Startdelegate(OnStartEvent);

        // fire our custom event


        StartEvent();
    }

    // this method is called when the "clickMe" button is pressed


    public void OnClickMeClicked(object sender, EventArgs ea)
    {
        MessageBox.Show("You Clicked My Button!");
    }

    // this method is called when the "StartEvent" Event is fired


    public void OnStartEvent()
    {
        MessageBox.Show("I Just Started!");
    }

    static void Main(string[] args)


    {
        Application.Run(new Eventdemo());
    }
}
Note: If you're using Visual Studio or another IDE, remember to add
references to System.Drawing.dll and System.Windows.Forms.dll before
compiling Listing 14.2 or just add the code to a Windows Forms project.
Teaching the operation of Visual Studio or other IDE's is out-of-scope for this
tutorial.

You may have noticed that Listing 14-2 is a Windows Forms program.
Although I haven't covered Windows Forms in this tutorial, you should know
enough about C# programming in general that you won't be lost. To help
out, I'll give a brief explanation of some of the parts that you may not be
familiar with.

The Eventdemo class inherits Form, which essentially makes it a Windows


Form. This automatically gives you all the functionality of a Windows Form,
including Title Bar, Minimize/Maximize/Close buttons, System Menu, and
Borders. A lot of power, that inheritance thing, eh?

The way a Windows Form's application is started is by calling


the Run() method of the static Application object with a reference to
the form object as its parameter. This starts up all the underlying Windows
plumbing, displays the GUI, and ensures that events are fired as
appropriate.

Let's look at the custom event first. Below is the event declaration, which is


a member of the Eventdemo class. It is declared with the event keyword,
a delegate type, and an event name.

    public event Startdelegate StartEvent;

Anyone interested in an event can register by hooking up a delegate for


that event. On the next line, we have a delegateof type Startdelegate, which
the event was declared to accept, hooked up to the StartEvent event.
The += syntax registers a delegate with an event. To unregister with
an event, use the -= with the same syntax.

        StartEvent += new Startdelegate(OnStartEvent);

Firing an event looks just like a method call, as shown below:

StartEvent();

This was how to implement events from scratch, declaring


the event and delegate yourself. However, much of the eventprogramming
you'll do will be with pre-defined events and delegates. This leads us to the
other event code you see in Listing 14-2, where we hook up
an EventHandler delegate to a Button Click event.

        clickMe.Click += new EventHandler(OnClickMeClicked);

The Click event already belongs to the Button class and all we have to do is


reference it when registering a delegate. Similarly,
the EventHandler delegate already exists in the System namespace of the
.NET Frameworks Class Library. All you really need to do is define your
callback method (delegate handler method) that is invoked when someone
presses theclickMe button. The OnClickMeClicked() method, shown below,
conforms to the signature of the EventHander delegate, which you can look
up in the .NET Framework Class Library reference.

    public void OnClickMeClicked(object sender, EventArgs ea)


    {
        MessageBox.Show("You Clicked My Button!");
    }

Any time the clickMe button is pressed with a mouse, it will fire


the Click event, which will invoke the OnClickMeClicked()method.
The Button class takes care of firing the Click event and there's nothing
more you have to do. Because it is so easy to use pre-
defined events and delegates, it would be a good idea to check if some exist
already that will do what you need, before creating your own.

Summary

This completes this lesson, which was an introduction


to delegates and events. You learned how to declare and
implementdelegates, which provide dynamic run-time method invocation
services. You also know how to declare events and use them in a couple
different scenarios. One way is to declare your own event, delegate, and
callback method from scratch. Another way is to use pre-
existing events and delegates and only implement the callback method,
which will save you time and make coding easier.

I invite you to return for Lesson 15: Introduction to Exception Handling.

You might also like