Reputation: 14399
I want to be sure that I understood how the events are propagated. Is the below correct?
For example, let's examine how button's Click
event is invoked when mouse left button is clicked within the button.
The button registers the Click
event:
public class Button : ButtonBase
{
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Button));
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
...
RaiseEvent(new RoutedEventArgs(ClickEvent, this));
...
}
...
}
EventManager.RegisterRoutedEvent
creates routed event named Click
and adds it to button's event handlers collection called EventHandlersStore
. I believe the collection(let's call it _routedEvents
) is of type similar to Dictionary<RoutedEvent, RoutedEventHandler>
. So, RegisterRoutedEvent
does _routedEvents.Add(ClickEvent, null)
.AddHandler
adds handler to ClickEvent
entry in EventHandlersStore
. If no one has subscribed to Click
event the handler for ClickEvent
remains null
.Now, when RaiseEvent
is invoked, in OnMouseLeftButtonDown
, this is what happening and how the event is routed according to my understanding:
void RaiseEvent(RoutedEventArgs e)
{
DependencyObject current = this;
do
{
// check if the element has handler for routed event
var handler = current.GetHandlerFromEventHandlersStore(e.RoutedEvent);
if (handler != null)
{
handler(e);
}
// the event was NOT handled -> route the event to the parent
// OR
// the event was handled but wasn't marked as handled -> route the event further to parent
if (e.Handled == false)
{
// assuming that RoutingStrategy is Bubble
current = VisualTreeHelper.GetParent(current);
}
// continue until either it has been handled or it reaches the root element
} while (e.Handled == false && current != null);
}
I'll appreciate if someone could correct me if I'm wrong and also tell me how OnMouseLeftButtonDown is called (I couldn't find it in resharper)
Upvotes: 2
Views: 1378
Reputation: 8791
RoutedEvents work sort of like that. The example you gave to us is a nice demo of how basically routedevents route.
There is however more under the hood. Its not that simple. I will give you few example.
When fired the event may switch between LogicalTree and VisualTree to find its route since the path may be defined through data which does not inherit from FrameworkElement or is not even a Visual. The path might be reversed in such case you have a tunneling strategy, else routed events bubble up by default. When routing the nodes may be visited even though event handled state is true. The event's args contain alot more than just e.Source, e.OriginalSource and e.Handled. The args contain information about previous and next node. The args futhermore may contain a list of another routedevents which shall be trigged once a certain node reached.
There is no big list of all the available handlers which belong to a certain routed event. Finding handlers is also a piece of logic that happens internally just at time since you may change templates or layout panels dynamically.
As you can see there are like 10 billion others things happening internally and its pretty complicated :)
Your example is a nice demo and dont worry you got the basics of routedevents right. You dont need to know all those internal things anyway. As long it works...
Upvotes: 2
Reputation: 25623
The logic is actually a bit different than you suggest. For instance, you will continue tunneling or bubbling even after e.Handled
has been set to true
. However, you will only invoke handlers that were registered with handledEventsToo = true
.
There are also multiple handlers per node. There may be class handlers, registered with EventManager.RegisterClassHandler()
, which are effectively static handlers shared by all instances of a given element type. Then there are the regular instance handlers, registered with the element's AddHandler()
method.
The event propagation logic looks something like this (pseudocode):
void RaiseEvent(RoutedEventArgs e) {
EventRoute route = BuildEventRoute(e);
RouteNode currentNode = route.Head;
while (currentNode != null) {
DependencyObject current = currentNode.Element;
// Update the Source of the RoutedEventArgs (OriginalSource remains the same).
e.Source = current;
// Invoke class handlers for current node, if any exist.
foreach (var handler in GetClassHandlers(e)) {
if (!e.Handled || handler.HandledEventsToo) {
handler.Invoke(e);
}
}
// Invoke instance handlers for current node, if any exist.
foreach (var handler in GetInstanceHandlers(e)) {
if (!e.Handled || handler.HandledEventsToo) {
handler.Invoke(e);
}
}
currentNode = currentNode.Next;
// Continue until we reach the end of the route.
}
}
The route depends on whether the event routing strategy is Bubble
, Tunnel
, or Direct
. Essentially, the route is built by starting at the source element and determining its parent, then filling in any intermediate nodes between the two, and continuing up the tree. The route is simply reversed for tunneling events.
As @devhedgehog points out in his answer, the process doesn't strictly follow the visual or logical tree; the conceptual parent may be the visual parent, logical parent, content parent, etc. It may cross between 2D and 3D space, from a document a visual tree, and so on.
Upvotes: 1