If you are deriving from common WPF base classes, you can implement your own attached events by including certain pattern methods in your class and by using utility methods that are already present on the base classes.
The pattern is as follows:
A method Add*Handler with two parameters. The first parameter must identify the event, and the identified event must match names with the * in the method name. The second parameter is the handler to add. The method must be public and static, with no return value.
A method Remove*Handler with two parameters. The first parameter must identify the event, and the identified event must match names with the * in the method name. The second parameter is the handler to remove. The method must be public and static, with no return value.
The Add*Handler accessor method facilitates the XAML processing when attached event handler attributes are declared on an element. The Add*Handler and Remove*Handler methods also enable code access to the event handler store for the attached event.
This general pattern is not yet precise enough for practical implementation in a framework, because any given XAML reader implementation might have different schemes for identifying underlying events in the supporting language and architecture. This is one of the reasons that WPF implements attached events as routed events; the identifier to use for an event (RoutedEvent) is already defined by the WPF event system. Also, routing an event is a natural implementation extension on the XAML language-level concept of an attached event.
The Add*Handler implementation for a WPF attached event consists of calling the AddHandler with the routed event and handler as arguments.
This implementation strategy and the routed event system in general restrict handling for attached events to either UIElement derived classes or ContentElement derived classes, because only those classes have AddHandler implementations.
For example, the following code defines the NeedsCleaning attached event on the owner class Aquarium, using the WPF attached event strategy of declaring the attached event as a routed event.
public static readonly RoutedEvent NeedsCleaningEvent = EventManager.RegisterRoutedEvent("NeedsCleaning", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AquariumFilter));
public static void AddNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
{
UIElement uie = d as UIElement;
if (uie != null)
{
uie.AddHandler(AquariumFilter.NeedsCleaningEvent, handler);
}
}
public static void RemoveNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
{
UIElement uie = d as UIElement;
if (uie != null)
{
uie.RemoveHandler(AquariumFilter.NeedsCleaningEvent, handler);
}
}
Note that the method used to establish the attached event identifier field, RegisterRoutedEvent, is actually the same method that is used to register a non-attached routed event. Attached events and routed events all are registered to a centralized internal store. This event store implementation enables the "events as an interface" conceptual consideration that is discussed in Routed Events Overview.