Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy doesn't this class handler attached to a tunneling event fire before an instance handler?
    text
    copied!<p>According to <a href="http://msdn.microsoft.com/en-us/library/ms742806.aspx#class_handlers" rel="nofollow">this MSDN article</a> (among others),</p> <blockquote> <p>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.</p> </blockquote> <p>I'm quite new to <code>RoutedEvent</code>s so there is a chance that I have a mistake in my code, but it seems as though a class handler attached to a <code>RoutedEvent</code> that is declared as <code>RoutingStrategy.Tunnel</code> does <em>not</em> always fire before the instance handlers attached to the same event.</p> <p>In my example below, I have created a <code>TouchButton</code> control class with a tunneling <code>RoutedEvent</code> and a bubbling <code>RoutedEvent</code>. I have registered class handlers for each. I then created an instance of the class in a window and handle each event in the code behind. I attached the same handler for the tunneling event on both the class element and the <code>Grid</code> that contains it. All four handlers display their name in a <code>MessageBox</code> so you can clearly see the order of execution.</p> <ol> <li>Grid Instance PreviewTouch</li> <li>Class TouchButton_PreviewTouch</li> <li>TouchButton Instance PreviewTouch</li> <li>Class TouchButton_Touch</li> <li>TouchButton Instance Touch</li> </ol> <p>This means that if I call <code>e.Handled = true;</code> in the class <code>PreviewTouch</code> event handler, I can stop execution from reaching all of the other event handlers except for the one attached to the <code>Grid</code> element. Is this supposed to be like this, or have I made a mistake somewhere? Otherwise, how can I stop execution from reaching <em>every</em> instance event handler?</p> <p>Here is the class:</p> <pre><code>public class TouchButton : Button { static TouchButton() { EventManager.RegisterClassHandler(typeof(TouchButton), PreviewTouchEvent, new RoutedEventHandler(TouchButton_PreviewTouch), true); EventManager.RegisterClassHandler(typeof(TouchButton), TouchEvent, new RoutedEventHandler(TouchButton_Touch), true); } private static void TouchButton_PreviewTouch(object sender, RoutedEventArgs e) { MessageBox.Show("Class TouchButton_PreviewTouch"); } private static void TouchButton_Touch(object sender, RoutedEventArgs e) { MessageBox.Show("Class TouchButton_Touch"); } public static RoutedEvent TouchEvent = EventManager.RegisterRoutedEvent("Touch", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TouchButton)); public event RoutedEventHandler Touch { add { AddHandler(TouchEvent, value); } remove { RemoveHandler(TouchEvent, value); } } public static RoutedEvent PreviewTouchEvent = EventManager.RegisterRoutedEvent( "PreviewTouch", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(TouchButton)); public event RoutedEventHandler PreviewTouch { add { AddHandler(PreviewTouchEvent, value); } remove { RemoveHandler(PreviewTouchEvent, value); } } protected override void OnClick() { RaiseTouchEvent(); } private void RaiseTouchEvent() { RoutedEventArgs touchEventArgs = new RoutedEventArgs(PreviewTouchEvent); RaiseEvent(touchEventArgs); if (!touchEventArgs.Handled) RaiseEvent(new RoutedEventArgs(TouchEvent)); } } </code></pre> <p>Here are the instance handlers in the window code behind:</p> <pre><code>private void TouchButton_PreviewTouch(object sender, RoutedEventArgs e) { MessageBox.Show(string.Format("{0} Instance PreviewTouch", ((FrameworkElement)sender).Name)); } private void TouchButton_Touch(object sender, RoutedEventArgs e) { MessageBox.Show(string.Format("{0} Instance Touch", ((FrameworkElement)sender).Name)); } </code></pre> <p>Here is the control XAML:</p> <pre><code>&lt;Grid Name="Grid" Controls:TouchButton.PreviewTouch="TouchButton_PreviewTouch"&gt; &lt;Controls:TouchButton x:Name="TouchButton" Width="200" Height="45" FontSize="24" Content="Touch me" Touch="TouchButton_Touch" PreviewTouch="TouchButton_PreviewTouch" /&gt; &lt;/Grid&gt; </code></pre> <p>I do understand that the tunneling event is handled by the <code>Grid</code> element before 'tunneling' down to the <code>TouchButton</code> element, but I thought that the class handlers were always supposed to fire before the instance handlers. If not, how can I achieve this?</p> <p><strong>UPDATE >>></strong></p> <p>Thanks to @sanguine's answer, I managed to find a way to stop all instance handlers from handling the event. If instead of replacing the declared class handling type of <code>TouchButton</code> with <code>Grid</code> as sanguine suggested, I replace it with <code>FrameworkElement</code>, then it will catch all <code>FrameworkElement</code>-derived controls.</p> <pre><code>EventManager.RegisterClassHandler(typeof(FrameworkElement), PreviewTouchEvent, new RoutedEventHandler(TouchButton_PreviewTouch), true); </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload