WPFAQS - Routed Events in WPF

WPFAQS – is a series on WPF frequently asked questions (FAQS). Today we will discuss about “Routed Events” in WPF with some examples.

WPFAQS(005) – What are “Routed Events”? Give examples
Prerequisite/s – WPF, XAML, Custom and User Controls.

In my last WPFAQS on “Logical and Visual Trees”, I discussed very briefly about the routed events that they get routed based primarily on the visual tree. In today's post I’ll discuss them in a bit detail. A routed event is part of the publish-subscribe mechanism defined in the WPF event mechanism and is no different than the ordinary events accept the routed events are CLR events and get routed based on the “routing strategy” defined while compositing the visual tree of the elements/controls and needs some sort of special kind of registration. There are three types of routing strategy, Bubble, Tunnel, or Direct.

RoutedEvents2

The event that is initiated from the source element and travels or “bubbles” upward through the elements visual tree until it reaches root (typically a page or a window) has a bubbling strategy is a bubble event. While the tunnel events travels in the reverse direction. They start at the root element and traverse down the visual tree until they are handled by the event generating source. The direct events are like normal events defined as delegates in the .NET Framework and you hook them up with some event-handler using += operator or so, and they are similar to WinForms Events. The obvious question is, what’s the purpose of these bubbling and tunneling strategies??? Here is why – Most of the controls like “Buttons” have a long composition of visual tree, and there is a possibility that when you click on them, you might clicked some child, like “ButtonChrome” in case of a “Button” that is well below the hierarchy. So what happens is the WPF event routing system bubbles/tunnels the event to/from the source, that it can be handled properly, and that's why these events are called routed events.

 Registration of Routed Events and their usage:

Lets take a look at the relevant classes of the WPF Routed Event Framework:

RoutedEvents

A routed event is basically an instance of the “RoutedEvent” class shown above and registered with WPF event system using “EventManager” class’s Static member “RegisterRoutedEvent”. Here is a typical example code of registering an event named “ExecuteMe”.

   1:  public partial class GlassButtonControl : Button
   2:      {
   3:          // Lazy, will be called once, when this class is accessed
   4:          public static readonly RoutedEvent ExecuteMeEvent;
   5:   
   6:          static GlassButtonControl()
   7:          {
   8:              ExecuteMeEvent = EventManager.RegisterRoutedEvent("ExecuteMe",
   9:                          RoutingStrategy.Bubble,
  10:                          typeof(RoutedEventHandler),
  11:                          typeof(GlassButtonControl));
  12:          }
  13:   
  14:          // Provide CLR accessors for the event
  15:          public event RoutedEventHandler ExecuteMe
  16:          {
  17:              add { AddHandler(ExecuteMeEvent, value); }
  18:              remove { RemoveHandler(ExecuteMeEvent, value); }
  19:          }
  20:   
  21:          public static void Test()
  22:          {
  23:              GlassButtonControl glassButton = new GlassButtonControl();
  24:              // glassButton.ExecuteMe += new RoutedEventHandler(glassButton_ExecuteMe);            
  25:              glassButton.AddHandler(GlassButtonControl.ExecuteMeEvent, 
  26:                  new RoutedEventHandler(glassButton_ExecuteMe));
  27:          }
  28:   
  29:          static void glassButton_ExecuteMe(object sender, RoutedEventArgs e)
  30:          {
  31:              throw new NotImplementedException();
  32:          }
  33:      }

The “RoutedEvent” instance is usually a public static readonly class level instance registers using EventManager.RegisterRoutedEvent and thus "owns" the routed event. In order to create handlers for them, typically we create backing and connection mechanism also called “wrapper” event, “ExecuteMe” getter and setter, the same way we create a dependency property  wrappers while registering the property with the WPF property system.  For example take a look at the above code under Test() method lines 21-27.

Lets take another example, this is a window that contains only one button, named “TestButton”

RoutedEvents4 and here is the “XAML” for it –

   1:  <Window x:Class="WpfTestApp.Window1"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      Title="Test Window" Height="75" Width="200">
   5:      <Grid>
   6:          <Button Name="TestButton" Click="TestButton_Click">Click Me</Button>
   7:      </Grid>
   8:  </Window>

and if you spy on the auto code generated for this XAML, it looks like this:

   1:          void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) {
   2:              switch (connectionId)
   3:              {
   4:              case 1:
   5:              this.TestButton = ((System.Windows.Controls.Button)(target));
   6:              
   7:              #line 6 "..\..\Window1.xaml"
   8:              this.TestButton.Click += 
   9:                  new System.Windows.RoutedEventHandler(this.TestButton_Click);
  10:              
  11:              #line default
  12:              #line hidden
  13:              return;
  14:              }
  15:              this._contentLoaded = true;
  16:          }

If you look closely at the line 8-9 above, you will see that the Click is “RoutedEventHandler” and the subscription is done the same way as we used to do in the regular event. One thing is important that behind the scene it actually calls the AddHandler, with a routing strategy of “RoutingStrategy.Bubble”, and that’s the default strategy. Lets take a closer look at the code behind of handling this routing event, “Click” –

   1:  namespace WpfTestApp
   2:  {
   3:      /// <summary>
   4:      /// Interaction logic for Window1.xaml
   5:      /// </summary>
   6:      public partial class Window1 : Window
   7:      {
   8:          public Window1()
   9:          {
  10:              InitializeComponent();
  11:          }
  12:   
  13:          private void TestButton_Click(object sender, 
  14:              RoutedEventArgs e)
  15:          {
  16:              e.Handled = true;
  17:          }
  18:      }
  19:  }

RoutedEvents5

You can also verify the “RoutingStrategy” for it by putting a breakpoint some where in the event handler, as shown above.

One other very powerful concept is introduced in WPF is like “Attached Properties”, you can also define “Attached Events”. So what does it mean? It means that there is a possibility that you might need to handle events somewhere else in the tree hierarchy besides the control itself and provides more flexibility in where you hook up your event handlers. As an example, lets change the XAML to something like this:

   1:  <Window x:Class="WpfTestApp.Window1"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      Title="Test Window" Height="75" Width="200">
   5:      <Grid x:Name="RootGrid" ButtonBase.Click="TestButton_Click">
   6:          <Button Name="TestButton">Click Me</Button>
   7:      </Grid>
   8:  </Window>

So what we did, we took the Click handler to the parent in the hierarchy – and if you spy on it, by looking at the auto-generated code, you’ll see some thing like this: -

   1:         void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) {
   2:              switch (connectionId)
   3:              {
   4:              case 1:
   5:              this.RootGrid = ((System.Windows.Controls.Grid)(target));
   6:              
   7:              #line 5 "..\..\Window1.xaml"
   8:              this.RootGrid.AddHandler(System.Windows.Controls.Primitives.ButtonBase.ClickEvent, 
   9:                  new System.Windows.RoutedEventHandler(this.TestButton_Click));
  10:              
  11:              #line default
  12:              #line hidden
  13:              return;
  14:              case 2:
  15:              this.TestButton = ((System.Windows.Controls.Button)(target));
  16:              return;
  17:              }
  18:              this._contentLoaded = true;
  19:          }

lines 8-9, call’s the “Grid’s” AddHandler to register the ClickEvent. and here is the output of when we put a breakpoint in the handler :

RoutedEvents6

You can see that the event is again of  RoutedEvent type of “ButtonBase.Click” and the strategy is of “Bubble” type.

Lets take another scenario, lets put the handler on the Grid as well as the source button itself, lets see the outcome of it. Also I will explain what happens when i put “e.Handled=true”. Here is the example XAML

RoutedEvents7

and here is auto generated code for it :

RoutedEvents8

The click event is subscribed on two locations as shown above, obviously two events are generated, when you click the button. But i suppressed one by setting “e.Handled = true” in the source one. Note if you dont set this on the source, that is the button’s one then two events will be generated. Here is the code for it

   1:  namespace WpfTestApp
   2:  {
   3:      /// <summary>
   4:      /// Interaction logic for Window1.xaml
   5:      /// </summary>
   6:      public partial class Window1 : Window
   7:      {
   8:          public Window1()
   9:          {
  10:              InitializeComponent();
  11:          }
  12:   
  13:          private void TestButton_Click(object sender, 
  14:              RoutedEventArgs e)
  15:          {
  16:   
  17:          }
  18:   
  19:          private void TestButton_Click_1(object sender, RoutedEventArgs e)
  20:          {
  21:              e.Handled = true;
  22:          }
  23:      }
  24:  }

If you look at the line-21, it means to inform the WPF routing system, that there is now no need of generating the event any more, its already handled. For more details on “Routed Events” take a look at MSDN documentation, here is the link for it “Routed Events Overview”.

That's all for now folks and will discuss another FAQS on WPF Command Routing and Commands/Commands Binding infrastructure, see you then. Look forward to your feed-back. enjoy :)

 

If you enjoyed reading this blog, leave your valuable feedback and consider subscribing to the RSS feed. You can also subscribe to it by email. Also, you can follow me on Twitter. Thank you!

Comments (1) -

bit.ly
10/12/2017 5:47:35 PM #

This is a topic which is close to my heart... Take care! Exactly where are your contact details though?

Add comment