So far, we have mainly dealt with the UI layer, but no application can be called an application without some logic. In this chapter, we are going to work mainly on the code behind layer of our application. We will discuss the concept of events, dive deep into dependency properties, interact with attached properties, and finish off with the ICommand
interface. With so many exciting topics, we'd better get started right away!
In this chapter, we will cover the following topics:
ICommand
If you have ever worked with events in any .NET-based language, you will feel right at home with Silverlight. Just like other .NET languages, you can select an element in the design area, and generate the event handler automatically using Visual Studio 2010. Of course, you are not limited to adding events only in the designer, you can attach events in the code behind as well.
The Silverlight event model is based upon the concept of bubbling events. This means that a control can raise an event that will be handled by its parent control. For example, if we have a Border
control that has a StackPanel
with images as its content, we can handle the MouseLeftButtonDown
event on the border level, instead of having to handle it for each individual image. Bubbling is a type of routed events and the only one that Silverlight supports. Bubbling means going up the control hierarchy and while WPF does support the tunneling type of routed event (going down the hierarchy), Silverlight does not.
A handful of events in Silverlight are routed events. For a full list of routed events, have a look at the MSDN documentation at http://msdn.microsoft.com/en-us/library/cc189018(v=vs.95).aspx#routed_events.
Silverlight does not support user-created bubbling events. That means that events you create on your own in Silverlight will never bubble.
Let's get the wheels rolling with understanding how to add events in Silverlight.
As mentioned previously, we can add events on both the UI (XAML) layer and code behind. Adding events with XAML is as easy as typing the event and specifying its handler. For example, the following line of code demonstrates how to declare a Click
handler for a button using XAML:
<Button x:Name="myBtn" Content="Click me!" Click="myBtn_Click"/>
By specifying the Click
method in XAML, Visual Studio creates the event handler (myBtn_Click
) in the code behind of the page. If you take a look in the code behind file, you will see the following method:
private void myBtn_Click(object sender, RoutedEventArgs e) { }
The other option of adding an event is purely on the code behind file. To attach an event handler for the Click
event of a button, we can use the following line of code:
myBtn.Click += new RoutedEventHandler(myBtn_Click);
This will also get Visual Studio to create the event handler for us, in this case the event handler will be myBtn_Click
, as specified in the declaration code.
Now that we know the basics of adding events in Silverlight, let's go ahead and understand the concept of routed events.
As we have discussed earlier, when using routed events, the control that raised the event isn't necessarily the one that handles it. This means that the bubbling begins with the control that raised the event and stops when a control, somewhere up the hierarchy handles the event, or when it reaches the top-level control.
Control-specific events, for example the Click
event for a Button
control, will never be routed. If you think about that, it's pretty logical too—why make an event bubble up the control hierarchy if it can only be handled by a specific type of control?
To demonstrate routed events, create a new Silverlight project in Visual Studio 2010 and name it Chapter4-HandleEvents.
Open MainPage.xaml
and replace your LayoutRoot Grid
element with the following code snippet:
<Grid x:Name="LayoutRoot" Background="White" MouseLeftButtonUp="LayoutRoot_MouseLeftButtonUp"> <Border x:Name="myBorder" Background="Aqua" Margin="20" MouseLeftButtonUp="myBorder_MouseLeftButtonUp"> <StackPanel Background="Red" x:Name="myStackPanel" Margin="20" MouseLeftButtonUp="myStackPanel_MouseLeftButtonUp"> <Rectangle Margin="20" Height="180" x:Name="myRectangle" Fill="DarkSalmon" MouseLeftButtonUp="myRectangle_MouseLeftButtonUp"/> </StackPanel> </Border> </Grid>
What we have here is a hierarchy of controls, starting with the top-level Grid
. Inside Grid
, we have a Border
child element, which in turn holds a StackPanel
child element, which in turn holds a Rectangle
child element. Each element has a MouseLeftButtonUp
event attached to it. Before we can run the application, we have to handle these events, so switch over to your MainPage.xaml.cs
file and add the following code snippet:
private void LayoutRoot_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { results += "LayoutRoot handled the event"; MessageBox.Show(results); results = ""; } private void myBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { results += "myBorder handled the event "; } private void myStackPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { results += "myStackPanel handled the event "; } private void myRectangle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { results += "myRectangle handled the event "; }
Add the following private variable just above the MainPage
constructor declaration:
private string results = "";
To recap, we have added all the event handlers for the events we declared in the UI layer. Each handler will add its name to the private
string variable, and the LayoutRoot
event handler will pop up a message box to the user with the content of the results
variable.
Go ahead and build your application. Run it and you should get the following result:
In the preceding diagram, we can see all of our controls. The white area is the top-level Grid
, below it we have our Border
, then StackPanel
, and finally the salmon-colored Rectangle
. Click on the Rectangle
element and you should get the result, as shown in the following screenshot:
As you can see, the MouseLeftButtonUp
event has bubbled up all the way to the LayoutRoot
control, which handled it. We know it was the LayoutRoot
event handler that finally handled it because it was the only event that popped out a message box for us. Click on any of the other controls and you will see that the event will always bubble up to the top-level control of the hierarchy.
Now you may ask yourself what if we wanted to stop the bubbling at the Border
control?
The answer to that is very easy—we set the Handled
property of the desired event handler's MouseButtonEventArgs
to true!
Switch back to your MainPage.xaml.cs
file and locate the myBorder_MouseLeftButtonUp
event handler. Add the following line of code at the end of the method:
e.Handled = true;
Build and run your application, and you should notice that the pop-up message doesn't show up unless you click directly on the Grid
control area (the white background). By setting the Handled
property, we have successfully told Silverlight that the event was handled and there is no need for it to keep bubbling up.
The AddHandler
method allows you to add a routed event handler for a specific routed event in the code behind layer. It also provides you with the option to always invoke the specified handler, even if the routed event was already marked as handled by another element along the control hierarchy. To demonstrate the AddHandler method
, let's remove the MouseLeftButtonUp
event from the LayoutRoot
control's XAML declaration. Switch over to your MainPage.xaml.cs
file and add the following line of code inside the constructor method:
this.AddHandler(UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(LayoutRoot_MouseLeftButtonUp), true);
The preceding line of code will attach the MouseLeftButtonUp
event to the this
object, which represents the current active page (MainPage.xaml
in our example), using the AddHandler
method. We aren't limited to the use of the this
object when using the AddHandler
method, and we could just as easily attach events using the AddHandler
method to any UIElement
control in the page (LayoutRoot
for example).
The AddHandler
method expects to get the following three arguments:
MouseLeftButtonUp
event. MouseLeftButtonUp
event within the page level, and we don't care if the event was already handled, we will pass true
as an argument in our example.In your code behind, add the following line of code to myBorder_MouseLeftButtonUp, myStackPanel_MouseLeftButtonUp
, and myRectangle_MouseLeftButtonUp:
e.Handled = true;
By setting the Handled
property of the EventArgs
object to true
, we tell the Silverlight framework that this event is handled and shouldn't bubble up the pipe.
Build and run your application. You should see that even though we haven't registered any event on LayoutRoot
directly, and even though we handled the event on all of the controls, we still get the message box to pop up with the text inside of it.
Try removing the event declarations on all of the controls in your XAML file and see what happens. It should come as no surprise to you that the only event handler that gets called is the one we defined within the AddHandler
method.
By using the AddHandler
method on the page level, we basically register that event for any control in the page, so no matter if the control itself declares an event handler for this method or not, the AddHandler
registered routed event will get called.
3.133.124.21