Exportar (0) Imprimir
Expandir todo

Routed Events Overview

This topic summarizes how you handle events when developing Windows Presentation Foundation (WPF) applications, how events are routed through a tree of elements, and how to create your own custom events. A routed event is an event that is backed by the RoutedEvent class and the Windows Presentation Foundation (WPF) event system. A routed event can invoke handlers that exist on various listeners in the element tree of an application. This topic gives some more detail about the design, purpose and behavior of routed events, and describes how and when to handle a routed event.

This topic contains the following sections.

Prerequisites

This topic assumes that you have basic knowledge of the common language runtime (CLR) and object-oriented programming, as well as the concept of element trees and relationships between WPF elements. In order to follow the examples in this topic, you should also understand Extensible Application Markup Language (XAML) and know how to write very basic WPF applications. For more information, see Get Started Using Windows Presentation Foundation and XAML Overview.

What is a Routed Event?

A typical WPF application contains many elements. Whether created in code or by loading XAML, these elements exist in an element tree relationship to each other. The routed event model enables you to use the element tree and perpetuate an event along a route after it is raised. The route can travel in one of two directions. The event can invoke handlers on listeners at the element tree root, and then route to successive child elements along the tree node route towards the node element that is the event source. Or, the event can invoke listeners on the source element, and then route to successive parent elements until reaching the element tree root.

Code for a Routed Event

A routed event is a CLR event that is backed by the RoutedEvent class and registered with the WPF event system. The RoutedEvent instance obtained from registration is retained as a public static readonly field member of the class that registers and thus "owns" the routed event. The backing is accomplished by overriding the add and remove implementations for the event, which ordinarily are left as an implicit default that uses the appropriate language-specific event syntax for adding and removing handlers of the event. This is conceptually similar to how a dependency property is a property that is backed by the DependencyProperty class and registered with the WPF property system.

The following example shows the declaration for a Tap routed event, including the registration and creation of identifier field and the add and remove implementations.

public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent("Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));

// Provide CLR accessors for the event
public event RoutedEventHandler Tap
   {
          add { AddHandler(TapEvent, value); } 
          remove { RemoveHandler(TapEvent, value); }
   }

Event Handlers and XAML

To attach a handler for an event using XAML, you declare the event name as an attribute on the element that is an event listener.

<Button Click="b1SetColor">button</Button>

The XAML syntax for events is the same, regardless of whether the event that the handler is attached for is implemented as a routed event. For more information about attaching event handlers in XAML, see XAML Overview.

Why Use Routed Events?

An easily understandable scenario where routed events are useful is if you group a series of controls together that should all interact. The application can be constructed such that the controls share a parent element. The parent element is a common listener for the routed event, and uses the same event handler whenever any of the controls raises a particular event.

For instance, consider what you might do in a small module of a larger application:

A grouped button set


With this module, you would either need to write separate handlers for each button, or use the Microsoft Visual Basic .NET Handles keyword to handle all three instances. By using routed events, you can take advantage of the fact that one of the buttons will raise a bubbling routed event, and can place a common event handler on the parent of all three buttons. The user interface (UI) illustrated above might be constructed in the following way in XAML with codebehind.

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>
  </StackPanel>
</Border>

private void CommonClickHandler(object sender, RoutedEventArgs e)
{
  FrameworkElement feSource = e.Source as FrameworkElement;
  switch (feSource.Name)
  {
    case "YesButton":
      // do something here ...
      break;
    case "NoButton":
      // do something ...
      break;
    case "CancelButton":
      // do something ...
      break;
  }
  e.Handled=true;
}

Because event listeners and event sources do not need to share a common event in their hierarchy, you can use the accumulated total of routed events as a conceptual "interface" whereby different elements in the application can exchange event information. This "interface" concept for routed events is particularly applicable for input events.

Routing Strategies

Routed events use one of three routing strategies:

  • Direct: Only the source element itself is given the opportunity to invoke handlers in response. This is analogous to the "routing" that Windows Forms and other Microsoft .NET libraries use for events.

  • Tunneling: Initially, event handlers at the element tree root are invoked. The event then travels a route through successive child elements along the tree node route, towards the node element that is the event source (the element that raised the event).

  • Bubbling: Event handlers on the event source are invoked. The event then routes to successive parent elements until reaching the element tree root.

The Concept of Handled

All routed events share a common event data base class, RoutedEventArgs. RoutedEventArgs defines the Handled property, which takes a Boolean value. The purpose of the Handled property is to allow any event handler along the route to mark the event as handled by setting the value of Handled to true. After being processed by the handler at one element along the route, the same event data is again reported to each listener along the route. The value of Handled holds special meaning for how an event is reported along the route. If Handled is true in data for a routed event , then handlers that listen for that event on other elements are no longer invoked, for that event instance. This is true either for handlers attached in XAML, or for handlers added by language-specific code event handler syntaxes such as += or Handles. For most common handler scenarios, marking an event as handled by setting Handled to true will "stop" routing for either a tunneling route, or a bubbling route.

However, there is still a mechanism whereby listeners can still invoke handlers in response to routed events where Handled is true in the event data. You can only use this mechanism in code, or in an EventSetter.

  • In code, instead of using a language-specific event syntax that works for general CLR events, use the WPF method AddHandler. Specify the value of handledEventsToo as true.

  • In an EventSetter, set the HandledEventsToo attribute to be true.

In addition to the behavior that Handled state produces in routed events, the concept of Handled has implications for how you should design your application, and write the event handler code. You can think of Handled as being a simple protocol that is exposed by routed events. Exactly how you use this protocol is up to you, but the intended design is as follows:

  • If an event is marked as handled, then it does not need to be handled again.

  • If an event is not marked as handled, then other listeners have chosen either not to register a handler, or the handlers that were registered chose not to manipulate the event data to set Handled to true. (Or, the current listener is the first listener that has the opportunity to handle the event.) In this case, the event can be handled on that listener, or left unhandled to route onward to the next listener.

This intended design is reinforced by the routing behavior mentioned earlier: it is more difficult (although possible in some cases) to attach handlers for routed events that will be invoked even if another handler along the route has set Handled to true.

For more information about Handled, class handling of events, and recommendations about when it is appropriate to mark an event as Handled, see Marking Routed Events as Handled, and Class Handling.

Class Handlers

If you are defining a class, you can also define a class handler for an event that is a declared or inherited event member of your class. Class handlers are invoked before any instance listener handlers for an instance of that class, when that event reaches an element in its route. Some WPF controls have inherent class handling for certain events. This might give the appearance that the event is not being raised, but in reality it is being class handled, and can potentially still be handled by your instance handlers if you use certain techniques. For more information, see Marking Routed Events as Handled, and Class Handling.

Attaching and Implementing an Event Handler for a Routed Event

To attach an event handler in XAML you simply add the event name to an element as an attribute and set the attribute value to name of the your event handler that implements an appropriate delegate, as in the following example.

<Button Click="b1SetColor">button</Button>

b1SetColor contains the code that handles the Click event, and it must have the same signature as the RoutedEventHandler delegate, the event handler delegate for the Click event, as in the following example. The first parameter of an event handler delegate specifies the element to which the event handler is attached, and the second parameter specifies the data for the event.

  void b1SetColor(object sender, RoutedEventArgs args)
  {
    //logic to handle the Click event

...

  }

RoutedEventHandler is the basic routed event handler delegate. For routed events that are more specialized around certain controls or scenarios, the delegates to use for the routed event handlers also might become more specialized. For instance, in a common data binding scenario, you might handle a SourceUpdated event. Your handler should implement the EventHandler delegate. By using the most specific delegate, you can process the DataTransferEventArgs in the handler and read properties in the event data that are relevant to what your handler is attempting to do. In this scenario, you typically need to know the specific Property where the bound data has changed.

For a complete example of how to attach an event handler on an element using XAML, see How to: Set the Background Property of a Button.

Attaching a handler for a routed event in an application that is created in code is also straightforward. Routed events almost always have background implementations of add and remove logic that allow the handlers to be added by a language-specific event syntax. Routed event handlers can also be attached through a helper method AddHandler. The following is an example usage of the helper method:

void MakeButton()
 {
     Button b2 = new Button();
     b2.AddHandler(Button.ClickEvent, new RoutedEventHandler(Onb2Click));
 }
 void Onb2Click(object sender, RoutedEventArgs e)
 {
     //logic to handle the Click event     
 }

The next example shows the C# operator syntax (Microsoft Visual Basic .NET has slightly different operator syntax because of its handling of indirection):

void MakeButton2()
{
  Button b2 = new Button();
  b2.Click += new RoutedEventHandler(Onb2Click2);
}
void Onb2Click2(object sender, RoutedEventArgs e)
{
  //logic to handle the Click event     
}

For an example of how to add an event handler in code, see How to: Add an Event Handler Using Code.

Using Microsoft Visual Basic .NET, you can also use the Handles keyword to assign handlers as part of the handler declarations. For more information, see Visual Basic and WPF Event Handling.

Attached Events

The XAML language also defines a special type of event called an attached event. An attached event allows you to attach a handler for a particular event to some child element rather than to the parent that actually defines the event, even though neither the object potentially raising the event nor the destination handling instance define or otherwise "own" that event in their namespace. In WPF, attached events are common in certain areas where there is service-level abstraction, such as for the events enabled by the static Mouse class. In WPF, attached events are backed by a RoutedEvent field, but do not expose a CLR event that can be used to add and remove handlers. You can add handlers for routed events either through the Add*Handler accessor method on the defining class through code, or by using the typename.eventname attribute usage in XAML.

Qualified Event Names in XAML

Another syntax usage that resembles typename.eventname attached event syntax but is not strictly speaking an attached event usage is when you attach events for child elements to a common parent, to take advantage of routing. Consider this example again:

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>
  </StackPanel>
</Border>

Here, the parent element listener that is actually attaching a handler is a StackPanel. Yet it is attaching a handler for an event that was declared on the Button class (ButtonBase actually, but available to Button through inheritance). Button "owns" the event, but the routed event system permits handlers for any routed event to be attached to any DependencyObject instance listener. The default namescope for these qualified event attribute names is typically the default WPF namespace, but you can also specify prefixed namespaces / namescopes for custom events; for more information, see XAML Namespaces and Namespace Mapping.

Input Events

One application of routed events within the WPF platform is for input events. In WPF, tunneling events are prefixed by the word Preview by convention. Input events often come in pairs, with one being the bubbling event and one being the tunneling event. For example, the KeyDown event and the PreviewKeyDown event have the same signature, with the former being the bubbling input event and the latter being the tunneling input event. Occasional input events only have a bubbling version, or perhaps only a direct version. Within the documentation, event topics will cross-reference to similar events with alternative routing strategies if such events exist, and sections in the reference pages will clarify the routing strategy of each routed event.

Input events that come in pairs are implemented such that a single user action from input such as a mouse button press will raise both events of the pair in sequence. First, the tunneling event is raised and travels its route. Then the bubbling event is raised and travels its route. The two events will literally share the same event data instance. Listeners with handlers for the tunneling event have first opportunity to mark the event handled. If an element along the tunneling route marked the event as handled, the already-handled event data is sent for the bubbling event, and typical handlers attached for the equivalent bubbling events will not be invoked. To outward appearances it will be as if the handled bubbling event had not even been raised.

To illustrate how input event processing works, consider the following input event example. In the following tree illustration, leaf element #2 is the source of both a PreviewMouseDown and then a MouseDown event.

Input Event Bubbling and Tunneling


Event routing diagram

The order of event processing is as follows:

  1. PreviewMouseDown (tunnel) on root element.

  2. PreviewMouseDown (tunnel) on intermediate element #1.

  3. PreviewMouseDown (tunnel) on source element #2.

  4. MouseDown (bubble) on source element #2.

  5. MouseDown (bubble) on intermediate element #1.

  6. MouseDown (bubble) on root element.

A routed event handler delegate provides references to two objects: the object that raised the event, and the object where the handler was invoked. The object where the handler was invoked is the object reported by the sender parameter. The object where the event was first raised is reported by the Source property in the event data. A routed event can still be raised and handled by the same object, in which case sender and Source are identical (this is the case with Steps 3 and 4 in the event processing example list above).

Because of tunneling and bubbling, parent elements receive input events where the Source is one of their child elements. When it is important to know what the source element is, you can identify the source element by accessing the Source property.

Usually, once the input event is marked Handled, further handlers are not invoked. Typically, you should mark input events as handled as soon as a handler is invoked that addresses your application-specific logical handling of the meaning of the input event.

The exception to this general statement about Handled state is that input event handlers that are registered to deliberately ignore Handled state of the event data would still be invoked along either route. For more information, see Preview Events.

The shared event data model between tunneling and bubbling events, and the sequential raising of first tunneling then bubbling events, is not a concept that is generally true for all routed events. That behavior is specifically implemented by how input devices choose to raise and connect the input event pairs. Implementing your own input events is an advanced scenario, but you might choose to follow that model for your own input events also.

Certain classes choose to class-handle certain input events, usually with the intent of redefining what a particular user-driven input event means within that control and raising a new event. For more information, see Marking Routed Events as Handled, and Class Handling.

For more information on input and how input and events interact in typical application scenarios, see Input Overview.

EventSetters and EventTriggers

In styles or in templates, you can include some pre-declared XAML event handling syntax in the markup by using an EventSetter. When the template or style is applied, the referenced handler is added to the templated instance. You can only declare an EventSetter for a routed event. The following is an example. Note that the b1SetColor method referenced here is again in a code-behind file.

<StackPanel
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.EventOvw2"
  Name="dpanel2"
  Initialized="PrimeHandledToo"
>
  <StackPanel.Resources>
    <Style TargetType="{x:Type Button}">
      <EventSetter Event="Click" Handler="b1SetColor"/>
    </Style>
  </StackPanel.Resources>
  <Button>Click me</Button>
  <Button Name="ThisButton" Click="HandleThis">Raise event, handle it, use handled=true handler to get it anyways</Button>
</StackPanel>

The advantage gained here is that the style or template is likely to contain a great deal of other information that could apply to any button in your application, and having the EventSetter be part of that style promotes code reuse even at the markup level. Also it abstracts method names one step further away from the general application page markup.

Another specialized syntax that overlaps the event and animation conceptual areas of WPF is an EventTrigger. As with EventSetter, only routed events may be used for an EventTrigger. Typically an EventTrigger is declared as part of a style or template, but an EventTrigger can also be declared on page-level elements as part of the Triggers collection. An EventTrigger enables you to specify a Storyboard that will run whenever a routed event reaches an element in its route that declared an EventTrigger for that event. The advantage of an EventTrigger over just handling the event and causing it to start an existing storyboard is that an EventTrigger provides better control over the storyboard and its run-time behavior. For more information, see How to: Use Event Triggers to Control a Storyboard After It Starts.

More About Routed Events

This overview mainly discusses routed events from the perspective of describing the basic concepts and offering guidance on how and when to respond to the routed events that are already present in the various base elements and controls. However, routed events are a mechanism that any DependencyObject derived class can also use, and you can create your own routed event on your custom class along with all the necessary support such as specialized event data classes and delegates. For more information about custom events see How to: Create a Custom Routed Event.

See Also

Adiciones de comunidad

AGREGAR
Mostrar:
© 2014 Microsoft