The use of events in Silverlight, like every other programming platform, is something usual to every programmer. Attaching the click of a button, handling the change of the index in a ComboBox, managing the mouse events to implement drag and drop of UI elements, all these tasks are so natural like ages ago it was usual to write a "gosub" to change the flow of a program written in BASIC.

Nevertheless not all the people are aware that, it exists a interesting category of events, the so-called "Routed Events", that have a special behavior. They appear very similar to every other event exposed by an element, but they are able to route the event from the innermost to the outermost in the case that multiple elements raise the same event at the same time.

As an example you can think about having three Border nested one inside the other. In this condition if you attach the MouseLeftButtonDown event of every Border when you click on the inner element you will see that the event is raised one time for each border. This happen because the MouseLeftButtonDown event is passed from the innermost Border element, to the second and finally to the third element. This is what it mean the term Routing, usually called also Bubbling.

Routed events are recognizable because the argument of the handler method is of type RoutedEventArgs or of another class derived from it. However they are limited to a small number of events that I list here: 

KeyDown,  KeyUp, GotFocus, LostFocus, MouseLeftButtonDown, MouseLeftButtonUp, MouseMove, MouseWheel, BindingValidationError, DragEnter, DragLeave, DragOver, Drop

Once you get one of these events you will get also an instance of the RoutedEventArgs class that contains two important properties. The OriginalSource property contains a reference to the element from which the event started its route. It is important to have this reference so you can apply different behaviors when you handle the event directly or indirectly because of the routing. Mostly important is the Handled property that does not carries any information but it receives a flag indicating if the event has been completely handled and the routing must stop. Setting this property to true mean saying to the runtime that the event must not be passed to the other elements interested by the routing that follow in the chain.

 

   1: private void InnerBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
   2: {
   3:     e.Handled = true;
   4: }
   5:  
   6: private void MiddleBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
   7: {
   8:     // events from InnerBorder never arrive here
   9: }
   10:  
   11: private void OuterBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
   12: {
   13:     // events from InnerBorder never arrive here 
   14:     // events from MiddleBorder are routed to here.
   15: }

 

Many implementations can benefit of this property, especially when you handle a routed event into a control and you give a special meaning to it. An example of what I'm saying is the Button control. The Button obviously handles the MouseLeftButtonUp event and uses it to generate the Click event. If you try to handle the MouseLeftButtonUp directly on the Button you will never get any event just because it is internally marked as Handled when it is transformed into the Click event. Also the Handled property may be often useful when you implement the drag and drop of elements because you probably have to prevent the propagation to nested elements, to move only one element at a time.

Handling handled events with AddHandler

As it is understandable that stopping the chain of the routing is very interesting and powerful, it is also understandable that sometimes (not so often indeed) may happen that you have to catch a routed event that has been marked as handled in a previous level of the chain. The fact is that, also if the handled flag is set, the runtime continue to follow the chain for the entire length without raising the related event unless it does not encounter a special case. Using the AddHandler method you can attach routed events and say to the runtime that you want to get the events also if they have been marked as handled:

   1: public MainPage()
   2: {
   3:     InitializeComponent();
   4:  
   5:     this.OuterBorder.AddHandler(
   6:         UIElement.MouseLeftButtonUpEvent, 
   7:         new MouseButtonEventHandler(OuterBorder_MouseLeftButtonUp_All), 
   8:         true);
   9: }
   10:  
   11: private void InnerBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
   12: {
   13:     e.Handled = true;
   14: }
   15:  
   16: private void MiddleBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
   17: {
   18:     // events from InnerBorder never arrive here
   19: }
   20:  
   21: private void OuterBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
   22: {
   23:     // events from InnerBorder never arrive here 
   24:     // events from MiddleBorder are routed to here.
   25: }
   26:  
   27: private void OuterBorder_MouseLeftButtonUp_All(object sender, MouseButtonEventArgs e)
   28: {
   29:     // here you can catch events already marked as handled
   30: }

Almost every routed event in the Framework is represented by a static property that can be used to attach it with the AddHandler method. So specifying "true" as the last parameter of the method it asks the runtime to forward the marked-as-handled events to the specified delegate.

The MSDN is really clear on this point. Also if you have the ability to change the default behavior of the routing chain it suggest not to abuse of this opportunity because it may lead to unexpected results. But if you are completely aware of what you are doing so... you are welcome.