How to create a custom routed event (WPF .NET)
Windows Presentation Foundation (WPF) application developers and component authors can create custom routed events to extend the functionality of common language runtime (CLR) events. For information on routed event capabilities, see Why use routed events. This article covers the basics of creating a custom routed event.
Prerequisites
The article assumes a basic knowledge of routed events, and that you've read Routed events overview. To follow the examples in this article, it helps if you're familiar with Extensible Application Markup Language (XAML) and know how to write Windows Presentation Foundation (WPF) applications.
Routed event steps
The basic steps to create a routed event are:
Register a RoutedEvent using the RegisterRoutedEvent method.
The registration call returns a
RoutedEvent
instance, known as a routed event identifier, which holds the registered event name, routing strategy, and other event details. Assign the identifier to a static readonly field. By convention:- The identifier for a routed event with a bubbling strategy is named
<event name>Event
. For example, if the event name isTap
then the identifier should be namedTapEvent
. - The identifier for a routed event with a tunneling strategy is named
Preview<event name>Event
. For example, if the event name isTap
then the identifier should be namedPreviewTapEvent
.
- The identifier for a routed event with a bubbling strategy is named
Define CLR add and remove event accessors. Without CLR event accessors, you'll only be able to add or remove event handlers through direct calls to the UIElement.AddHandler and UIElement.RemoveHandler methods. With CLR event accessors, you gain these event handler assignment mechanisms:
- For Extensible Application Markup Language (XAML), you can use attribute syntax to add event handlers.
- For C#, you can use the
+=
and-=
operators to add or remove event handlers. - For VB, you can use the AddHandler and RemoveHandler statements to add or remove event handlers.
Add custom logic for triggering your routed event. For example, your logic might trigger the event based on user-input and application state.
Example
The following example implements the CustomButton
class in a custom control library. The CustomButton
class, which derives from Button:
- Registers a RoutedEvent named
ConditionalClick
using the RegisterRoutedEvent method, and specifies the bubbling strategy during registration. - Assigns the
RoutedEvent
instance returned from the registration call to a static readonly field namedConditionalClickEvent
. - Defines CLR add and remove event accessors.
- Adds custom logic to raise the custom routed event when the
CustomButton
is clicked and an external condition applies. Although the example code raises theConditionalClick
routed event from within the overriddenOnClick
virtual method, you can raise your event any way you choose.
public class CustomButton : Button
{
// Register a custom routed event using the Bubble routing strategy.
public static readonly RoutedEvent ConditionalClickEvent = EventManager.RegisterRoutedEvent(
name: "ConditionalClick",
routingStrategy: RoutingStrategy.Bubble,
handlerType: typeof(RoutedEventHandler),
ownerType: typeof(CustomButton));
// Provide CLR accessors for assigning an event handler.
public event RoutedEventHandler ConditionalClick
{
add { AddHandler(ConditionalClickEvent, value); }
remove { RemoveHandler(ConditionalClickEvent, value); }
}
void RaiseCustomRoutedEvent()
{
// Create a RoutedEventArgs instance.
RoutedEventArgs routedEventArgs = new(routedEvent: ConditionalClickEvent);
// Raise the event, which will bubble up through the element tree.
RaiseEvent(routedEventArgs);
}
// For demo purposes, we use the Click event as a trigger.
protected override void OnClick()
{
// Some condition combined with the Click event will trigger the ConditionalClick event.
if (DateTime.Now > new DateTime())
RaiseCustomRoutedEvent();
// Call the base class OnClick() method so Click event subscribers are notified.
base.OnClick();
}
}
Public Class CustomButton
Inherits Button
' Register a custom routed event with the Bubble routing strategy.
Public Shared ReadOnly ConditionalClickEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
name:="ConditionalClick",
routingStrategy:=RoutingStrategy.Bubble,
handlerType:=GetType(RoutedEventHandler),
ownerType:=GetType(CustomButton))
' Provide CLR accessors to support event handler assignment.
Public Custom Event ConditionalClick As RoutedEventHandler
AddHandler(value As RoutedEventHandler)
[AddHandler](ConditionalClickEvent, value)
End AddHandler
RemoveHandler(value As RoutedEventHandler)
[RemoveHandler](ConditionalClickEvent, value)
End RemoveHandler
RaiseEvent(sender As Object, e As RoutedEventArgs)
[RaiseEvent](e)
End RaiseEvent
End Event
Private Sub RaiseCustomRoutedEvent()
' Create a RoutedEventArgs instance.
Dim routedEventArgs As New RoutedEventArgs(routedEvent:=ConditionalClickEvent)
' Raise the event, which will bubble up through the element tree.
[RaiseEvent](routedEventArgs)
End Sub
' For demo purposes, we use the Click event as a trigger.
Protected Overrides Sub OnClick()
' Some condition combined with the Click event will trigger the ConditionalClick event.
If Date.Now > New DateTime() Then RaiseCustomRoutedEvent()
' Call the base class OnClick() method so Click event subscribers are notified.
MyBase.OnClick()
End Sub
End Class
The example includes a separate WPF application that uses XAML markup to add an instance of the CustomButton
to a StackPanel, and to assign the Handler_ConditionalClick
method as the ConditionalClick
event handler for the CustomButton
and StackPanel1
elements.
<Window x:Class="CodeSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:WpfControl;assembly=WpfControlLibrary"
Title="How to create a custom routed event" Height="100" Width="300">
<StackPanel Name="StackPanel1" custom:CustomButton.ConditionalClick="Handler_ConditionalClick">
<custom:CustomButton
Name="customButton"
ConditionalClick="Handler_ConditionalClick"
Content="Click to trigger a custom routed event"
Background="LightGray">
</custom:CustomButton>
</StackPanel>
</Window>
In code-behind, the WPF application defines the Handler_ConditionalClick
event handler method. Event handler methods can only be implemented in code-behind.
// The ConditionalClick event handler.
private void Handler_ConditionalClick(object sender, RoutedEventArgs e)
{
string senderName = ((FrameworkElement)sender).Name;
string sourceName = ((FrameworkElement)e.Source).Name;
Debug.WriteLine($"Routed event handler attached to {senderName}, " +
$"triggered by the ConditionalClick routed event raised on {sourceName}.");
}
// Debug output when CustomButton is clicked:
// Routed event handler attached to CustomButton,
// triggered by the ConditionalClick routed event raised on CustomButton.
// Routed event handler attached to StackPanel1,
// triggered by the ConditionalClick routed event raised on CustomButton.
' The ConditionalClick event handler.
Private Sub Handler_ConditionalClick(sender As Object, e As RoutedEventArgs)
Dim sourceName As String = CType(e.Source, FrameworkElement).Name
Dim senderName As String = CType(sender, FrameworkElement).Name
Debug.WriteLine($"Routed event handler attached to {senderName}, " +
$"triggered by the ConditionalClick routed event raised on {sourceName}.")
End Sub
' Debug output when CustomButton is clicked:
' Routed event handler attached to CustomButton,
' triggered by the ConditionalClick routed event raised on CustomButton.
' Routed event handler attached to StackPanel1,
' triggered by the ConditionalClick routed event raised on CustomButton.
When CustomButton
is clicked:
- The
ConditionalClick
routed event is raised onCustomButton
. - The
Handler_ConditionalClick
event handler attached toCustomButton
is triggered. - The
ConditionalClick
routed event traverses up the element tree toStackPanel1
. - The
Handler_ConditionalClick
event handler attached toStackPanel1
is triggered. - The
ConditionalClick
routed event continues up the element tree potentially triggering otherConditionalClick
event handlers attached to other traversed elements.
The Handler_ConditionalClick
event handler obtains the following information about the event that triggered it:
- The sender object, which is the element that the event handler is attached to. The
sender
will beCustomButton
the first time the handler runs, andStackPanel1
the second time. - The RoutedEventArgs.Source object, which is the element that originally raised the event. In this example, the
Source
is alwaysCustomButton
.
Note
A key difference between a routed event and a CLR event is that a routed event traverses the element tree, looking for handlers, whereas a CLR event doesn't traverse the element tree and handlers can only attach to the source object that raised the event. As a result, a routed event sender
can be any traversed element in the element tree.
You can create a tunneling event the same way as a bubbling event, except you'll set the routing strategy in the event registration call to Tunnel. For more information on tunneling events, see WPF input events.
See also
.NET Desktop feedback