Defining Events for Custom Silverlight Classes

Microsoft Silverlight will reach end of support after October 2021. Learn more.

For any class that you define for Silverlight, you can also define your own events, using the CLR event system conventions. You can also define events for structures or interfaces, although this is less common. Defining a custom event involves the following breakdown of tasks:

  • Define an event member for your custom type.

  • Either define a delegate class that will represent the handlers for your custom event, or re-use an already existing delegate if you can find a suitable one.

  • If you are defining a specialized delegate. define an event data class that the delegate uses.

  • Raise the event from within your own class logic.

  • Optional: Expose a protected virtual method so that derived classes can raise the event or otherwise influence how the base class uses the event

Prerequisites

This topic assumes that you understand events in Silverlight from the perspective of an event consumer, as documented in the topic Events Overview for Silverlight.

This topic also assumes you understand the CLR event system conventions such as the concept of delegates, as documented in the topic Events, Delegates, and CLR Event System Conventions.

Define an Event

  • You define an event member in your class using the event keyword. In most cases you define the event as public, so that user-created instances of the class can handle it.

Part of the event definition is to state the delegate that represents possible handlers of the event. Often, you define a new delegate class, as will be described in the next section. However, you can also re-use an already existing delegate if you can find a suitable delegate that already exists in libraries that your code uses.

For example, the following code defines the CapacityExceeded event for a custom control class named CircularFile:

public class CircularFile : ContentControl {
...
    public event CapacityExceededHandler CapacityExceeded;
}
Public Class CircularFile
Inherits ContentControl
...
    Public Event CapacityExceededHandler CapacityExceeded
End Class

In this example, when the event occurs it is intended to pass its own specific event data, so defining a specialized delegate type that incorporates a custom event data class is required.

NoteNote:

Another technique is to use the EventHandler<TEventArgs> generic delegate for your event. You then constrain the generic form with the type of your specialized event data. For example: public event EventHandler<CapacityExceededEventArgs> CapacityExceeded;

Define the Delegate

To define a delegate, you define a new type, at the type level in code, although the syntax actually more resembles a method definition. The key to this is the delegate keyword.

Delegates for event handlers should generally be public, and should have a void return type (or be defined as Sub with no body in Visual Basic).

Delegates for event handlers should always have two parameters. The first parameter should be of type Object and by convention is named sender, both in delegate definition and also in typical handlers based on the delegate. The second parameter should be a custom class based on EventArgs directly or indirectly, and by convention the parameter is named e.

For example, the following code defines the delegate CapacityExceededHandler, which was referenced in the previous example:

public delegate void CapacityExceededHandler (object sender, CapacityExceededEventArgs e);
Public Delegate Sub CapacityExceededHandler (sender As Object, e As CapacityExceededEventArgs)

Define the Event Data Class

You define an event data class when your event reports at least one property that is unique to the event. For example, our example CapacityExceeded event reports the amount by which capacity is exceeded as a percentage and as an absolute number, which is reported as two different properties:

If you intend your event to be raised by callers, you generally should define a constructor for the event data so that callers can pass specific event data when they raise the event. Alternatively you should make relevant properties settable and provide at least a default constructor. The following example declares an event data class.

public class CapacityExceededEventArgs : EventArgs
{
   private Int32 exceededTotal;
   private Double exceededPercentage;

   public CapacityExceededEventArgs(Int32 exceededTotal, Double exceededPercentage)
   {
      this.exceededTotal = exceededTotal;
      this.exceededPercentage = exceededPercentage;
      // not shown - validation for input
   }

   public Int32 ExceededTotal {
      get { return this.exceededTotal; }
   }
   public Double ExceededPercentage {
      get { return this.exceededPercentage; }
   }
}
TipTip:

Event data classes can also contain methods if that is useful for a scenario For example, the MouseEventArgs class defines a method GetPosition rather than just having a Position property, so that you can pass a parameter to GetPosition and get a position that is relative to the coordinate space of the object you specify.

Raise the Event From Your Class

This task really depends on the nature of what your event and your class are intended to do. The most common scenario is that you use the event to report a change in state for instances of your class. Continuing with the CapacityExceeded example, the logical scenario here is that your class would perform run-time checks on the value of a related property, and raise the event when something notable happened to that property. To raise the event, you must first create an instance of the event data:

...
Int32 overflow = this.CurrentContent - this.Capacity;
CapacityExceededEventArgs newData = new CapacityExceededEventArgs(overflow, (Double) this.CurrentContent / this.Capacity);

Once you have the event data, the simplest way to raise the event is to use a syntax that invokes it:

CapacityExceeded(this, newData);

However, there is an event pattern that is sometimes used for classes that are left open for subclassing that you should consider using instead. This pattern is discussed in the next section.

Implementing the OnEvent Pattern

The OnEvent pattern is documented as a best practice for CLR events, starting in early versions of the .NET Framework. The OnEvent pattern is intended to make it possible for classes that subclass from your class to suppress the event you defined, which is useful if the subclass wants to raise a more specialized event instead. To implement the OnEvent pattern, your class should expose a protected virtual method that does nothing more than invoke the event. The event should be raised based on the event data that is passed as an input parameter, because the derived class might also want to provide its own interpretation of event data when it raises the event. To make this pattern work, your own class should call the virtual method when it raises the event, so that overrides are called when appropriate:

protected virtual void OnCapacityExceeded(CapacityExceededEventArgs e)
{
    if (CapacityExceeded != null) {CapacityExceeded(this, e);}
}
// to raise the event, do this:
...
OnCapacityExceeded(newData);

Custom Routed Events

Silverlight does not support creating a custom routed event. The only routed events available in Silverlight are the ones that are defined by existing Silverlight classes, as listed in the "Routed Events" section of Events Overview for Silverlight.