Download the tutorial in PDF format
By Jesse Liberty Download the code
Controls
Silverlight 2 Beta 1 has over 2 dozen User
Interface (UI) controls and the final release will have more. .NET
programmers already familiar with ASP.Net or (especially) WPF, will
find using the Silverlight controls very natural and straight forward.

Silverlight controls were created to look great
and provide extensive and customizable functionality right out of the
box. Moreover, all of the standard controls can be modified in numerous
ways to meet your needs.
The look and feel of the control can be tweaked
through styles or can be entirely redesigned through templates, and the
behavior of the controls can be modified through event handlers. In
the rare cases when none of that is enough, you can create (or derive)
your own customized controls as well. This tutorial will cover event
handlers; and Syles and Templates will be covered in tutorials to
follow shortly and a tutorial on custom controls will follow some weeks
later.
A Note On The Design Surface
During the Beta Period the Design Surface in Visual Studio will increase its functionality.
At the time of this writing, for example, just
before we release Beta 1 of Silverlight 2, the design surface in Visual
Studio will reflect changes made in the markup (XAML) but you can not
yet drag and drop controls onto the design surface (though you can drag
and drop controls intot he markup and see the effect immediately in the
design window).
That is a temporary limitation. For now, one
alternative is to use Expression Designer as a fully functional
design surface though it was created for UI designers, and not for
programmers.
On a personal note, I find the Intellisense
support in Visual Studio is an excellent way to do my design, and to
learn more about the controls at the same time, but Expression can be
faster when your design is complex.
Note that controls can also be created dynamically (in code, at run time) as explained at the end of this tutorial.
Layout Controls
While we intend to devote an entire tutorial to
the details of the layout controls later in the series, their
fundamentals are both straight-forward and essential to the creation of
Silverlight applications.
Layout controls are used to place visible
controls in your Silverlight application. You can think of the three
major layout controls as “containers” into which you place your visible
controls (or other layout controls)
The Layout controls consist of the three Panel types (that is, the three types that derive from the abstract type Panel):
- Canvas – the simplest container, used for absolute positioning (and unchanged from Silverlight 1.0)
- StackPanel - used to position objects next to one another, either
horizontally or vertically (one on top of the other). The StackPanel is
rarely used alone though it can be. It is most effective inside a Grid.
- Grid – the most flexible layout control; essentially a table used for positioning objects in rows and columns.
There is also a Border layout control, but we won’t be using the Border in this tutorial.
Canvas
The Canvas provides “absolute positioning” of
controls. Canvases can also provide a background, but note that the
default background color for a Canvas is transparent and the default
width and height are zero.
Every visible UI control will refer to its
position on the Canvas by referring to the Canvas’s Left and Top
properties. This ability to use the Canvas’s property within a control
is referred to as an Attached Property. For example, the Button object might use the Attached Property Canvas.Left to position itself with respect to the left border of its surrounding canvas,

This will place the button 150 pixels to the
right of the left border and 50 pixels down from the top border of the
immediately surrounding Canvas, as shown in the figure,
You may have noticed the
apparent paradox that the Canvas defaults to a width and height of 0x0,
yet you often position an object “in” the canvas at, e.g.,
Canvas.Left=”150” Canvas.Top=”50”. This works as if the canvas had a height and width large enough to encompass all its controls.
Creating The First Example
We recommend you create every example as you
progress through these tutorials. To create the above admittedly
trivial example, open Visual Studio 2008 and click on Create Project and then in the new project window you’ll want to create a C# project using the Silverlight Application Template.
Pick a location for your application and give it a meaningful name; be sure that you are building against the latest framework.
When you click OK you will next be asked if
you’d like to generate a Web Site or a Web Application (using the top
radio button) or just a test page (using the bottom radio button) as
shown in the next figure.
If you create just a test page, the project is kept very simple,
but if you generate a Web Site or Web Application Project, then Visual
Studio creates two projects in your new solution; the Silverlight
Application
and a test application; excellent for test-based programming but more than we need right now.
In either case it sets up your development
environment and guesses, incorrectly this time, that you’d like to wrap
your application in a grid,
StackPanels
StackPanels are typically combined with other
layout controls. They allow you to stack objects one on top of the
other, or next to each other (like books on a shelf).
One convenience of a StackPanel is that you do
not have to provide the absolute position of the objects held by a
StackPanel, the are positioned relative to the object declared earlier
in the Stack..
In the following example, you will stack a
TextBlock on top of a TextBox which in turn will sit on top of a Button
that will sit on top of a CheckBox (shades of Yertle the Turtle!)

There is quite a bit of information in this code snippet, so let’s unpack it piece by piece.
The top and bottom lines show the declaration of
the StackPanel in the XAML file. The StackPanel is declared with two
attributes: a BackgroundColor (Beige) and an Orientation (which must be
either Vertical or Horizontal).
There are many other attributes you can set, as
will be true for nearly all the controls These, along with the methods
are conveniently listed in the documentation,

By setting the Orientation to Vertical we are stacking its contents one on top of another rather than side by side.
Within the StackPanel, the four objects are
declared, and the order of their declaration will determine the order
in which they are stacked. Each is set with its TextAlignment property
set to Left so that they will align, and each has its Margin property
set.
The Margin property is actually an object of type Thickness,

The documentation states that when you are declaring a Thickness object in XAML you may do so in one of three ways:
1. You
may provide a double that will be the margin for the margin on all four
sides (left, top, right, and bottom) uniformly around the object. Thus,
you might write

Thereby isolating the button with a margin of 100 on either side and above and below,

Notice that to accommodate the oversided margin, the width of the button was compromised!
The second way to declare a Thickness (and in
this case, a Margin) is to provide the sum of the sides and the sum of
the top and bottom (thus, the sides must be equal and the top and
bottom must be equal,

The effect of this declaration is that the left and right margins are 25, and the top and bottom margins are each 10.
Finally, you may declare each independently, as long as you do so in the required order:

That is: Left, Top, Right, Bottom; or in this
case, the Left margin is 10, the Top margin is 2, the Right margin is 0
and the Bottom margin is 1.
Once the four controls are placed in the stack Panel, and aligned, the stack panel is responsible for their placement,
Notice that the StackPanel is responsible for
its own background color, and for stacking its contents (the four
controls) but each control is responsible for its own alignment and
margins.
Horizontal Stack Panels
If we want to shift the stack panel to align all
the controls into a single row, we’ll want to make a few additional
changes. Not all controls default to aligning in the same way (top,
center or bottom) so we’ll explicitly set their vertical alignment to
“Center,” just as we previously set their horizontal alignment to
“Left”. Let’s also set the margins to provide a bit of space between
each object as the default is to abut each object.

Note that I set the left margin on the text box
to 5 (rather than 10) to bring it a bit closer to the TextBlock that
serves as its label.

Grids
Grids (not to be confused with DataGrids) offer
easy and exact placement by providing a table-like structure. You
declare rows and columns, and then place controls into a specific
row/column location using Extended Properties.
While you can tweak your grids to achieve very precise placement, the fundamental use of grids is extremely straight forward.
You typically declare a grid, declare its rows
and columns and then start placing controls into specific locations
(e.g., column 1, row 3). To see this at work, start a new Silverlight
project called EasyGrid and notice that Visual Studio has, in fact,
created a Grid for you.
We’ll name the grid, and define the Rows and
Columns, designating, if we choose, the minimum and maximum dimensions
of each. With that done, we can place whatever controls we want into
each “cell” of the Grid, as shown in the code,
The area circled on top defines three rows and
three columns, though by no means must your grid be symmetrical. I
chose to set both a minimum and a maximum height for the rows, but
neither for a minimum nor a maximum width for the columns.
With that done, I placed TextBlocks into column 0 of rows 0,1 and 2, serving as prompts to the user.
For user input, I placed two Text boxes, one
each in rows 0 and 1 (spanning columns 1 and 2 – see the highlighting)
and then in row 2 I placed two CheckBox controls, one each in columns 1
and 2. For fun, I set the isChecked property of the first checkbox to
true. The result is shown here,
As you can see, the table neatly lines everything up, with a minimum of fuss.
Grid Properties
Each row or column may have its own properties,
and each control may have properties (or styles) that determine how it
is positioned in its location.
In addition to supporting placement in a “cell”
in the table, and “rowspan” and “colspan” familiar from HTML,
Silverlight Grid supports a few advanced layout techniques:
Auto
With Auto, the Grid’s space is distributed evenly based on the size of the content within a column or row.
Star or Proportional Sizing
In proportional sizing the value of a column or
row is expressed in XAML as *. However, you can give twice the space
to one column or row as another by using 2* (or a 5:7 ration by using
5* and 7*). If you combine this with HorizontalAlignment and
VerticalAlignment, which default to a value of Stretch (indicating that
the cell will fill the available area) you can assign the available
space in whatever proportions you choose without assigning absolute
values.
Minimal or Controlled space
By default child elements of grid take up the
least amount of space necessary to to accommodate the largest content
within a cell in a given row or column. You can take greater control
over positioning, however, by using the margin and alignment properties
as seen in the discussion of StackPanel above.
Sizing Units
To provide the most flexibility, Grid columns
and rows are sized by GridLength objects which use the GridUnitType,
which in turn allows you to choose among:
- Auto (size based on the size properties of the object being placed in the grid)
- Pixel (size in pixels)
- Star (size based on a weighted proportion of the available space)
Control Events and Event Handlers
In Silverlight 2 each class declares whether it
is supported by managed code or not, by including (or not including)
the x:Class attribute in its root element. These tutorials will assume
you are working in managed code, and that this attribute is therefore
present.
When you create a C# Silverlight application, this is placed for you, as shown here,
Declaring Event Handlers
There are two ways to declare event handlers in
Silverlight: in the XAML file or in the code-behind. If you declare
event handlers in XAML you cannot add parameters. A typical event
handler declaration in XAMl might look like this

Here I’ve declared a Canvas object and assigned
an event handler named “Canvas_Loader” to the Loaded event (a
pre-existing event common to all canvases that fires when the Canvas is
loaded (created).
I also declare a button in the XAML and set its content to Hello.
This code appears in Page.xaml. In the code
behind file, Page.xaml.cs, I must now implement the event handler with
the name I’ve promised to use:

The first thing to notice is that the name of
the method is identical to that as declared in the XAML. The second is
that this method follows the pattern of all .NET event handlers: it
returns void and takes two parameters: the first of type object (and
containing a reference to the object that raised the event) and the
second of type EventArgs or a type that derives from EventArgs; in this
case RoutedEventArgs. We’ll come back to RoutedEventArgs in just a
moment.
The implementation is that when the Canvas is
loaded it grabs the button declared in the XAML file and changes its
Content property from “Hello” to “Please push me”,

Even in this incredibly simple example there are two important things to notice:
1. You did not have to declare myButton in the code behind; it was known simply by declaring it in the XAML
2. The button adjusted its size to accommodate the larger string.
Declaring Event Handlers In Code
I admit it, I have a strong preference for
declaring all event handlers in code. I believe it is better
encapsulation, making for more scalable and more maintainable code. But
this is a personal opinion.
In any case, if you want to declare your event handlers correctly
in code, Visual Studio makes it very easy to do so. The most common way
is to wire up the event handlers in the OnLoaded event handler, with that event (Loaded) wired up in the Page’s constructor.
To see this, return to your previous code and
remove the event handler from the XAML.. Save the XAML file and open
the code behind.
In the constructor, type Lo. Intellisense
will pop up and offer to help you create the EventHandling code,
landing on the event Loaded, which is exactly what you want. The tip
(next to the Intellisence box) shows the type of the event.

We’ll return to the fact that Loaded is a
RoutedEventHandler shortly. Press tab to accept Loaded and type += to
begin adding the delegate. (If delegates and events are new to you, you
may want to read this article. It’s a bit old, but still accurate.
Intellisense will walk you through each step of
wiring up the event handler, and if you let it, will also create the
stub of the event handler method, ultimately placing your cursor in the
method which it prefills with an exception (in case you forget to add a
meaningful implementation.
Take the opportunity to add two controls to the
Canvas: a button (“myButton) and a check box (“rushOrder”), and create
a Click event handler for the button and both Checked and Unchecked
event handlers for the checkbox::
RoutedEvents
Silverlight extends the CLR event handling mechanism (borrowing from WPF) with RoutedEvents.
Normal CLR events are ignored if no object
subscribes to them. This presents a problem for WPF and Silverlight
controls because many controls are created from numerous visual
“pieces” such as text, curves, shapes and so forth. Moreover, the
developer is allowed and encouraged to add one control onto (or into)
another.
A button may have text and a rectangle along
with a background and more. (Silverlight designers can be quite
creative in mixing together visual elements). It would be tedious to
force the developer to register a mouse event handler for the text of
the button and for its rectangle and each of the other controls and
objects that compose the button. Tedious, and error prone.
As an example, in a series of Silverlight 1.0
demonstrations I created some months ago, I used simple graphic
primitives to create a “Little Green Man” out of a canvas, four
ellipses and a path (a curved line). Let’s add that to the XMAL file:

Here’s what he looks like when you run the application…

Cute, but not a control; but we can use in a
control. Let’s shrink the Little Green man and put him inside a button.
This is easy to do; just divide every value in his dimensions by 5 as a
start to make him a Very Little Green Man

That makes him a lot more manageable

He’s now
just the right size to put inside a button. We do that by creating a
composite control by combining a standard button and our VLGM into a
canvas, which from then on we can treat as a single control,

There are
a lot of canvases here (and if I were doing this again, I might look at
using StackPanels and Grids to simplify, but remember I’m porting this
from a previous application and often the easiest way to port an old
application is to make small changes).
The first canvas is the new composite button that will contain a Silverlight button and our shrunken Very Little Green Man.
The
second canvas (whose Canvas.Left and Canvas.Top positions it within the
Composite Button) holds the parts of the VLGM: the ellipses and the
path).

As the developer of composite button, you
certainly don’t want to have to wire up separate event handlers for the
internal button (which itself may be made up of text, a rectangle,
etc.) and the VLGM (which we know is made up of four ellipses, a path
and a canvas) . What is needed is a way to catch the click event on any
of these objects and pass it up to the object that has the event
handler (in this case, the Composite Button canvas.
How Routed Events Work: Event Bubbling
The defining characteristic of Routed Events is that each event is passed up the Interface Tree in a process known as bubbling (the events are like tiny bubbles rising up from the bottom)
If you click on the VLGM’s middle eye the event
is passed up the UI tree to the canvas that holds that eye, from there
to the composite button where it is responded to.
Implementing Dragging
To give our CompositeButton something to do, and
to see that the RoutedEvents are actually working, let’s implement the
ability to drag the button around the Silverlight application (why
not?)
Your XAML file is almost ready. To really
see the effect of RoutedEvent handlers, though, I’d like to name the
VLGM so that we can later add a separate event handler for that part of
the composite control.

Implementing the Event Handlers
Our goal is to be able to click on the
CompositeButton and drag it around the browser. To do so we need to
implement three event handlers:
- MouseLeftButonDown (pressing down on the left mouse button)
- MouseLeftButonDown (releasing the left mouse button)
- MouseMove (dragging with the left mouse button depressed)
The logic for MouseLeftButonDown is to get
the current position of the mouse (and store it) and then to capture
the mouse. Normally when you move off an object it stops sending
MouseMove events, but in a Drag and Drop application we don’t want it
to stop sending MouseMove events until the button is released.
To keep track of the position of the mouse and
whether we are tracking it, we’ll create three private member
variables. Since no methods of other classes will need access to these
values, there is no advantage to making them properties.

CompositeButton_MouseLeftButtonUp is passed the
usual two parameters, but since this is a RoutedEvent you must
remember that you cannot know the exact type of the sender (it could be
a Canvas, a path, etc.). You can know, though that it is a
FrameworkElement; the base class of all controls

The second parameter is of type
MouseButtonEventArgs, filled with… well not that much useful
information but one very useful method,

With this we’re all set.
The method begins on lines 80 and 81. On lines
83 and 84 we call the GetPosition method discussed above, passing in
null indicating that we do not want to provide a coordinate origin –
that gives us the X and Y as an absolute position with respect to the
surrounding canvas which is just what we want.
On line 85 we set the Boolean value
trackingMouseMove to true (we are now tracking the mouse) and we are
ready to capture the mouse. Sender, however is an object and it can’t
Capture the Mouse. Moreover, as noted above, we don’t know what type of
control we have (could be an ellipse, TextBox or RadioButton. We’ll
cast to the base type FrameworkElement and, being neurotically careful
make sre the cast was successful (if not we’d get back null) and then
call CaptureMouse. (A robust program would handle the condition where
fe is null!)
The implementation of MouseLeftButtonUp is now straight-forward, we undo the work we just did,

NB:
To save space, I’ve left out the if statement, and in this and future
tutorials I’ll often leave out the exceptions, testing and other error
checking code necessary to build robust code but which is clutter when
covering new concepts.
The real work is in MouseMove

On line 50 we test if we’re tracking MouseMove.
If not, we can exit. If we are (and thus the left mouse button is down)
we get the current mouse position just as we did before, and we get the
sender as a FrameworkElement as we did before.
We need to ask the Framework element to give us
its Attached Property Canvas.Left, and we do that using the GetValue
method, which takes the LeftProperty property of the Canvas object as a
parameter, which in turn we convert to a double and a assign to a
temporary variable on lines 55 and 56
We do the same for the Canvas.Top property on lines 57 and 58
We now know the current position of the mouse,
the Canvas.Left and Canvas.Top and the original position. With that we
can compute where the object should move to, which we do on lines 63
and 64. On lines 63 and 64 we reach back into the object and set its
Attached Properties, and then we clean up by setting the BeginX and
BeginY values to the current and CurrentY and we’re ready to handle the
next MouseMove.
When you run this application the three objects
come up on the screen. Click on the composite button and the mouse down
event handler kicks into effect. Drag and watch the button move. Lift
your finger off the mouse and it stops moving

Notice that you can click anywhere on the
composite button and the event is routed to the event handler which is
attached to the Canvas.There is no separate registration for each part.
Of course, there could be. Remember that we named the Very Little Green
Man. We can register an event handler for that,

Our implementation will be to reduce the opacity of the VLGM inside the button,

Now, we can continue to click anywhere we want,
but if we click in the middle eye the Little Green Man begins to fade a
bit as he moves (notice that the event continues to be routed, even
though it is also handled by the node lower in the UI tree,

Each time we drag the LGM he fades a bit more.
Moving other Controls
Now, just for fun, return to the event handler
registration, and register the same handlers but for the other button:
Note, we’ll reuse the same exact implementation methods.

Because the event handlers were written to
handle whatever FrameworkElement is clicked on, they’ll work just fine
(though I’d argue they should now be renamed, but that is left as an
exercise for the reader!)
The result? Two draggable buttons..

Creating Controls Dynamically
In Silverlight 2, all XAML controls are
isomorphic with CLR objects. That is, anything you can create in XAML
you can create in code.
Where you might write,
<Button x:Name="myButton" Content="Hello" />
You can also write
Button myButton = new Button();
myButton.Content = "Hello";
While it is possible to create all your controls
and objects in code, best practices dictate that it is usually better
to do so in XAML. The most compelling reason is that XAML is highly
“toolable” – that is, it lends itself to round-trip modification in
tools such as Visual Studio and Expression and thus is easier to scale,
modify and maintain.
On the other hand, there are times that you
can’t know at design time which or how many objects you’ll need, and
the ability to create objects dynamically can be a fundamental
requirement.
We can make a minor modification to our existing
program to add another button on the user’s request. Add the following
button to the XAML file:

The effect is to add a button to the page with
the words “Add Another.” When the user clicks on this button, we want
to add a button to the UI and we want that button to have its own size,
position and behavior.
We do all of that in the code behind.
Creating a Button Dynamically
Begin by adding an event handler for the new button you just added to the XAML file:
Another.Click += new RoutedEventHandler(Another_Click);
The implementation for this event is to create the new (dynamically created) button,

On line 45 we create a new instance of the CLR
object Button. We set its content on line 46 and its Width and Height
on the following two lines. On line 50 we register its event handler
for the click event. Finally, on line 51 we access the topmost
container in the XAML file (named LayoutRoot) and add the new Button to
the children collection. That last action effectively adds the new
button to the page.
The implementation for the click event will be shared by all the new buttons created,

That’s all there is to it, at least for creating
this simple control with its simple event handler. Running the
application shows that clicking on the button you added in the XAML
will create the new dynamic button, and clicking on that will invoke the event handler that you dynamically wired up.
