Add a listener for handled routed events in WPF

Routed events are special events in WPF that “bubble” up or “tunnel” down the visual tree. A routed event can be observed from every element in the visual tree by calling UIelement.AddHandler(..). Usually, a control that exposes a routed event also declares a normal event with the same name. This lets us add and remove EventHandlers or delegates for the routed event just like for any normal event. The UIElement.AddHandler() takes three arguments:

NameTypeDescription
routedEventRoutedEventAn identifier for the routed event to be handled
handlerDelegateA reference to the handler implementation.
handledEventsToobooltrue to register the handler such that it is invoked even when the routed event is marked handled in its event data; false to register the handler with the default condition that it will not be invoked if the routed event is already marked handled.
The default is false.
Do not routinely ask to rehandle a routed event.

By adding an event handler using the CLR event, the AddHandler(...) method will be called with false for the handledEventsToo argument. That means event handlers will not be invoked for events that have already been handled.

Here is the ButtonBase.Click event for example: (from the .net reference source ButtonBase.cs)

[Category("Behavior")]
public event RoutedEventHandler Click 
{ 
    add { AddHandler(ClickEvent, value); }
    remove { RemoveHandler(ClickEvent, value); }
}

And here is the AddHandler() method that gets called above: (from UIElement.cs)

public void AddHandler(RoutedEvent routedEvent, Delegate handler)
{
    // HandledEventToo defaults to false
    // Call forwarded
    AddHandler(routedEvent, handler, false);
}

To register an event handler for all events, even the ones that have been handled, we just use the AddHandler(..) method instead of the CLR event and pass in true for handledEventsToo.

Example

Here is a button click event handler that gets called every time a button somewhere in the visual tree is clicked:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        AddHandler(ButtonBase.ClickEvent,
            new RoutedEventHandler(OnGlobalButtonClicked),
            handledEventsToo: true);
    }

    private void OnGlobalButtonClicked(object sender, RoutedEventArgs args)
    {
        var button = args.OriginalSource as ButtonBase;
        Debug.WriteLine($"Button '{button.Name}' was clicked.");
    }
}

Leave a Comment

Your email address will not be published. Required fields are marked *