달력

2

« 2025/2 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
2016. 11. 1. 19:22

Events 프로그래밍/WPF2016. 11. 1. 19:22

Events



Routed Events Overview


https://msdn.microsoft.com/ko-kr/library/ms742806(v=vs.110).aspx

https://msdn.microsoft.com/en-us/library/ms742806(v=vs.110).aspx


This topic describes the concept of routed events in Windows Presentation Foundation (WPF). The topic defines routed events terminology, describes how routed events are routed through a tree of elements, summarizes how you handle routed events, and introduces how to create your own custom routed events. 

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 how the relationships between WPF elements can be conceptualized as a tree. 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 or pages. For more information, see Walkthrough: My First WPF Desktop Application and XAML Overview (WPF).

What Is a Routed Event?

You can think about routed events either from a functional or implementation perspective. Both definitions are presented here, because some people find one or the other definition more useful.

Functional definition: A routed event is a type of event that can invoke handlers on multiple listeners in an element tree, rather than just on the object that raised the event.

Implementation definition: A routed event is a CLR event that is backed by an instance of the RoutedEvent class and is processed by the Windows Presentation Foundation (WPF) event system.

A typical WPF application contains many elements. Whether created in code or declared in XAML, these elements exist in an element tree relationship to each other. The event route can travel in one of two directions depending on the event definition, but generally the route travels from the source element and then "bubbles" upward through the element tree until it reaches the element tree root (typically a page or a window). This bubbling concept might be familiar to you if you have worked with the DHTML object model previously.

Consider the following simple element tree:

<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>

This element tree produces something like the following:

Yes, No, and Cancel buttons

In this simplified element tree, the source of a Click event is one of the Button elements, and whichever Button was clicked is the first element that has the opportunity to handle the event. But if no handler attached to the Button acts on the event, then the event will bubble upwards to the Buttonparent in the element tree, which is the StackPanel. Potentially, the event bubbles to Border, and then beyond to the page root of the element tree (not shown).

In other words, the event route for this Click event is:

Button-->StackPanel-->Border-->...

Top-level Scenarios for Routed Events

The following is a brief summary of the scenarios that motivated the routed event concept, and why a typical CLR event was not adequate for these scenarios:

Control composition and encapsulation: Various controls in WPF have a rich content model. For example, you can place an image inside of a Button, which effectively extends the visual tree of the button. However, the added image must not break the hit-testing behavior that causes a button to respond to a Click of its content, even if the user clicks on pixels that are technically part of the image.

Singular handler attachment points: In Windows Forms, you would have to attach the same handler multiple times to process events that could be raised from multiple elements. Routed events enable you to attach that handler only once, as was shown in the previous example, and use handler logic to determine where the event came from if necessary. For instance, this might be the handler for the previously shown XAML:

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;
}

Class handling: Routed events permit a static handler that is defined by the class. This class handler has the opportunity to handle an event before any attached instance handlers can.

Referencing an event without reflection: Certain code and markup techniques require a way to identify a specific event. A routed event creates a RoutedEvent field as an identifier, which provides a robust event identification technique that does not require static or run-time reflection.

How Routed Events Are Implemented

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

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

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); }
}

Routed Event Handlers and XAML

To add a handler for an event using XAML, you declare the event name as an attribute on the element that is an event listener. The value of the attribute is the name of your implemented handler method, which must exist in the partial class of the code-behind file.

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

The XAML syntax for adding standard CLR event handlers is the same for adding routed event handlers, because you are really adding handlers to the CLR event wrapper, which has a routed event implementation underneath. For more information about adding event handlers in XAML, see XAML Overview (WPF).

Routing Strategies

Routed events use one of three routing strategies:

  • Bubbling: Event handlers on the event source are invoked. The routed event then routes to successive parent elements until reaching the element tree root. Most routed events use the bubbling routing strategy. Bubbling routed events are generally used to report input or state changes from distinct controls or other UI elements.

  • Direct: Only the source element itself is given the opportunity to invoke handlers in response. This is analogous to the "routing" that Windows Forms uses for events. However, unlike a standard CLR event, direct routed events support class handling (class handling is explained in an upcoming section) and can be used by EventSetter and EventTrigger.

  • Tunneling: Initially, event handlers at the element tree root are invoked. The routed event then travels a route through successive child elements along the route, towards the node element that is the routed event source (the element that raised the routed event). Tunneling routed events are often used or handled as part of the compositing for a control, such that events from composite parts can be deliberately suppressed or replaced by events that are specific to the complete control. Input events provided in WPF often come implemented as a tunneling/bubbling pair. Tunneling events are also sometimes referred to as Preview events, because of a naming convention that is used for the pairs.

Why Use Routed Events?

As an application developer, you do not always need to know or care that the event you are handling is implemented as a routed event. Routed events have special behavior, but that behavior is largely invisible if you are handling an event on the element where it is raised.

Where routed events become powerful is if you use any of the suggested scenarios: defining common handlers at a common root, compositing your own control, or defining your own custom control class.

Routed event listeners and routed event sources do not need to share a common event in their hierarchy. Any UIElement or ContentElement can be an event listener for any routed event. Therefore, you can use the full set of routed events available throughout the working API set as a conceptual "interface" whereby disparate elements in the application can exchange event information. This "interface" concept for routed events is particularly applicable for input events.

Routed events can also be used to communicate through the element tree, because the event data for the event is perpetuated to each element in the route. One element could change something in the event data, and that change would be available to the next element in the route.

Other than the routing aspect, there are two other reasons that any given WPF event might be implemented as a routed event instead of a standard CLR event. If you are implementing your own events, you might also consider these principles:

  • Certain WPF styling and templating features such as EventSetter and EventTrigger require the referenced event to be a routed event. This is the event identifier scenario mentioned earlier.

  • Routed events support a class handling mechanism whereby the class can specify static methods that have the opportunity to handle routed events before any registered instance handlers can access them. This is very useful in control design, because your class can enforce event-driven class behaviors that cannot be accidentally suppressed by handling an event on an instance.

Each of the above considerations is discussed in a separate section of this topic.

Adding and Implementing an Event Handler for a Routed Event

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

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

b1SetColor is the name of the implemented handler that contains the code that handles the Click event. b1SetColor must have the same signature as the RoutedEventHandler delegate, which is the event handler delegate for the Click event. The first parameter of all routed event handler delegates specifies the element to which the event handler is added, 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 specialized for certain controls or scenarios, the delegates to use for the routed event handlers also might become more specialized, so that they can transmit specialized event data. For instance, in a common input scenario, you might handle a DragEnter routed event. Your handler should implement the DragEventHandler delegate. By using the most specific delegate, you can process the DragEventArgs in the handler and read the Data property, which contains the clipboard payload of the drag operation.

For a complete example of how to add an event handler to an element using XAML, see How to: Handle a Routed Event.

Adding a handler for a routed event in an application that is created in code is straightforward. Routed event handlers can always be added through a helper method AddHandler (which is the same method that the existing backing calls for add.) However, existing WPF routed events generally have backing implementations of add and remove logic that allow the handlers for routed events to be added by a language-specific event syntax, which is more intuitive syntax than the helper method. 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 (Visual Basic has slightly different operator syntax because of its handling of dereferencing):

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.

If you are using Visual Basic, you can also use the Handles keyword to add handlers as part of the handler declarations. For more information, see Visual Basic and WPF Event Handling.

The Concept of Handled

All routed events share a common event data base class, RoutedEventArgsRoutedEventArgs defines the Handled property, which takes a Boolean value. The purpose of the Handled property is to enable any event handler along the route to mark the routed event as handled, by setting the value of Handled to true. After being processed by the handler at one element along the route, the shared event data is again reported to each listener along the route.

The value of Handled affects how a routed event is reported or processed as it travels further along the route. If Handled is true in the event data for a routed event, then handlers that listen for that routed event on other elements are generally no longer invoked for that particular event instance. This is true both for handlers attached in XAML and for handlers added by language-specific event handler attachment 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, and also for any event that is handled at a point in the route by a class handler.

However, there is a "handledEventsToo" mechanism whereby listeners can still run handlers in response to routed events where Handled is true in the event data. In other words, the event route is not truly stopped by marking the event data as handled. You can only use the handledEventsToo mechanism in code, or in an EventSetter:

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 conceptualize Handled as being a simple protocol that is exposed by routed events. Exactly how you use this protocol is up to you, but the conceptual design for how the value of Handled is intended to be used is as follows:

  • If a routed event is marked as handled, then it does not need to be handled again by other elements along that route.

  • If a routed event is not marked as handled, then other listeners that were earlier along the route have chosen either not to register a handler, or the handlers that were registered chose not to manipulate the event data and set Handled to true. (Or, it is of course possible that the current listener is the first point in the route.) Handlers on the current listener now have three possible courses of action:

    • Take no action at all; the event remains unhandled, and the event routes to the next listener.

    • Execute code in response to the event, but make the determination that the action taken was not substantial enough to warrant marking the event as handled. The event routes to the next listener.

    • Execute code in response to the event. Mark the event as handled in the event data passed to the handler, because the action taken was deemed substantial enough to warrant marking as handled. The event still routes to the next listener, but with Handled=true in its event data, so only handledEventsToo listeners have the opportunity to invoke further handlers.

This conceptual design is reinforced by the routing behavior mentioned earlier: it is more difficult (although still possible in code or styles) to attach handlers for routed events that are invoked even if a previous handler along the route has already set Handled to true.

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

In applications, it is quite common to just handle a bubbling routed event on the object that raised it, and not be concerned with the event's routing characteristics at all. However, it is still a good practice to mark the routed event as handled in the event data, to prevent unanticipated side effects just in case an element that is further up the element tree also has a handler attached for that same routed event.

Class Handlers

If you are defining a class that derives in some way from DependencyObject, you can also define and attach a class handler for a routed event that is a declared or inherited event member of your class. Class handlers are invoked before any instance listener handlers that are attached to an instance of that class, whenever a routed event reaches an element instance in its route.

Some WPF controls have inherent class handling for certain routed events. This might give the outward appearance that the routed event is not ever raised, but in reality it is being class handled, and the routed event can potentially still be handled by your instance handlers if you use certain techniques. Also, many base classes and controls expose virtual methods that can be used to override class handling behavior. For more information both on how to work around undesired class handling and on defining your own class handling in a custom class, see Marking Routed Events as Handled, and Class Handling.

Attached Events in WPF

The XAML language also defines a special type of event called an attached event. An attached event enables you to add a handler for a particular event to an arbitrary element. The element handling the event need not define or inherit the attached event, and neither the object potentially raising the event nor the destination handling instance must define or otherwise "own" that event as a class member.

The WPF input system uses attached events extensively. However, nearly all of these attached events are forwarded through base elements. The input events then appear as equivalent non-attached routed events that are members of the base element class. For instance, the underlying attached event Mouse.MouseDown can more easily be handled on any given UIElement by using MouseDown on that UIElement rather than dealing with attached event syntax either in XAML or code.

For more information about attached events in WPF, see Attached Events Overview.

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 handlers for routed events that are raised by child elements. You attach the handlers to a common parent, to take advantage of event routing, even though the common parent might not have the relevant routed event as a member. 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 where the handler is added is a StackPanel. However, it is adding a handler for a routed event that was declared and will be raised by 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 UIElement or ContentElement instance listener that could otherwise attach listeners for a common language runtime (CLR) event. The default xmlns namespace for these qualified event attribute names is typically the default WPF xmlns namespace, but you can also specify prefixed namespaces for custom routed events. For more information about xmlns, see XAML Namespaces and Namespace Mapping for WPF XAML.

WPF Input Events

One frequent application of routed events within the WPF platform is for input events. In WPF, tunneling routed events names are prefixed with the word "Preview" by convention. Input events often come in pairs, with one being the bubbling event and the other 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. Occasionally, input events only have a bubbling version, or perhaps only a direct routed version. In the documentation, routed event topics cross-reference similar routed events with alternative routing strategies if such routed events exist, and sections in the managed reference pages clarify the routing strategy of each routed event.

WPF input events that come in pairs are implemented so that a single user action from input, such as a mouse button press, will raise both routed 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 literally share the same event data instance, because the RaiseEvent method call in the implementing class that raises the bubbling event listens for the event data from the tunneling event and reuses it in the new raised event. Listeners with handlers for the tunneling event have the first opportunity to mark the routed event handled (class handlers first, then instance handlers). If an element along the tunneling route marked the routed event as handled, the already-handled event data is sent on for the bubbling event, and typical handlers attached for the equivalent bubbling input events will not be invoked. To outward appearances it will be as if the handled bubbling event has not even been raised. This handling behavior is useful for control compositing, where you might want all hit-test based input events or focus-based input events to be reported by your final control, rather than its composite parts. The final control element is closer to the root in the compositing, and therefore has the opportunity to class handle the tunneling event first and perhaps to "replace" that routed event with a more control-specific event, as part of the code that backs the control class.

As an illustration of 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.

Event routing diagram

Input Event Bubbling and Tunneling

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).

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 or Marking Routed Events as Handled, and Class Handling.

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 WPF 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, you can include some pre-declared XAML event handling syntax in the markup by using an EventSetter. When the style is applied, the referenced handler is added to the styled instance. You can declare an EventSetter only for a routed event. The following is an example. Note that the b1SetColor method referenced here is 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 anyway.
  </Button>
</StackPanel>

The advantage gained here is that the style 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, an EventSetter abstracts method names for handlers one step further away from the general application and page markup.

Another specialized syntax that combines the routed event and animation features 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, but an EventTrigger can also be declared on page-level elements as part of the Triggers collection, or in a ControlTemplate. An EventTrigger enables you to specify a Storyboard that runs whenever a routed event reaches an element in its route that declares 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 topic 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, 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. The routed event owner can be any class, but routed events must be raised by and handled by UIElement or ContentElement derived classes in order to be useful. For more information about custom events, see How to: Create a Custom Routed Event.       




Attached Events Overview


Extensible Application Markup Language (XAML) defines a language component and type of event called an attached event. The concept of an attached event enables you to add a handler for a particular event to an arbitrary element rather than to an element that actually defines or inherits the event. In this case, neither the object potentially raising the event nor the destination handling instance defines or otherwise "owns" the event. 

Prerequisites

This topic assumes that you have read Routed Events Overview and XAML Overview (WPF).

Attached Event Syntax

Attached events have a XAML syntax and a coding pattern that must be used by the backing code in order to support the attached event usage.

In XAML syntax, the attached event is specified not just by its event name, but by its owning type plus the event name, separated by a dot (.). Because the event name is qualified with the name of its owning type, the attached event syntax allows any attached event to be attached to any element that can be instantiated.

For example, the following is the XAML syntax for attaching a handler for a custom NeedsCleaning attached event:

<aqua:Aquarium Name="theAquarium" Height="600" Width="800" aqua:AquariumFilter.NeedsCleaning="WashMe"/>

Note the aqua: prefix; the prefix is necessary in this case because the attached event is a custom event that comes from a custom mapped xmlns.

How WPF Implements Attached Events

In WPF, attached events are backed by a RoutedEvent field and are routed through the tree after they are raised. Typically, the source of the attached event (the object that raises the event) is a system or service source, and the object that runs the code that raises the event is therefore not a direct part of the element tree.

Scenarios for Attached Events

In WPF, attached events are present in certain feature areas where there is service-level abstraction, such as for the events enabled by the static Mouse class or the Validation class. Classes that interact with or use the service can either use the event in the attached event syntax, or they can choose to surface the attached event as a routed event that is part of how the class integrates the capabilities of the service.

Although WPF defines a number of attached events, the scenarios where you will either use or handle the attached event directly are very limited. Generally, the attached event serves an architecture purpose, but is then forwarded to a non-attached (backed with a CLR event "wrapper") routed event.

For instance, the underlying attached event Mouse.MouseDown can more easily be handled on any given UIElement by using MouseDown on that UIElement rather than dealing with attached event syntax either in XAML or code. The attached event serves a purpose in the architecture because it allows for future expansion of input devices. The hypothetical device would only need to raise Mouse.MouseDown in order to simulate mouse input, and would not need to derive from Mouse to do so. However, this scenario involves code handling of the events, and XAML handling of the attached event is not relevant to this scenario.

Handling an Attached Event in WPF

The process for handling an attached event, and the handler code that you will write, is basically the same as for a routed event.

In general, a WPF attached event is not very different from a WPF routed event. The differences are how the event is sourced and how it is exposed by a class as a member (which also affects the XAML handler syntax).

However, as noted earlier, the existing WPF attached events are not particularly intended for handling in WPF. More often, the purpose of the event is to enable a composited element to report a state to a parent element in compositing, in which case the event is usually raised in code and also relies on class handling in the relevant parent class. For instance, items within a Selector are expected to raise the attached Selected event, which is then class handled by the Selector class and then potentially converted by the Selector class into a different routed event, SelectionChanged. For more information on routed events and class handling, see Marking Routed Events as Handled, and Class Handling.

Defining Your Own Attached Events as Routed Events

If you are deriving from common WPF base classes, you can implement your own attached events by including certain pattern methods in your class and by using utility methods that are already present on the base classes.

The pattern is as follows:

  • A method Add*Handler with two parameters. The first parameter must identify the event, and the identified event must match names with the * in the method name. The second parameter is the handler to add. The method must be public and static, with no return value.

  • A method Remove*Handler with two parameters. The first parameter must identify the event, and the identified event must match names with the * in the method name. The second parameter is the handler to remove. The method must be public and static, with no return value.

The Add*Handler accessor method facilitates the XAML processing when attached event handler attributes are declared on an element. The Add*Handler and Remove*Handler methods also enable code access to the event handler store for the attached event.

This general pattern is not yet precise enough for practical implementation in a framework, because any given XAML reader implementation might have different schemes for identifying underlying events in the supporting language and architecture. This is one of the reasons that WPF implements attached events as routed events; the identifier to use for an event (RoutedEvent) is already defined by the WPF event system. Also, routing an event is a natural implementation extension on the XAML language-level concept of an attached event.

The Add*Handler implementation for a WPF attached event consists of calling the AddHandler with the routed event and handler as arguments.

This implementation strategy and the routed event system in general restrict handling for attached events to either UIElement derived classes or ContentElement derived classes, because only those classes have AddHandler implementations.

For example, the following code defines the NeedsCleaning attached event on the owner class Aquarium, using the WPF attached event strategy of declaring the attached event as a routed event.

public static readonly RoutedEvent NeedsCleaningEvent = EventManager.RegisterRoutedEvent("NeedsCleaning", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AquariumFilter));
public static void AddNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
{
    UIElement uie = d as UIElement;
    if (uie != null)
    {
        uie.AddHandler(AquariumFilter.NeedsCleaningEvent, handler);
    }
}
public static void RemoveNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
{
    UIElement uie = d as UIElement;
    if (uie != null)
    {
        uie.RemoveHandler(AquariumFilter.NeedsCleaningEvent, handler);
    }
}

Note that the method used to establish the attached event identifier field, RegisterRoutedEvent, is actually the same method that is used to register a non-attached routed event. Attached events and routed events all are registered to a centralized internal store. This event store implementation enables the "events as an interface" conceptual consideration that is discussed in Routed Events Overview.

Raising a WPF Attached Event

You do not typically need to raise existing WPF defined attached events from your code. These events follow the general "service" conceptual model, and service classes such as InputManager are responsible for raising the events.

However, if you are defining a custom attached event based on the WPF model of basing attached events on RoutedEvent, you can use RaiseEvent to raise an attached event from any UIElement or ContentElement. Raising a routed event (attached or not) requires that you declare a particular element in the element tree as the event source; that source is reported as the RaiseEvent caller. Determining which element is reported as the source in the tree is your service's responsibility




Object Lifetime Events


This topic describes the specific WPF events that signify stages in an object lifetime of creation, use, and destruction.

Prerequisites

This topic assumes that you understand dependency properties from the perspective of a consumer of existing dependency properties on Windows Presentation Foundation (WPF) classes, and have read the Dependency Properties Overview topic. In order to follow the examples in this topic, you should also understand Extensible Application Markup Language (XAML) (see XAML Overview (WPF)) and know how to write WPF applications.

Object Lifetime Events

All objects in Microsoft .NET Framework managed code go through a similar set of stages of life, creation, use, and destruction. Many objects also have a finalization stage of life that occurs as part of the destruction phase. WPF objects, more specifically the visual objects that WPF identifies as elements, also have a set of common stages of object life. The WPF programming and application models expose these stages as a series of events. There are four main types of objects in WPF with respect to lifetime events; elements in general, window elements, navigation hosts, and application objects. Windows and navigation hosts are also within the larger grouping of visual objects (elements). This topic describes the lifetime events that are common to all elements and then introduces the more specific ones that apply to application definitions, windows or navigation hosts.

Common Lifetime Events for Elements

Any WPF framework-level element (those objects deriving from either FrameworkElement or FrameworkContentElement) has three common lifetime events: InitializedLoaded, and Unloaded.

Initialized

Initialized is raised first, and roughly corresponds to the initialization of the object by the call to its constructor. Because the event happens in response to initialization, you are guaranteed that all properties of the object are set. (An exception is expression usages such as dynamic resources or binding; these will be unevaluated expressions.) As a consequence of the requirement that all properties are set, the sequence of Initialized being raised by nested elements that are defined in markup appears to occur in order of deepest elements in the element tree first, then parent elements toward the root. This order is because the parent-child relationships and containment are properties, and therefore the parent cannot report initialization until the child elements that fill the property are also completely initialized.

When you are writing handlers in response to the Initialized event, you must consider that there is no guarantee that all other elements in the element tree (either logical tree or visual tree) around where the handler is attached have been created, particularly parent elements. Member variables may be null, or data sources might not yet be populated by the underlying binding (even at the expression level).

Loaded

Loaded is raised next. The Loaded event is raised before the final rendering, but after the layout system has calculated all necessary values for rendering. Loaded entails that the logical tree that an element is contained within is complete, and connects to a presentation source that provides the HWND and the rendering surface. Standard data binding (binding to local sources, such as other properties or directly defined data sources) will have occurred prior to Loaded. Asynchronous data binding (external or dynamic sources) might have occurred, but by definition of its asynchronous nature cannot be guaranteed to have occurred.

The mechanism by which the Loaded event is raised is different than Initialized. The Initialized event is raised element by element, without a direct coordination by a completed element tree. By contrast, the Loaded event is raised as a coordinated effort throughout the entire element tree (specifically, the logical tree). When all elements in the tree are in a state where they are considered loaded, the Loaded event is first raised on the root element. The Loaded event is then raised successively on each child element.

System_CAPS_noteNote

This behavior might superficially resemble tunneling for a routed event. However, no information is carried from event to event. Each element always has the opportunity to handle its Loaded event, and marking the event data as handled has no effect beyond that element.

Unloaded

Unloaded is raised last and is initiated by either the presentation source or the visual parent being removed. When Unloaded is raised and handled, the element that is the event source parent (as determined by Parent property) or any given element upwards in the logical or visual trees may have already been unset, meaning that data binding, resource references, and styles may not be set to their normal or last known run-time value.

Lifetime Events Application Model Elements

Building on the common lifetime events for elements are the following application model elements: ApplicationWindowPageNavigationWindow, and Frame. These extend the common lifetime events with additional events that are relevant to their specific purpose. These are discussed in detail in the following locations:




Marking Routed Events as Handled, and Class Handling


Handlers for a routed event can mark the event handled within the event data. Handling the event will effectively shorten the route. Class handling is a programming concept that is supported by routed events. A class handler has the opportunity to handle a particular routed event at a class level with a handler that is invoked before any instance handler on any instance of the class.

Prerequisites

This topic elaborates on concepts introduced in the Routed Events Overview.

When to Mark Events as Handled

When you set the value of the Handled property to true in the event data for a routed event, this is referred to as "marking the event handled". There is no absolute rule for when you should mark routed events as handled, either as an application author, or as a control author who responds to existing routed events or implements new routed events. For the most part, the concept of "handled" as carried in the routed event's event data should be used as a limited protocol for your own application's responses to the various routed events exposed in WPF APIs as well as for any custom routed events. Another way to consider the "handled" issue is that you should generally mark a routed event handled if your code responded to the routed event in a significant and relatively complete way. Typically, there should not be more than one significant response that requires separate handler implementations for any single routed event occurrence. If more responses are needed, then the necessary code should be implemented through application logic that is chained within a single handler rather than by using the routed event system for forwarding. The concept of what is "significant" is also subjective, and depends on your application or code. As general guidance, some "significant response" examples include: setting focus, modifying public state, setting properties that affect the visual representation, and raising other new events. Examples of nonsignificant responses include: modifying private state (with no visual impact, or programmatic representation), logging of events, or looking at arguments of an event and choosing not to respond to it.

The routed event system behavior reinforces this "significant response" model for using handled state of a routed event, because handlers added in XAML or the common signature of AddHandler are not invoked in response to a routed event where the event data is already marked handled. You must go through the extra effort of adding a handler with the handledEventsToo parameter version (AddHandler(RoutedEvent, Delegate, Boolean)) in order to handle routed events that are marked handled by earlier participants in the event route.

In some circumstances, controls themselves mark certain routed events as handled. A handled routed event represents a decision by WPF control authors that the control's actions in response to the routed event are significant or complete as part of the control implementation, and the event needs no further handling. Usually this is done by adding a class handler for an event, or by overriding one of the class handler virtuals that exist on a base class. You can still work around this event handling if necessary; see Working Around Event Suppression by Controls later in this topic.

"Preview" (Tunneling) Events vs. Bubbling Events, and Event Handling

Preview routed events are events that follow a tunneling route through the element tree. The "Preview" expressed in the naming convention is indicative of the general principle for input events that preview (tunneling) routed events are raised prior to the equivalent bubbling routed event. Also, input routed events that have a tunneling and bubbling pair have a distinct handling logic. If the tunneling/preview routed event is marked as handled by an event listener, then the bubbling routed event will be marked handled even before any listeners of the bubbling routed event receive it. The tunneling and bubbling routed events are technically separate events, but they deliberately share the same instance of event data to enable this behavior.

The connection between the tunneling and bubbling routed events is accomplished by the internal implementation of how any given WPF class raises its own declared routed events, and this is true of the paired input routed events. But unless this class-level implementation exists, there is no connection between a tunneling routed event and a bubbling routed event that share the naming scheme: without such implementation they would be two entirely separate routed events and would not be raised in sequence or share event data.

For more information about how to implement tunnel/bubble input routed event pairs in a custom class, see How to: Create a Custom Routed Event.

Class Handlers and Instance Handlers

Routed events consider two different types of listeners to the event: class listeners and instance listeners. Class listeners exist because types have called a particular EventManager API ,RegisterClassHandler, in their static constructor, or have overridden a class handler virtual method from an element base class. Instance listeners are particular class instances/elements where one or more handlers have been attached for that routed event by a call to AddHandler. Existing WPF routed events make calls to AddHandler as part of the common language runtime (CLR) event wrapper add{} and remove{} implementations of the event, which is also how the simple XAML mechanism of attaching event handlers via an attribute syntax is enabled. Therefore even the simple XAML usage ultimately equates to an AddHandler call.

Elements within the visual tree are checked for registered handler implementations. Handlers are potentially invoked throughout the route, in the order that is inherent in the type of the routing strategy for that routed event. For instance, bubbling routed events will first invoke those handlers that are attached to the same element that raised the routed event. Then the routed event "bubbles" to the next parent element and so on until the application root element is reached.

From the perspective of the root element in a bubbling route, if class handling or any element closer to the source of the routed event invoke handlers that mark the event arguments as being handled, then handlers on the root elements are not invoked, and the event route is effectively shortened before reaching that root element. However, the route is not completely halted, because handlers can be added using a special conditional that they should still be invoked, even if a class handler or instance handler has marked the routed event as handled. This is explained in Adding Instance Handlers That Are Raised Even When Events Are Marked Handled, later in this topic.

At a deeper level than the event route, there are also potentially multiple class handlers acting on any given instance of a class. This is because the class handling model for routed events enables all possible classes in a class hierarchy to each register its own class handler for each routed event. Each class handler is added to an internal store, and when the event route for an application is constructed, the class handlers are all added to the event route. Class handlers are added to the route such that the most-derived class handler is invoked first, and class handlers from each successive base class are invoked next. Generally, class handlers are not registered such that they also respond to routed events that were already marked handled. Therefore, this class handling mechanism enables one of two choices:

  • Derived classes can supplement the class handling that is inherited from the base class by adding a handler that does not mark the routed event handled, because the base class handler will be invoked sometime after the derived class handler.

  • Derived classes can replace the class handling from the base class by adding a class handler that marks the routed event handled. You should be cautious with this approach, because it will potentially change the intended base control design in areas such as visual appearance, state logic, input handling, and command handling.

Class Handling of Routed Events by Control Base Classes

On each given element node in an event route, class listeners have the opportunity to respond to the routed event before any instance listener on the element can. For this reason, class handlers are sometimes used to suppress routed events that a particular control class implementation does not wish to propagate further, or to provide special handling of that routed event that is a feature of the class. For instance, a class might raise its own class-specific event that contains more specifics about what some user input condition means in the context of that particular class. The class implementation might then mark the more general routed event as handled. Class handlers are typically added such that they are not invoked for routed events where shared event data was already marked handled, but for atypical cases there is also a RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) signature that registers class handlers to invoke even when routed events are marked handled.

Class Handler Virtuals

Some elements, particularly the base elements such as UIElement, expose empty "On*Event" and "OnPreview*Event" virtual methods that correspond to their list of public routed events. These virtual methods can be overridden to implement a class handler for that routed event. The base element classes register these virtual methods as their class handler for each such routed event using RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) as described earlier. The On*Event virtual methods make it much simpler to implement class handling for the relevant routed events, without requiring special initialization in static constructors for each type. For instance, you can add class handling for the DragEnter event in any UIElement derived class by overriding the OnDragEnter virtual method. Within the override, you could handle the routed event, raise other events, initiate class-specific logic that might change element properties on instances, or any combination of those actions. You should generally call the base implementation in such overrides even if you mark the event handled. Calling the base implementation is strongly recommended because the virtual method is on the base class. The standard protected virtual pattern of calling the base implementations from each virtual essentially replaces and parallels a similar mechanism that is native to routed event class handling, whereby class handlers for all classes in a class hierarchy are called on any given instance, starting with the most-derived class' handler and continuing to the base class handler. You should only omit the base implementation call if your class has a deliberate requirement to change the base class handling logic. Whether you call the base implementation before or after your overriding code will depend on the nature of your implementation.

Input Event Class Handling

The class handler virtual methods are all registered such that they are only invoked in cases where any shared event data are not already marked handled. Also, for the input events uniquely, the tunneling and bubbling versions typically are raised in sequence and share event data. This entails that for a given pair of class handlers of input events where one is the tunneling version and the other is the bubbling version, you may not want to mark the event handled immediately. If you implement the tunneling class handling virtual method to mark the event handled, that will prevent the bubbling class handler from being invoked (as well as preventing any normally registered instance handlers for either the tunneling or bubbling event from being invoked).

Once class handling on a node is complete, the instance listeners are considered.

Adding Instance Handlers That Are Raised Even When Events Are Marked Handled

The AddHandler method supplies a particular overload that allows you to add handlers that will be invoked by the event system whenever an event reaches the handling element in the route, even if some other handler has already adjusted the event data to mark that event as handled. This is not typically done. Generally, handlers can be written to adjust all areas of application code that might be influenced by an event, regardless of where it was handled in an element tree, even if multiple end results are desired. Also, typically there is really only one element that needs to respond to that event, and the appropriate application logic had already happened. But the handledEventsToo overload is available for the exceptional cases where some other element in an element tree or control compositing has already marked an event as handled, but other elements either higher or lower in the element tree (depending on route) still wish to have their own handlers invoked.

When to Mark Handled Events as Unhandled

Generally, routed events that are marked handled should not be marked unhandled (Handled set back to false) even by handlers that act on handledEventsToo. However, some input events have high-level and lower-level event representations that can overlap when the high-level event is seen at one position in the tree and the low-level event at another position. For instance, consider the case where a child element listens to a high-level key event such as TextInput while a parent element listens to a low-level event such as KeyDown. If the parent element handles the low-level event, the higher-level event can be suppressed even in the child element that intuitively should have first opportunity to handle the event.

In these situations it may be necessary to add handlers to both parent elements and child elements for the low-level event. The child element handler implementation can mark the low-level event as handled, but the parent element handler implementation would set it unhandled again so that further elements up the tree (as well as the high-level event) can have the opportunity to respond. This situation is should be fairly rare.

Deliberately Suppressing Input Events for Control Compositing

The main scenario where class handling of routed events is used is for input events and composited controls. A composited control is by definition composed of multiple practical controls or control base classes. Often the author of the control wishes to amalgamate all of the possible input events that each of the subcomponents might raise, in order to report the entire control as the singular event source. In some cases the control author might wish to suppress the events from components entirely, or substitute a component-defined event that carries more information or implies a more specific behavior. The canonical example that is immediately visible to any component author is how a Windows Presentation Foundation (WPF) Button handles any mouse event that will eventually resolve to the intuitive event that all buttons have: a Click event.

The Button base class (ButtonBase) derives from Control which in turn derives from FrameworkElement and UIElement, and much of the event infrastructure needed for control input processing is available at the UIElement level. In particular, UIElement processes general Mouse events that handle hit testing for the mouse cursor within its bounds, and provides distinct events for the most common button actions, such as MouseLeftButtonDownUIElement also provides an empty virtual OnMouseLeftButtonDown as the preregistered class handler for MouseLeftButtonDown, and ButtonBase overrides it. Similarly, ButtonBase uses class handlers for MouseLeftButtonUp. In the overrides, which are passed the event data, the implementations mark that RoutedEventArgs instance as handled by setting Handled to true, and that same event data is what continues along the remainder of the route to other class handlers and also to instance handlers or event setters. Also, the OnMouseLeftButtonUp override will next raise the Click event. The end result for most listeners will be that the MouseLeftButtonDown and MouseLeftButtonUp events "disappear" and are replaced instead by Click, an event that holds more meaning because it is known that this event originated from a true button and not some composite piece of the button or from some other element entirely.

Working Around Event Suppression by Controls

Sometimes this event suppression behavior within individual controls can interfere with some more general intentions of event handling logic for your application. For instance, if for some reason your application had a handler for MouseLeftButtonDown located at the application root element, you would notice that any mouse click on a button would not invoke MouseLeftButtonDown or MouseLeftButtonUp handlers at the root level. The event itself actually did bubble up (again, event routes are not truly ended, but the routed event system changes their handler invocation behavior after being marked handled). When the routed event reached the button, the ButtonBase class handling marked the MouseLeftButtonDown handled because it wished to substitute the Click event with more meaning. Therefore, any standard MouseLeftButtonDown handler further up the route would not be invoked. There are two techniques you can use to ensure that your handlers would be invoked in this circumstance.

The first technique is to deliberately add the handler using the handledEventsToo signature of AddHandler(RoutedEvent, Delegate, Boolean). A limitation of this approach is that this technique for attaching an event handler is only possible from code, not from markup. The simple syntax of specifying the event handler name as an event attribute value via Extensible Application Markup Language (XAML) does not enable that behavior.

The second technique works only for input events, where the tunneling and bubbling versions of the routed event are paired. For these routed events, you can add handlers to the preview/tunneling equivalent routed event instead. That routed event will tunnel through the route starting from the root, so the button class handling code would not intercept it, presuming that you attached the Preview handler at some ancestor element level in the application's element tree. If you use this approach, be cautious about marking any Preview event handled. For the example given with PreviewMouseLeftButtonDown being handled at the root element, if you marked the event as Handled in the handler implementation, you would actually suppress the Click event. That is typically not desirable behavior.




Preview Events


Preview events, also known as tunneling events, are routed events where the direction of the route travels from the application root towards the element that raised the event and is reported as the source in event data. Not all event scenarios support or require preview events; this topic describes the situations where preview events exist, how applications or components should handle them, and cases where creating preview events in custom components or classes might be appropriate.

Preview Events and Input

When you handle Preview events in general, be cautious about marking the events handled in the event data. Handling a Preview event on any element other than the element that raised it (the element that is reported as the source in the event data) has the effect of not providing an element the opportunity to handle the event that it originated. Sometimes this is the desired result, particularly if the elements in question exist in relationships within the compositing of a control.

For input events specifically, Preview events also share event data instances with the equivalent bubbling event. If you use a Preview event class handler to mark the input event handled, the bubbling input event class handler will not be invoked. Or, if you use a Preview event instance handler to mark the event handled, handlers for the bubbling event will not typically be invoked. Class handlers or instance handlers can be registered or attached with an option to be invoked even if the event is marked handled, but that technique is not commonly used.

For more information about class handling and how it relates to Preview events see Marking Routed Events as Handled, and Class Handling.

Working Around Event Suppression by Controls

One scenario where Preview events are commonly used is for composited control handling of input events. Sometimes, the author of the control suppresses a certain event from originating from their control, perhaps in order to substitute a component-defined event that carries more information or implies a more specific behavior. For instance, a Windows Presentation Foundation (WPF) Buttonsuppresses MouseLeftButtonDown and MouseLeftButtonDown bubbling events raised by the Button or its composite elements in favor of capturing the mouse and raising a Click event that is always raised by the Button itself. The event and its data still continue along the route, but because the Button marks the event data as Handled, only handlers for the event that specifically indicated they should act in the handledEventsToo case are invoked. If other elements towards the root of your application still wanted an opportunity to handle a control-suppressed event, one alternative is to attach handlers in code with handledEventsToo specified as true. But often a simpler technique is to change the routing direction you handle to be the Preview equivalent of an input event. For instance, if a control suppresses MouseLeftButtonDown, try attaching a handler for PreviewMouseLeftButtonDown instead. This technique only works for base element input events such as MouseLeftButtonDown. These input events use tunnel/bubble pairs, raise both events, and share the event data.

Each of these techniques has either side effects or limitations. The side effect of handling the Preview event is that handling the event at that point might disable handlers that expect to handle the bubbling event, and therefore the limitation is that it is usually not a good idea to mark the event handled while it is still on the Preview part of the route. The limitation of the handledEventsToo technique is that you cannot specify a handledEventsToo handler in XAML as an attribute, you must register the event handler in code after obtaining an object reference to the element where the handler is to be attached.




Property Change Events


Windows Presentation Foundation (WPF) defines several events that are raised in response to a change in the value of a property. Often the property is a dependency property. The event itself is sometimes a routed event and is sometimes a standard common language runtime (CLR) event. The definition of the event varies depending on the scenario, because some property changes are more appropriately routed through an element tree, whereas other property changes are generally only of concern to the object where the property changed.

Identifying a Property Change Event

Not all events that report a property change are explicitly identified as a property changed event, either by virtue of a signature pattern or a naming pattern. Generally, the description of the event in the software development kit (SDK) documentation indicates whether the event is directly tied to a property value change and provides cross-references between the property and event.

RoutedPropertyChanged Events

Certain events use an event data type and delegate that are explicitly used for property change events. The event data type is RoutedPropertyChangedEventArgs<T>, and the delegate is RoutedPropertyChangedEventHandler<T>. The event data and delegate both have a generic type parameter that is used to specify the actual type of the changing property when you define the handler. The event data contains two properties, OldValue and NewValue, which are both then passed as the type argument in the event data.

The "Routed" part of the name indicates that the property changed event is registered as a routed event. The advantage of routing a property changed event is that the top level of a control can receive property changed events if properties on the child elements (the control's composite parts) change values. For instance, you might create a control that incorporates a RangeBase control such as a Slider. If the value of the Valueproperty changes on the slider part, you might want to handle that change on the parent control rather than on the part.

Because you have an old value and a new value, it might be tempting to use this event handler as a validator for the property value. However, that is not the design intention of most property changed events. Generally, the values are provided so that you can act on those values in other logic areas of your code, but actually changing the values from within the event handler is not advisable, and may cause unintentional recursion depending on how your handler is implemented.

If your property is a custom dependency property, or if you are working with a derived class where you have defined the instantiation code, there is a much better mechanism for tracking property changes that is built in to the WPF property system: the property system callbacks CoerceValueCallback and PropertyChangedCallback. For more details about how you can use the WPF property system for validation and coercion, see Dependency Property Callbacks and Validation and Custom Dependency Properties.

DependencyPropertyChanged Events

Another pair of types that are part of a property changed event scenario is DependencyPropertyChangedEventArgs and DependencyPropertyChangedEventHandler. Events for these property changes are not routed; they are standard CLR events. DependencyPropertyChangedEventArgs is an unusual event data reporting type because it does not derive from EventArgsDependencyPropertyChangedEventArgs is a structure, not a class. 

Events that use DependencyPropertyChangedEventArgs and DependencyPropertyChangedEventHandler are slightly more common than RoutedPropertyChanged events. An example of an event that uses these types is IsMouseCapturedChanged.

Like RoutedPropertyChangedEventArgs<T>DependencyPropertyChangedEventArgs also reports an old and new value for the property. Also, the same considerations about what you can do with the values apply; it is generally not recommended that you attempt to change the values again on the sender in response to the event.

Property Triggers

A closely related concept to a property changed event is a property trigger. A property trigger is created within a style or template and enables you to create a conditional behavior based on the value of the property where the property trigger is assigned.

The property for a property trigger must be a dependency property. It can be (and frequently is) a read-only dependency property. A good indicator for when a dependency property exposed by a control is at least partially designed to be a property trigger is if the property name begins with "Is". Properties that have this naming are often a read-only Boolean dependency property where the primary scenario for the property is reporting control state that might have consequences to the real-time UI and is thus a property trigger candidate.

Some of these properties also have a dedicated property changed event. For instance, the property IsMouseCaptured has a property changed event IsMouseCapturedChanged. The property itself is read-only, with its value adjusted by the input system, and the input system raises IsMouseCapturedChanged on each real-time change.

Compared to a true property changed event, using a property trigger to act on a property change has some limitations.

Property triggers work through an exact match logic. You specify a property and a value that indicates the specific value for which the trigger will act. For instance: <Setter Property="IsMouseCaptured" Value="true"> ... </Setter>. Because of this limitation, the majority of property trigger usages will be for Boolean properties, or properties that take a dedicated enumeration value, where the possible value range is manageable enough to define a trigger for each case. Or property triggers might exist only for special values, such as when an items count reaches zero, and there would be no trigger that accounts for the cases when the property value changes away from zero again (instead of triggers for all cases, you might need a code event handler here, or a default behavior that toggles back from the trigger state again when the value is nonzero).

The property trigger syntax is analogous to an "if" statement in programming. If the trigger condition is true, then the "body" of the property trigger is "executed". The "body" of a property trigger is not code, it is markup. That markup is limited to using one or more Setter elements to set other properties of the object where the style or template is being applied.

To offset the "if" condition of a property trigger that has a wide variety of possible values, it is generally advisable to set that same property value to a default by using a Setter. This way, the Trigger contained setter will have precedence when the trigger condition is true, and the Setter that is not within a Trigger will have precedence whenever the trigger condition is false.

Property triggers are generally appropriate for scenarios where one or more appearance properties should change, based on the state of another property on the same element.

To learn more about property triggers, see Styling and Templating.




Visual Basic and WPF Event Handling


For the Microsoft Visual Basic .NET language specifically, you can use the language-specific Handles keyword to associate event handlers with instances, instead of attaching event handlers with attributes or using the AddHandler method. However, the Handles technique for attaching handlers to instances does have some limitations, because the Handles syntax cannot support some of the specific routed event features of the WPF event system.

Using "Handles" in a WPF Application

The event handlers that are connected to instances and events with Handles must all be defined within the partial class declaration of the instance, which is also a requirement for event handlers that are assigned through attribute values on elements. You can only specify Handles for an element on the page that has a Name property value (or x:Name Directive declared). This is because the Name in XAML creates the instance reference that is necessary to support the Instance.Event reference format required by the Handles syntax. The only element that can be used for Handles without a Name reference is the root-element instance that defines the partial class.

You can assign the same handler to multiple elements by separating Instance.Event references after Handles with commas.

You can use Handles to assign more than one handler to the same Instance.Event reference. Do not assign any importance to the order in which handlers are given in the Handles reference; you should assume that handlers that handle the same event can be invoked in any order.

To remove a handler that was added with Handles in the declaration, you can call RemoveHandler.

You can use Handles to attach handlers for routed events, so long as you attach handlers to instances that define the event being handled in their members tables. For routed events, handlers that are attached with Handles follow the same routing rules as do handlers that are attached as XAML attributes, or with the common signature of AddHandler. This means that if the event is already marked handled (the Handled property in the event data is True), then handlers attached with Handles are not invoked in response to that event instance. The event could be marked handled by instance handlers on another element in the route, or by class handling either on the current element or earlier elements along the route. For input events that support paired tunnel/bubble events, the tunneling route may have marked the event pair handled. For more information about routed events, see Routed Events Overview.

Limitations of "Handles" for Adding Handlers

Handles cannot reference handlers for attached events. You must use the add accessor method for that attached event, or typename.eventnameevent attributes in XAML. For details, see Routed Events Overview.

For routed events, you can only use Handles to assign handlers for instances where that event exists in the instance members table. However, with routed events in general, a parent element can be a listener for an event from child elements, even if the parent element does not have that event in its members table. In attribute syntax, you can specify this through a typename.membername attribute form that qualifies which type actually defines the event you want to handle. For instance, a parent Page (with no Click event defined) can listen for button-click events by assigning an attribute handler in the form Button.Click. But Handles does not support the typename.membername form, because it must support a conflicting Instance.Event form. For details, see Routed Events Overview.

Handles cannot attach handlers that are invoked for events that are already marked handled. Instead, you must use code and call the handledEventsToo overload of AddHandler(RoutedEvent, Delegate, Boolean).

System_CAPS_noteNote

Do not use the Handles syntax in Visual Basic code when you specify an event handler for the same event in XAML. In this case, the event handler is called twice.

How WPF Implements "Handles" Functionality

When a Extensible Application Markup Language (XAML) page is compiled, the intermediate file declares Friend WithEvents references to every element on the page that has a Name property set (or x:Name Directive declared). Each named instance is potentially an element that can be assigned to a handler through Handles.

System_CAPS_noteNote

Within Microsoft Visual Studio, IntelliSense can show you completion for which elements are available for a Handles reference in a page. However, this might take one compile pass so that the intermediate file can populate all the Friends references.




Weak Event Patterns


In applications, it is possible that handlers that are attached to event sources will not be destroyed in coordination with the listener object that attached the handler to the source. This situation can lead to memory leaks. Windows Presentation Foundation (WPF) introduces a design pattern that can be used to address this issue, by providing a dedicated manager class for particular events and implementing an interface on listeners for that event. This design pattern is known as the weak event pattern.

Why Implement the Weak Event Pattern?

Listening for events can lead to memory leaks. The typical technique for listening to an event is to use the language-specific syntax that attaches a handler to an event on a source. For example, in C#, that syntax is: source.SomeEvent += new SomeEventHandler(MyEventHandler).

This technique creates a strong reference from the event source to the event listener. Ordinarily, attaching an event handler for a listener causes the listener to have an object lifetime that is influenced by the object lifetime of the source (unless the event handler is explicitly removed). But in certain circumstances, you might want the object lifetime of the listener to be controlled by other factors, such as whether it currently belongs to the visual tree of the application, and not by the lifetime of the source. Whenever the source object lifetime extends beyond the object lifetime of the listener, the normal event pattern leads to a memory leak: the listener is kept alive longer than intended.

The weak event pattern is designed to solve this memory leak problem. The weak event pattern can be used whenever a listener needs to register for an event, but the listener does not explicitly know when to unregister. The weak event pattern can also be used whenever the object lifetime of the source exceeds the useful object lifetime of the listener. (In this case, useful is determined by you.) The weak event pattern allows the listener to register for and receive the event without affecting the object lifetime characteristics of the listener in any way. In effect, the implied reference from the source does not determine whether the listener is eligible for garbage collection. The reference is a weak reference, thus the naming of the weak event pattern and the related APIs. The listener can be garbage collected or otherwise destroyed, and the source can continue without retaining noncollectible handler references to a now destroyed object.

Who Should Implement the Weak Event Pattern?

Implementing the weak event pattern is interesting primarily for control authors. As a control author, you are largely responsible for the behavior and containment of your control and the impact it has on applications in which it is inserted. This includes the control object lifetime behavior, in particular the handling of the described memory leak problem.

Certain scenarios inherently lend themselves to the application of the weak event pattern. One such scenario is data binding. In data binding, it is common for the source object to be completely independent of the listener object, which is a target of a binding. Many aspects of WPF data binding already have the weak event pattern applied in how the events are implemented.

How to Implement the Weak Event Pattern

There are three ways to implement weak event pattern. The following table lists the three approaches and provides some guidance for when you should use each.

Approach

When to Implement

Use an existing weak event manager class

If the event you want to subscribe to has a corresponding WeakEventManager, use the existing weak event manager. For a list of weak event managers that are included with WPF, see the inheritance hierarchy in the WeakEventManager class. Note, however, that there are relatively few weak event managers that are included with WPF, so you will probably need to choose one of the other approaches.

Use a generic weak event manager class

Use a generic WeakEventManager<TEventSource, TEventArgs> when an existing WeakEventManager is not available, you want an easy way to implement, and you are not concerned about efficiency. The generic WeakEventManager<TEventSource, TEventArgs> is less efficient than an existing or custom weak event manager. For example, the generic class does more reflection to discover the event given the event's name. Also, the code to register the event by using the generic WeakEventManager<TEventSource, TEventArgs> is more verbose than using an existing or custom WeakEventManager.

Create a custom weak event manager class

Create a custom WeakEventManager when you an existing WeakEventManager is not available and you want the best efficiency. Using a custom WeakEventManager to subscribe to an event will be more efficient, but you do incur the cost of writing more code at the beginning.

The following sections describe how to implement the weak event pattern. For purposes of this discussion, the event to subscribe to has the following characteristics.

  • The event name is SomeEvent.

  • The event is raised by the EventSource class.

  • The event handler has type: SomeEventEventHandler (or EventHandler<SomeEventEventArgs>).

  • The event passes a parameter of type SomeEventEventArgs to the event handlers.

Using an Existing Weak Event Manager Class

  1. Find an existing weak event manager.

    For a list of weak event managers that are included with WPF, see the inheritance hierarchy in the WeakEventManager class.

  2. Use the new weak event manager instead of the normal event hookup.

    For example, if your code uses the following pattern to subscribe to an event:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
    

    Change it to the following pattern:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
    

    Similarly, if your code uses the following pattern to unsubscribe to an event:

    source.SomeEvent -= new SomeEventEventHandler(OnSome);
    

    Change it to the following pattern:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);
    

Using the Generic Weak Event Manager Class

  1. Use the generic WeakEventManager<TEventSource, TEventArgs> class instead of the normal event hookup.

    When you use WeakEventManager<TEventSource, TEventArgs> to register event listeners, you supply the event source and EventArgs type as the type parameters to the class and call AddHandler as shown in the following code:

    WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, "SomeEvent", source_SomeEvent);
    

Creating a Custom Weak Event Manager Class

  1. Copy the following class template to your project.

    This class inherits from the WeakEventManager class.

    class SomeEventWeakEventManager : WeakEventManager
    {
    
        private SomeEventWeakEventManager()
        {
    
        }
    
        /// <summary>
        /// Add a handler for the given source's event.
        /// </summary>
        public static void AddHandler(EventSource source, 
                                      EventHandler<SomeEventEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (handler == null)
                throw new ArgumentNullException("handler");
    
            CurrentManager.ProtectedAddHandler(source, handler);
        }
    
        /// <summary>
        /// Remove a handler for the given source's event.
        /// </summary>
        public static void RemoveHandler(EventSource source, 
                                         EventHandler<SomeEventEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (handler == null)
                throw new ArgumentNullException("handler");
    
            CurrentManager.ProtectedRemoveHandler(source, handler);
        }
    
        /// <summary>
        /// Get the event manager for the current thread.
        /// </summary>
        private static SomeEventWeakEventManager CurrentManager
        {
            get
            {
                Type managerType = typeof(SomeEventWeakEventManager);
                SomeEventWeakEventManager manager = 
                    (SomeEventWeakEventManager)GetCurrentManager(managerType);
    
                // at first use, create and register a new manager
                if (manager == null)
                {
                    manager = new SomeEventWeakEventManager();
                    SetCurrentManager(managerType, manager);
                }
    
                return manager;
            }
        }
    
    
    
        /// <summary>
        /// Return a new list to hold listeners to the event.
        /// </summary>
        protected override ListenerList NewListenerList()
        {
            return new ListenerList<SomeEventEventArgs>();
        }
    
    
        /// <summary>
        /// Listen to the given source for the event.
        /// </summary>
        protected override void StartListening(object source)
        {
            EventSource typedSource = (EventSource)source;
            typedSource.SomeEvent += new EventHandler<SomeEventEventArgs>(OnSomeEvent);
        }
    
        /// <summary>
        /// Stop listening to the given source for the event.
        /// </summary>
        protected override void StopListening(object source)
        {
            EventSource typedSource = (EventSource)source;
            typedSource.SomeEvent -= new EventHandler<SomeEventEventArgs>(OnSomeEvent);
        }
    
        /// <summary>
        /// Event handler for the SomeEvent event.
        /// </summary>
        void OnSomeEvent(object sender, SomeEventEventArgs e)
        {
            DeliverEvent(sender, e);
        }
    }
    
  2. Replace the SomeEventWeakEventManager name with your own name.

  3. Replace the three names described previously with the corresponding names for your event. (SomeEventEventSource, and SomeEventEventArgs)

  4. Set the visibility (public / internal / private) of the weak event manager class to the same visibility as event it manages.

  5. Use the new weak event manager instead of the normal event hookup.

    For example, if your code uses the following pattern to subscribe to an event:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
    

    Change it to the following pattern:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
    

    Similarly, if your code uses the following pattern to unsubscribe to an event:

    source.SomeEvent -= new SomeEventEventHandler(OnSome);
    

    Change it to the following pattern:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);




How-to Topics


How to: Add an Event Handler Using Code


This example shows how to add an event handler to an element by using code.

If you want to add an event handler to a XAML element, and the markup page that contains the element has already been loaded, you must add the handler using code. Alternatively, if you are building up the element tree for an application entirely using code and not declaring any elements using XAML, you can call specific methods to add event handlers to the constructed element tree.

Example

The following example adds a new Button to an existing page that is initially defined in XAML. A code-behind file implements an event handler method and then adds that method as a new event handler on the Button.

The C# example uses the += operator to assign a handler to an event. This is the same operator that is used to assign a handler in the common language runtime (CLR) event handling model. Microsoft Visual Basic does not support this operator as a means of adding event handlers. It instead requires one of two techniques:

  • Use the AddHandler method, together with an AddressOf operator, to reference the event handler implementation.

  • Use the Handles keyword as part of the event handler definition. This technique is not shown here; see Visual Basic and WPF Event Handling.

<TextBlock Name="text1">Start by clicking the button below</TextBlock>
<Button Name="b1" Click="MakeButton">Make new button and add handler to it</Button>
public partial class RoutedEventAddRemoveHandler {
    void MakeButton(object sender, RoutedEventArgs e)
    {
        Button b2 = new Button();
        b2.Content = "New Button";
        // Associate event handler to the button. You can remove the event 
        // handler using "-=" syntax rather than "+=".
        b2.Click  += new RoutedEventHandler(Onb2Click);
        root.Children.Insert(root.Children.Count, b2);
        DockPanel.SetDock(b2, Dock.Top);
        text1.Text = "Now click the second button...";
        b1.IsEnabled = false;
    }
    void Onb2Click(object sender, RoutedEventArgs e)
    {
        text1.Text = "New Button (b2) Was Clicked!!";
    }
System_CAPS_noteNote

Adding an event handler in the initially parsed XAML page is much simpler. Within the object element where you want to add the event handler, add an attribute that matches the name of the event that you want to handle. Then specify the value of that attribute as the name of the event handler method that you defined in the code-behind file of the XAML page. For more information, see XAML Overview (WPF) or Routed Events Overview.




How to: Handle a Routed Event


This example shows how bubbling events work and how to write a handler that can process the routed event data.

Example

In Windows Presentation Foundation (WPF), elements are arranged in an element tree structure. The parent element can participate in the handling of events that are initially raised by child elements in the element tree. This is possible because of event routing.

Routed events typically follow one of two routing strategies, bubbling or tunneling. This example focuses on the bubbling event and uses the ButtonBase.Click event to show how routing works.

The following example creates two Button controls and uses XAML attribute syntax to attach an event handler to a common parent element, which in this example is StackPanel. Instead of attaching individual event handlers for each Button child element, the example uses attribute syntax to attach the event handler to the StackPanel parent element. This event-handling pattern shows how to use event routing as a technique for reducing the number of elements where a handler is attached. All the bubbling events for each Button route through the parent element.

Note that on the parent StackPanel element, the Click event name specified as the attribute is partially qualified by naming the Button class. The Buttonclass is a ButtonBase derived class that has the Click event in its members listing. This partial qualification technique for attaching an event handler is necessary if the event that is being handled does not exist in the members listing of the element where the routed event handler is attached.

<StackPanel
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.RoutedEventHandle"
  Name="dpanel"
  Button.Click="HandleClick"
>
  <StackPanel.Resources>
      <Style TargetType="{x:Type Button}">
        <Setter Property="Height" Value="20"/>
        <Setter Property="Width" Value="250"/>
        <Setter Property="HorizontalAlignment" Value="Left"/>
      </Style>
  </StackPanel.Resources>
  <Button Name="Button1">Item 1</Button>
  <Button Name="Button2">Item 2</Button>
  <TextBlock Name="results"/>
</StackPanel>

The following example handles the Click event. The example reports which element handles the event and which element raises the event. The event handler is executed when the user clicks either button.

public partial class RoutedEventHandle : StackPanel
{
    StringBuilder eventstr = new StringBuilder();
    void HandleClick(object sender, RoutedEventArgs args)
    {
        // Get the element that handled the event.
        FrameworkElement fe = (FrameworkElement)sender;
        eventstr.Append("Event handled by element named ");
        eventstr.Append(fe.Name);
        eventstr.Append("\n");

        // Get the element that raised the event. 
        FrameworkElement fe2 = (FrameworkElement)args.Source;
        eventstr.Append("Event originated from source element of type ");
        eventstr.Append(args.Source.GetType().ToString());
        eventstr.Append(" with Name ");
        eventstr.Append(fe2.Name);
        eventstr.Append("\n");

        // Get the routing strategy.
        eventstr.Append("Event used routing strategy ");
        eventstr.Append(args.RoutedEvent.RoutingStrategy);
        eventstr.Append("\n");

        results.Text = eventstr.ToString();
    }
}




How to: Create a Custom Routed Event


For your custom event to support event routing, you need to register a RoutedEvent using the RegisterRoutedEvent method. This example demonstrates the basics of creating a custom routed event.

Example

As shown in the following example, you first register a RoutedEvent using the RegisterRoutedEvent method. By convention, the RoutedEvent static field name should end with the suffix Event. In this example, the name of the event is Tap and the routing strategy of the event is Bubble. After the registration call, you can provide add-and-remove common language runtime (CLR) event accessors for the event.

Note that even though the event is raised through the OnTap virtual method in this particular example, how you raise your event or how your event responds to changes depends on your needs.

Note also that this example basically implements an entire subclass of Button; that subclass is built as a separate assembly and then instantiated as a custom class on a separate Extensible Application Markup Language (XAML) page. This is to illustrate the concept that subclassed controls can be inserted into trees composed of other controls, and that in this situation, custom events on these controls have the very same event routing capabilities as any native Windows Presentation Foundation (WPF) element does.

public class MyButtonSimple: Button
{
    // Create a custom routed event by first registering a RoutedEventID
    // This event uses the bubbling routing strategy
    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); }
    }

    // This method raises the Tap event
    void RaiseTapEvent()
    {
            RoutedEventArgs newEventArgs = new RoutedEventArgs(MyButtonSimple.TapEvent);
            RaiseEvent(newEventArgs);
    }
    // For demonstration purposes we raise the event when the MyButtonSimple is clicked
    protected override void OnClick()
    {
        RaiseTapEvent();
    }

}
<Window  
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:SDKSample;assembly=SDKSampleLibrary"
    x:Class="SDKSample.RoutedEventCustomApp"

    >
    <Window.Resources>
      <Style TargetType="{x:Type custom:MyButtonSimple}">
        <Setter Property="Height" Value="20"/>
        <Setter Property="Width" Value="250"/>
        <Setter Property="HorizontalAlignment" Value="Left"/>
        <Setter Property="Background" Value="#808080"/>
      </Style>
    </Window.Resources>
    <StackPanel Background="LightGray">
	    <custom:MyButtonSimple Name="mybtnsimple" Tap="TapHandler">Click to see Tap custom event work</custom:MyButtonSimple>
    </StackPanel>
</Window>

Tunneling events are created the same way, but with RoutingStrategy set to Tunnel in the registration call. By convention, tunneling events in WPF are prefixed with the word "Preview".

To see an example of how bubbling events work, see How to: Handle a Routed Event.




How to: Find the Source Element in an Event Handler


This example shows how to find the source element in an event handler.

Example

The following example shows a Click event handler that is declared in a code-behind file. When a user clicks the button that the handler is attached to, the handler changes a property value. The handler code uses the Source property of the routed event data that is reported in the event arguments to change the Width property value on the Source element.

<Button Click="HandleClick">Button 1</Button>
     void HandleClick(object sender, RoutedEventArgs e)
     {
         // You must cast the sender object as a Button element, or at least as FrameworkElement, to set Width
         Button srcButton = e.Source as Button;
srcButton.Width = 200;
     }




How to: Add Class Handling for a Routed Event


Routed events can be handled either by class handlers or instance handlers on any given node in the route. Class handlers are invoked first, and can be used by class implementations to suppress events from instance handling or introduce other event specific behaviors on events that are owned by base classes. This example illustrates two closely related techniques for implementing class handlers.

Example

This example uses a custom class based on the Canvas panel. The basic premise of the application is that the custom class introduces behaviors on its child elements, including intercepting any left mouse button clicks and marking them handled, before the child element class or any instance handlers on it will be invoked.

The UIElement class exposes a virtual method that enables class handling on the PreviewMouseLeftButtonDown event, by simply overriding the event. This is the simplest way to implement class handling if such a virtual method is available somewhere in your class' hierarchy. The following code shows the OnPreviewMouseLeftButtonDown implementation in the "MyEditContainer" that is derived from Canvas. The implementation marks the event as handled in the arguments, and then adds some code to give the source element a basic visible change.

protected override void OnPreviewMouseRightButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
    e.Handled = true; //suppress the click event and other leftmousebuttondown responders
    MyEditContainer ec = (MyEditContainer)e.Source;
    if (ec.EditState)
    { ec.EditState = false; }
    else
    { ec.EditState = true; }
    base.OnPreviewMouseRightButtonDown(e);
}

If no virtual is available on base classes or for that particular method, class handling can be added directly using a utility method of the EventManagerclass, RegisterClassHandler. This method should only be called within the static initialization of classes that are adding class handling. This example adds another handler for PreviewMouseLeftButtonDown , and in this case the registered class is the custom class. In contrast, when using the virtuals, the registered class is really the UIElement base class. In cases where base classes and subclass each register class handling, the subclass handlers are invoked first. The behavior in an application would be that first this handler would show its message box and then the visual change in the virtual method's handler would be shown.

static MyEditContainer()
{
  EventManager.RegisterClassHandler(typeof(MyEditContainer), PreviewMouseRightButtonDownEvent, new RoutedEventHandler(LocalOnMouseRightButtonDown));
}
internal static void LocalOnMouseRightButtonDown(object sender, RoutedEventArgs e)
{
  MessageBox.Show("this is invoked before the On* class handler on UIElement");
  //e.Handled = true; //uncommenting this would cause ONLY the subclass' class handler to respond
}








'프로그래밍 > WPF' 카테고리의 다른 글

Documents  (0) 2016.11.05
Resources  (0) 2016.11.01
Element Tree and Serialization  (0) 2016.11.01
Base Elements (기본 요소)  (0) 2016.10.24
종속성 속성(Dependency Properties)  (0) 2016.10.24
:
Posted by 지훈2