Thursday, June 5, 2008

[WPF] Preview vs. Non-Preview Events

So in my reading travels today I started looking up animations, which took me to triggers, which took me to event handling, and I then bumped into the concept of preview vs. non-preview events [the mind is a wonderful thing]. This is particularly interesting to me since I have been using event handling in my current project, but don't fully understand how the model works.

So first of all... some definitions yoinked from MS because they make more sense then I ever could:

Taken from MSDN
Routed Event: 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.

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.

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.



Using routed events properly means that you can setup a common event listener to catch multiple events triggered from multiple objects. This concept is probably not new, but I found it interesting.
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.
So for example you can setup something like this:

< StackPanel Background="LightGray"
Orientation="Horizontal"
Button.Click="CommonClickHandler" >
< Button Name="YesButton" Width="Auto" >Yes
< Button Name="NoButton" Width="Auto" >No
< Button Name="CancelButton" Width="Auto" >Cancel
< /StackPanel >


and then use one handler like this:

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

FOR CRYING OUT LOUD... WHAT DOES THIS HAVE TO DO WITH PREVIEW EVENTS???
Gosh, weren't you reading? Tunneling events are referred to as "Preview" events. This is due to the fact that in WPF the naming convention to call such events are prefixed with the word Preview. Preview events are fired in the tunneling phase, and give a 'preview' of the state of the objects - however note that you cannot actually 'handle' the event in this phase, and thus you cannot set the event as being handled.

All other non-preview events are just as we expect. They are triggered in the bubbling phase and allow you to properly set the event as being handled.

Silverlight Note:
I am uncertain if Silverlight supports preview events yet. From the documentation I have seen, these events are prefixed with "Preview" [like "PreviewKeyDown" vs. "KeyDown"] and I cannot seem to access them via the VS2008 intellisense.

Why Use Preview Events?:
Use preview events if you wish to log some logic before any event handling occurs.
This forum post talks a little about the purpose of Preview Events.

No comments: