Routed Events in Silverlight
Silverlight provides a set of events that enable you to respond to actions, such as changes in Silverlight object states and user input. In the managed API, you write the handlers for events generated within your application, and these handlers are defined in the code-behind file that backs the XAML definition of your application's visual elements.
In Silverlight, there are two general event cases: input events and non-input events. Because Silverlight is built to work within the plug-in architecture of the hosting browser, the input stimulus for input events is initially handled by the browser. The event is then sent to the Silverlight plug-in and generates events in whichever Silverlight API you are using: the managed API or the JavaScript API.
In WPF, the RoutedEvent class holds additional information, such as the routing strategy. In Silverlight 3, a routed event does not require this information; only the bubbling route is supported in Silverlight 3, and the event identifiers are not required for trigger usages because trigger usages are limited to the Loaded case. Therefore, in Silverlight 3 a routed event is generally identified by name in XAML usages, which does not require the identifier field. The RoutedEvent class in Silverlight 3 exists primarily because of WPF compatibility.
Before explaining the routed events you have to understand the element tree (also known as visual tree), that is typical for every Silverlight application. It consists of the controls hosted by the application and their parent-child relationships. The routed events are directly connected with the visual tree. It is used to propagate the event upwards or downwards on it, so the event could be handled by controls that are different than the control that originally raised the event.
There are three types of RoutedEvents - direct, tunneling, and bubbling.
Direct - This type of routed events can be handled only by the controls that had raised them. They are very similar to the events we are familiar with from WinForms and ASP.NET.
Tunneling - By this type of events, first the handlers of the root element of the visual tree are invoked and then the event travels through the child elements till it reaches the one that originally has raised it.
Bubbling - This type of events is first handled by their source and then the event handlers of the parent elements are invoked till the event reaches the root element of the visual tree.
In Silverlight a routed event is is bubbled up the user interface tree whether or not it is handled by the object that receives the event. Event arguments of every routed event have a Handled property. When set to true in the event handler the event won't propagate further in the visual tree. Silverlight 3 employs event routing for built-in events but, unfortunately, you cannot create your own custom RoutedEvents.
The only type of routed events in Silverlight is the bubbling event. Such events for example are the MouseLeftButtonDown and the MouseLeftButtonUp. The control specific events don't bubble - like the Click event of the Button control will be handled at the Button level.
I have created a demo for explaning the Routing Events. The visual tree of my example looks as below.
<Grid>
<Grid>
<Rectangle/>
<Rectangle/>
<Rectangle/>
<Rectangle/>
</Grid>
<Grid>
<StackPanel>
<CheckBox/>
<ListBox/>
</Button>
</StackPanel>
</Grid>
</Grid>
The CheckBox on the top allows you to control the event bubbling. See the running demo below.
XAML code :
<UserControl x:Class="RoutedEvents.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot"
Background="BurlyWood"
Width="600" Height="500"
MouseLeftButtonUp="LayoutRoot_MouseLeftButtonUp">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid x:Name="GridOne"
Grid.Column="0"
Grid.Row="0"
Background="CornflowerBlue"
Margin="5,5,5,5"
MouseLeftButtonUp="GridOne_MouseLeftButtonUp">
<Rectangle x:Name="redRect"
Width="180"
Height="480"
Grid.Column="0"
Grid.Row="0"
Fill="Red"
VerticalAlignment="Center"
MouseLeftButtonUp="redRect_MouseLeftButtonUp"/>
<Rectangle x:Name="BlueRect"
Width="160"
Height="460"
Grid.Column="0"
Grid.Row="0"
Fill="Blue"
VerticalAlignment="Center"
MouseLeftButtonUp="BlueRect_MouseLeftButtonUp"/>
<Rectangle x:Name="YelloRect"
Width="140"
Height="440"
Grid.Column="0"
Grid.Row="0"
Fill="Yellow"
VerticalAlignment="Center"
MouseLeftButtonUp="YelloRect_MouseLeftButtonUp"/>
<Rectangle x:Name="HoneydewRect"
Width="120"
Height="340"
Grid.Column="0"
Grid.Row="0"
Fill="Honeydew"
VerticalAlignment="Center"
MouseLeftButtonUp="HoneydewRect_MouseLeftButtonUp"/>
</Grid>
<Grid x:Name="GridTwo"
Grid.Column="1"
Grid.Row="0"
Background="DarkSeaGreen"
Margin="5,5,5,5"
MouseLeftButtonUp="GridTwo_MouseLeftButtonUp">
<StackPanel x:Name="stakPanelResultHolder"
Grid.Column="1"
Grid.Row="0"
Margin="5,5,5,5"
Background="Brown"
MouseLeftButtonUp="stakPanelResultHolder_MouseLeftButtonUp">
<CheckBox x:Name="chkIsRouting"
Content="Disable Routing"
Margin="5,5,5,5"
/>
<ListBox x:Name="Results"
Margin="5,5,5,5"
Height="150" Width="380">
</ListBox>
<Button x:Name="btnClear"
Width="70" Height="30"
Content="Clear"
Click="btnClear_Click"/>
</StackPanel>
</Grid>
</Grid>
</UserControl>
Code behind :
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace RoutedEvents
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void LayoutRoot_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = new ListBoxItem();
item.Content = "The LayoutRoot has handled the MouseLeftButtonUp event.";
Results.Items.Add(item);
if ((bool)chkIsRouting.IsChecked)
{
e.Handled = true;
}
}
private void GridOne_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = new ListBoxItem();
item.Content = "The GridOne has handled the MouseLeftButtonUp event.";
Results.Items.Add(item);
if ((bool)chkIsRouting.IsChecked)
{
e.Handled = true;
}
}
private void GridTwo_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = new ListBoxItem();
item.Content = "The GridTwo has handled the MouseLeftButtonUp event.";
Results.Items.Add(item);
if ((bool)chkIsRouting.IsChecked)
{
e.Handled = true;
}
}
private void redRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = new ListBoxItem();
item.Content = "The redRect has handled the MouseLeftButtonUp event.";
Results.Items.Add(item);
if ((bool)chkIsRouting.IsChecked)
{
e.Handled = true;
}
}
private void BlueRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = new ListBoxItem();
item.Content = "The BlueRect has handled the MouseLeftButtonUp event.";
Results.Items.Add(item);
if ((bool)chkIsRouting.IsChecked)
{
e.Handled = true;
}
}
private void YelloRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = new ListBoxItem();
item.Content = "The YellowRect has handled the MouseLeftButtonUp event.";
Results.Items.Add(item);
if ((bool)chkIsRouting.IsChecked)
{
e.Handled = true;
}
}
private void btnClear_Click(object sender, RoutedEventArgs e)
{
Results.Items.Clear();
}
private void HoneydewRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = new ListBoxItem();
item.Content = "The HoneydewRect has handled the MouseLeftButtonUp event.";
Results.Items.Add(item);
if ((bool)chkIsRouting.IsChecked)
{
e.Handled = true;
}
}
private void stakPanelResultHolder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = new ListBoxItem();
item.Content = "The stakPanelResultHolder has handled the MouseLeftButtonUp event.";
Results.Items.Add(item);
if ((bool)chkIsRouting.IsChecked)
{
e.Handled = true;
}
}
}
}
Use the routed events where needed and be sure to stop the events you don't need to propagate through the visual tree, because this might cause faults and malfunctions in your application. The routed events are a very powerful mechanism that can improve the functionality of our applications a lot.
Reference Articles
- Silverlight Documentation
- www.silverlightshow.net
