Click to Rate and Give Feedback
MSDN
MSDN Library
Web Development
Silverlight
 Events and Delegates
Silverlight
Events and Delegates

An event is a message sent by an object to signal the occurrence of an action. The action could be caused by user interaction, such as a mouse click, or it could be triggered by some other program logic, such as an integer variable exceeding or falling below a particular threshold. The object that raises the event is called the event sender. The object that captures the event and responds to it is called the event receiver.

In event communication, the event sender class does not know which object or method will receive (handle) the events it raises. What is needed is an intermediary (or pointer-like mechanism) between the source and the receiver. The .NET Framework defines a special type (Delegate) that provides the functionality of a function pointer.

A delegate is a class that can hold a reference to a method. Unlike other classes, a delegate class has a signature, and it can hold references only to methods that match its signature. A delegate is thus equivalent to a type-safe function pointer or a callback. While delegates have other uses, the discussion here focuses on the event handling functionality of delegates. A delegate declaration is sufficient to define a delegate class. The declaration supplies the signature of the delegate, and the common language runtime provides the implementation. The following example shows an event delegate declaration.

C#
public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);

Visual Basic
Public Delegate Sub AlarmEventHandler(sender As Object, e As AlarmEventArgs)   

The syntax is similar to that of a method declaration; however, the delegate keyword informs the compiler that AlarmEventHandler is a delegate type. By convention, event delegates in the .NET Framework have two parameters, the source that raised the event, represented by the sender parameter, and the data for the event, represented by the e parameter.

An instance of the AlarmEventHandler delegate can bind to any event or method that matches its signature, such as the AlarmRang method of the WakeMeUp class shown in the following example.

C#
public class WakeMeUp 
{
   // Alert has the same signature as AlarmEventHandler.
   public void AlarmRang(object sender, AlarmEventArgs e)
   { 
      // Method implementation. 
   }

   // Other class members.
   //
}

Visual Basic
Public Class WakeMeUp
   ' AlarmRang has the same signature as AlarmEventHandler.
   Public Sub AlarmRang(sender As Object, e As AlarmEventArgs)
      ' Method implementation.
   End Sub

   ' Other class members.
   '
End Class

Custom event delegates are needed only when an event generates event data. For events that do not generate event data, System..::.EventHandler, the delegate provided in the class library for the no-data event, is adequate. Its declaration is as follows.

C#
delegate void EventHandler(object sender, EventArgs e);

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

Event delegates are multicast, which means that they can hold references to more than one event handling method. For details, see Delegate. Delegates allow for flexibility and fine-grain control in event handling. A delegate acts as an event dispatcher for the class that raises the event by maintaining a list of registered event handlers for the event.

Raising an Event

You can define events that are raised by your own classes, structures, and interfaces. The event can then be raised under a specified condition, such as the occurrence of a particular user action, a change in state, or a change in the value of a variable.

Cc189018.alert_note(en-us,VS.95).gifNote:

This section lists the steps involved in raising an event and presents code fragments from a larger example that illustrate each step. Consuming an event is discussed in the Consuming an Event section later in this topic, and the code to handle the events raised by the example code in this section is also presented there.

Defining a custom event requires that you provide three interrelated elements:

  • A class that holds event data. By convention, this class should be named EventNameEventArgs. This class must inherit from System..::.EventArgs. In some cases, it may be possible to use an existing event data class rather than define a custom one. For example, if your event does not use custom data, you can use System..::.EventArgs for your event data.

    The following example defines a class named AlarmEventArgs to hold custom event data. It inherits from EventArgs and adds three additional properties: Rings, which indicates the number of times the alarm should ring when the event is raised; Snooze, which indicates whether the alarm event should be raised repeatedly at a regular interval after it is first raised; and Cancel, which indicates that the alarm event should stop being raised.

    C#
    public class AlarmEventArgs
    {
       private int numberOfRings;
       private bool snoozePressed;
       private bool cancelled = false;
    
       public AlarmEventArgs(bool snoozePressed, int numberOfRings)
       {
          this.snoozePressed = snoozePressed;
          this.numberOfRings = numberOfRings;
       }
    
       public int Rings {
          get { return this.numberOfRings; }
       }
    
       public bool Snooze {
          get { return this.snoozePressed; }
       }
    
       public bool Cancel {
          get { return this.cancelled; }
          set { this.cancelled = value; }
       }
    }
    
    
    Visual Basic
    Public Class AlarmEventArgs : Inherits EventArgs
       Private numberOfRings As Integer
       Private snoozePressed As Boolean
       Private cancelled As Boolean = False
    
       Public Sub New(snoozePressed As Boolean, numberOfRings As Integer)
          Me.snoozePressed = snoozePressed
          Me.numberOfRings = numberOfRings
       End Sub
    
       Public ReadOnly Property Rings As Integer
          Get
             Return Me.numberOfRings
          End Get
       End Property   
    
       Public ReadOnly Property Snooze As Boolean
          Get
             Return Me.snoozePressed
          End Get
       End Property
    
       Public Property Cancel As Boolean
          Get   
             Return Me.Cancelled
          End Get
          Set
             Me.Cancelled = Value
          End Set
       End Property
    End Class
    
    
  • A delegate for the event. By convention, event delegates are named EventNameEventHandler and have two parameters: an Object that indicates the source of the event, and an object derived from System..::.EventArgs that provides information about the event. Depending on the object that provides event data, it may be possible to use an existing event handler. For example, if your event does not use custom event data, you can use EventHandler for your delegate.

    The following example defines an event delegate named AlarmEventArgs that takes two parameters: a reference to the object that raised the event, and an AlarmEventArgs object that contains custom event data.

    C#
    public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);
    
    
    Visual Basic
    Public Delegate Sub AlarmEventHandler(sender As Object, e As AlarmEventArgs)
    
    
  • A class, structure, or interface that provides the event declaration (EventName) and a method that raises the event. You define an event in your class using the event keyword.

    You raise the event by calling the protected OnEventName method in the class that defines the event, or in a derived class. The OnEventName method raises the event by invoking the delegates and passing any event-specific data. The delegate methods for the event can perform actions for the event or process the event-specific data.

    The following example defines an Alarm class. Its members include an AlarmEvent event and three properties, Rings, SnoozePressed, and Cancel, that correspond to the three properties of the AlarmEventArgs class. Members also include a protected OnAlarm method that raises the event and a Start method that is responsible for calling the OnAlarm method at regular intervals.

    C#
    public class AlarmClock
    {
       public event AlarmEventHandler AlarmEvent;
    
       protected int numberOfRings;
       protected bool snooze;
    
       public AlarmClock(int numberOfRings, bool snooze)
       {
          this.numberOfRings = numberOfRings;
          this.snooze = snooze;
       }
    
       public int Rings {
          get { return this.numberOfRings; }
       }
    
       public bool SnoozePressed {
          get { return this.snooze; }
       }
    
       public void OnAlarm(AlarmEventArgs e)
       {
          AlarmEvent(this, e);
       }
    
       public void Start()
       {
          AlarmEventArgs e = new AlarmEventArgs(snooze, numberOfRings);
    
          do {
             System.Threading.Thread.Sleep(1500);
    
             OnAlarm(e);
          } while (! e.Cancel);
       }   
    }
    
    
    Cc189018.alert_note(en-us,VS.95).gifNote:

    The protected OnEventName method also allows derived classes to override the event without attaching a delegate to it. A derived class must always call the OnEventName method of the base class to ensure that registered delegates receive the event. However, in a class that is sealed or NotInheritable, you can raise the event directly rather than raising it indirectly in the OnEventName method.

Consuming an Event

To consume an event in an application, you must provide an event handler (a method that handles the event) that executes program logic in response to the event and register the event handler with the event source. This process is referred to as event wiring. The exact technique used to wire events depends on the language. The following example shows the Visual Basic and C# code for an Example class that handles the AlarmEvent event and that defines an AlarmHandler method that is executed whenever the event is raised. Note that the event handler, by setting the AlarmEventArgs.Cancel property to true, stops the event from being raised repeatedly.

C#
public class Example
{
   private static System.Windows.Controls.TextBlock outputBlock;
   private static AlarmClock alarm;

   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      Example.outputBlock = outputBlock;
      alarm = new AlarmClock(3, true);
      alarm.AlarmEvent += new AlarmEventHandler(Example.AlarmHandler); 
      outputBlock.Text += "Alarm started...\n";
      alarm.Start();   
   }

   private static void AlarmHandler(object sender, AlarmEventArgs e)
   {
      int totalAlarms = 0;

      do {
         for (int ring = 1; ring <= e.Rings; ring++)
            outputBlock.Text += "Ring...";

         outputBlock.Text += "\n";

         if (totalAlarms == 3) 
            e.Cancel = true;

         totalAlarms++;
      } while (! e.Cancel);
   }
}

Visual Basic
Public Class AlarmClock 
   Public Event AlarmEvent As AlarmEventHandler

   Protected numberOfRings As Integer
   Protected snooze As Boolean 

   Public Sub New(numberOfRings As Integer, snooze As Boolean)
      Me.numberOfRings = numberOfRings
      Me.snooze = snooze
   End Sub   

   Public ReadOnly Property Rings As Integer
      Get
         Return Me.numberOfRings
      End Get
   End Property

   Public ReadOnly Property SnoozePressed As Boolean
      Get   
         Return Me.snooze
      End Get   
   End Property   

   Protected Overridable Sub OnAlarm(e As AlarmEventArgs)
      RaiseEvent AlarmEvent(Me, e)
   End Sub

   Public Overridable Sub Start()
      Dim e As New AlarmEventArgs(snooze, numberOfRings)

      Do      
         System.Threading.Thread.Sleep(1500)

         OnAlarm(e)
      Loop While Not e.Cancel 
   End Sub
End Class

Public Module Example
   Public WithEvents alarm As AlarmClock
   Private outputBlock As System.Windows.Controls.TextBlock
   Dim rings As Integer

   Public Sub Demo(outputBlock As System.Windows.Controls.TextBlock)
      Example.outputBlock = outputBlock
      alarm = New AlarmClock(3, True)
      outputBlock.Text += "Alarm started..." + vbCrLf
      alarm.Start()
   End Sub

   Private Sub AlarmHandler(sender As Object, e As AlarmEventArgs) Handles alarm.AlarmEvent
      Dim totalAlarms As Integer
      Do 
         For ring As Integer = 1 To e.Rings
            outputBlock.Text += "Ring..."
         Next
         outputBlock.Text += vbCrLf

         If totalAlarms = 3 Then e.Cancel = True
         totalAlarms += 1
      Loop While Not e.Cancel
   End Sub            
End Module  

Silverlight provides a set of events that enable you to respond to actions, such as changes in Silverlight object states and user input. In the managed API, you write the handlers for events generated within your application, and these handlers are defined in the code-behind file that backs the XAML definition of your application's visual elements.

In Silverlight, there are two general event cases: input events and non-input events. Because Silverlight is built to work within the plug-in architecture of the hosting browser, the input stimulus for input events is initially handled by the browser. The event is then sent to the Silverlight plug-in and generates events in whichever Silverlight API you are using: the managed API or the JavaScript API.

Non-input events typically report a state change to a particular object. For instance, state change events report the asynchronous download state or progress of actions initiated by WebClient (or the JavaScript-only Downloader). MediaElement also has a number of events that are exposed so that you can create transport controls for the media.

There are also some non-input events that provide object lifetime information at a framework level. The most useful of these is FrameworkElement..::.Loaded.

A few events can only be handled on the plug-in instance itself, such as the OnError event. These events are really events within the HTML DOM. These events are exposed and possibly handled by any script working against the plug-in instance in the DOM, so these events are not passed on to the managed API.

Declaring the Programming Model

You declare the programming model that a given page in your Silverlight-based application uses for event handling (and other functionality) at the page level by including or excluding the x:Class attribute on the root element. If the x:Class attribute exists, the page uses the managed API (and the page's XAML is markup compiled). If the x:Class attribute does not exist, the page can use the JavaScript API.

The majority of this topic specifically discusses Silverlight events and the managed API. For a description of the JavaScript API for event handling, see Handling Silverlight Events by Using JavaScript.

The following XAML example shows how to add a handler for the Loaded event on a Canvas object. Notice that the event-handler function cannot be called with parameter values (even empty ones), which is a notable difference from event handler syntax in HTML markup that is used with JavaScript and the HTML DOM, but is the same event handler syntax as used by XAML for WPF and as specified by XAML in general.

<Canvas Loaded="OnLoaded" />

Event handlers for the managed API that are referenced by using the preceding XAML attribute syntax must be defined in the partial class that serves as the code-behind for a XAML page.

Event handlers in the partial class are written as methods, based on the CLR delegates that are used by that particular event. Your event handler methods can be public, or they can have a private access level. Private access works because the handler and instance created by the XAML are ultimately joined by code generation. The general recommendation is to not make your event handler methods public in the class.

Although there are numerous delegates for the various Silverlight events, as well as matching dedicated event-data classes, the event handler for a Silverlight event always references two parameters in its signature, the sender object and the event data, as described in Raising an Event earlier in this topic.

For some events, the event data in the EventArgs derived class is as important as knowing that the event was raised. This is particularly true of the input events. For mouse events, the position of the mouse when the event occurred might be important. For keyboard events, keys on the keyboard raise the same KeyUp and KeyDown events. In order to determine which key was pressed, you must access the KeyEventArgs that is available to the event handler. The event-data type that is available to a handler for a Silverlight event is documented as part of the syntax of the relevant delegate.

To add event-handler functions in managed code, you use the CLR-language-specific syntax for adding event handlers. If you are adding event handlers in code, a common practice for Silverlight (shown in the example) is to add such handlers in response to a Loaded event, so that the event handlers on the relevant object are ready for user-initiated events at run time. In C#, the syntax is to use the += operator, and to instantiate the handler by declaring a new delegate using the event handler method name.

C#
void OnLoaded(object sender, RoutedEventArgs e)
{
    TextBlock tb= sender as TextBlock;
    tb.MouseEnter += new MouseEventHandler(OnMouseEnter);
    tb.MouseLeave += new MouseEventHandler(OnMouseLeave);
}

There are two possibilities for Visual Basic syntax. One is to parallel the C# syntax and attach handlers directly to instances. This requires the AddHandler function as well as the AddressOf operator that dereferences the handler method name.

Visual Basic
Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs e)
    Dim tb as TextBlock = CType(sender,TextBlock)
    AddHandler tb.MouseEnter, AddressOf OnMouseEnter
    AddHandler tb.MouseEnter, AddressOf OnMouseEnter
End Sub

The other option for Visual Basic syntax is to use the Handles keyword on event handlers. This technique is appropriate for cases where handlers are expected to exist on objects at load time and persist throughout the object lifetime. Using Handles on an object that is defined in XAML requires that you provide a Name / x:Name for the object element in XAML that defines the object. This name becomes the instance qualifier that is needed for the Instance.Event value you provide for the Handles information. As part of the XAML code generation behavior for compiled XAML, a Friend WithEvents field reference is created for any object with a Name declared in the XAML, so that such a reference is compatible with the Handles syntax.

    <TextBlock Name="tb" ... />
Visual Basic
Sub OnMouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs) Handles tb.MouseEnter
'....
End Sub
Sub OnMouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs) Handles tb.MouseLeave
'....
End Sub

Removing Event Handlers in Managed Code

In some circumstances, you might want to remove event handlers during the application lifetime. To remove event-handler functions in managed code, you use the CLR-language-specific syntax for removing event handlers. In C#, the syntax is to use the -= operator, referencing the event handler method name. In Visual Basic, use the RemoveHandler function.

C#
void OnLoaded(object sender, RoutedEventArgs e)
{
    TextBlock tb= sender as TextBlock;
    tb.MouseEnter -= OnMouseEnter;
    tb.MouseLeave -= OnMouseLeave;
}
Visual Basic
Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs e)
    Dim tb as TextBlock = CType(sender,TextBlock)
    RemoveHandler tb.MouseEnter, AddressOf OnMouseEnter
    RemoveHandler tb.MouseEnter, AddressOf OnMouseEnter
End Sub

You can also remove handlers for cases where the event was added through a XAML attribute.

Silverlight supports the concept of a routed event for several framework-level input events:

A routed event is an event that is passed from a child object to each of its successive parent objects in the object tree. The object tree is approximated by the XAML structure that is used to declare objects. The true object tree might vary somewhat from the XAML because the object tree does not include XAML language features such as property element tags, but in general you can think of the events as "bubbling" from XAML object element children towards the parent object element that contains them, and doing so continuously until the root element is reached. For more information on this "tree" concept, see Silverlight Object Trees.

Cc189018.alert_note(en-us,VS.95).gifNote:

WPF supports an analogous "tunneling" routing strategy, whereby the root of a page / object tree has the first chance to handle a routed event, and the event then "tunnels" down the object tree toward its event source. Silverlight does not support the "tunneling" routed event concept. Events in Silverlight either follow the "bubbling" routing strategy (and are referred to as routed events) or do not route at all.

The OriginalSource Property

When an event bubbles up an event route, sender is no longer the same object as the event-raising object. Instead, sender is the object where the handler that is being invoked is attached. In many cases, sender is not the object of interest, and you are instead interested in knowing which of the possible child objects the mouse is over, or which object held focus when a keyboard key was pressed. For these cases, the value of the OriginalSource property is the object of interest.

At all points on the route, OriginalSource reports the original object that raised the event. For an example scenario where this is useful, consider an application where you want certain key combinations to be "hot keys" or accelerators, regardless of which control currently holds keyboard focus. In terms of the object tree, the focused object might be nested within some items list in a list box, or could be one of hundreds of objects in the overall UI. Having to attach handlers to every possible focusable object in an application to detect your accelerator is obviously impractical. But because the keyboard events are bubbling routed events, the event eventually reaches the root object in its route. Therefore, you can often attach a single KeyDown handler on the root object and rely on the behavior that a KeyDown event eventually bubbles to the root object. Then the handler can determine whether that key is the valid combination for an intended accelerator action, or whether no action is necessary.

The Handled Property

Several event-data classes for specific routed events that bubble contain a property named Handled. For instance, see MouseButtonEventArgs..::.Handled, KeyEventArgs..::.Handled, and ValidationErrorEventArgs..::.Handled. Handled is a settable Boolean property. The concept of a Handled property for routed events also exists in WPF, but not all features apply to the Silverlight event model.

In WPF, the Handled property exists on all routed event-data classes, and setting the value to true influences the way that the event continues in its route.

In Silverlight, specific event-data classes for events that route each define their own Handled property, but the event system treats all Handled cases the same way. Setting the Handled property to true also influences the event system in Silverlight. When you set the value to true in event data, the routing stops; the event does not continue along the route to notify other attached handlers of that particular event case. What "handled" means in the context of the event and how your application responds is entirely up to you, but you should be aware of this behavior of the Silverlight event system when you choose to set Handled in your event handlers.

Not all routed events are cancellable in this way. GotFocus, LostFocus, and MouseMove will each route all the way to the root, and their event data classes do not have a Handled property that can influence that behavior. Therefore, checking OriginalSource values for those events is often a good idea, to make sure you are not handling an event and making incorrect assumptions about what the event means.

Cc189018.alert_note(en-us,VS.95).gifNote:

WPF has an additional available behavior that can cause event routing to continue even if Handled is set to true in an event handler. For this behavior, only handlers registered with the special handledEventsToo value are invoked on elements further along a Handled route. Silverlight does not have an equivalent behavior to handledEventsToo, and there is no API for registering handlers in this way. If you set Handled to true in Silverlight, the event will not invoke handlers further along the route.

Silverlight 2 does not support creating a custom routed event. The only routed events are those events that are defined by Silverlight, listed in the "Routed Events" section.

Specific Silverlight controls sometimes use this Handled concept for input events internally. This can give the appearance from user code that an event never occurs. For example, the Button class includes logic that deliberately handles the general input event MouseLeftButtonDown. It does so because the class raises a Click event that is initiated by that mouse input. For purposes of the class design the raw mouse event is handled, and class consumers should instead choose to handle or not handle Click. Reference pages for specific controls often note the event handling behavior implemented by the class. In some cases, the behavior can be altered or appended in subclasses by overriding OnEvent methods. For example, you can change how your TextBox subclass reacts to key input by overriding TextBox..::.OnKeyDown.

Tags What's this?: Add a tag
Community Content   What is Community Content?
Add new content RSS  Annotations
Processing
© 2008 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Page view tracker