3. Layout Controls
Along with the controls we have seen for the
presentation of content and user interaction, a final set of controls
exists whose purpose is to facilitate the flexible and predictable
layout of control elements on each page. In this section, we will
explore some of these controls, covering the Grid, StackPanel, ScrollViewer, Border, and Canvas controls.
3.1. Grid Controls
One of the most flexible of the layout controls is the Grid
control. This control can seem somewhat puzzling to developers who
encounter it in Silverlight after having worked with grid controls in
WinForms development because the expectation is that it will offer
functionality along the lines of a data grid, presenting tables of data
to the user. That is not the Grid's purpose, however; instead it is used to allow the organization of other controls into consistent rows or columns.
A reasonable approximation of the Grid is perhaps the HTML table element, which also defines a layout of rows and columns, and allows content to be placed within them. Just like HTML tables, Grids
allow row and column sizes to be set, arbitrary content to appear
within each cell, and their content to span across multiple rows or
columns.
For an example of a useful application for a grid,
imagine that we are creating a Settings page for a game that we are
writing. The page can be divided into two columns. For each setting,
the first column will contain a TextBlock providing the name
of the setting, while the second column will contain a control allowing
the value of the setting to be entered or modified. With the columns
configured in this way, we can then add as many rows as we need to
accommodate each of the available settings.
When a Grid control is first added to the
page, it is completely empty and does not define any rows or columns.
This empty configuration results in a default row and a default column,
providing a single empty cell ready for use. In order to turn the
control into an actual grid, we need to tell it how many rows and how
many columns we want it to display.
There are two ways to achieve this. The first is
through the page designer. If you hover the mouse cursor over the top
or left edge of a selected Grid control, you will see that it
displays a split point at the cursor position. Clicking the left mouse
button will create a new column or row boundary at that position. This
process can be repeated for each of the rows and columns that are
required, as shown in Figure 14.
The second approach is to create the rows and
columns within the XAML. When configuring the grid in this way, we do
not necessarily need to specify the size of each of the rows or
columns. Instead we can simply state that there is a row or column and
let Silverlight automatically manage the size based on the content that
we subsequently provide.
Listing 10 shows a Grid defined in XAML with two columns and three rows, with no sizes specified at all.
Example 10. Declaring rows and columns for a Grid in XAML
<Grid Name="grid1"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> </Grid>
|
If you declare a grid in this way and then view it
in the designer preview, you will see that the row and column headings,
which were showing the row or column size in pixels in Figure 14, display something that might be unexpected. All the sizes are now displayed as 1*
instead of a pixel width. Alongside this displayed value, the rows and
columns are evenly distributed across the height and width of the grid.
The reason for this behavior is that we actually have a variety of methods for specifying the sizes of rows and columns:
Fixed size: We specify a size in pixels, and the grid uses exactly this size for the row or column.
Weighted size: We specify a proportion of the grid by weighting each column, and the grid retains these weights as the grid.
Automatic size:
The row or column size will be set to match the largest item that it
contains. If there is nothing contained within the row or column at
all, it will collapse to have a size of 0.
Fixed-size rows and columns are declared by just
giving them a size value. Weighted sizes are specified by providing a
weight value followed by an asterisk. The size will be calculated by
comparing the weight of each row or column to the total weights of all
the rows or columns. For example, if two columns are present with
widths of 2* and 1*, the first column will occupy
two-thirds of the width, and the second column will occupy the
remaining one-third. A weight of 1 can be specified just by using an
asterisk character without a numeric prefix. Automatic sizes are
specified by providing the keyword Auto as the size.
The grid defined by the code in Listing 11 uses all three of these sizing methods for the three columns that it declares.
Example 11. Declaring rows and columns for a Grid in XAML
<Grid Name="grid1"> <Grid.RowDefinitions> <RowDefinition Height="50" /> <RowDefinition Height="2*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> </Grid>
|
Although the page designer clearly shows the lines
that separate the grid cells, they are only there as a guide. When the
application runs, the grid lines are completely invisible. To aid in
developing against grids, Silverlight allows the lines to be displayed
at runtime, too. This display is only intended as a development and
debugging aid, however, so there is no control provided for configuring
the appearance of the grid lines. The lines can be switched on by using
the Grid's ShowGridLines property.
So once we have defined the structure for the grid,
how then do we tell Silverlight which cell to use for each of the
controls that we want to place inside the grid? As is becoming very
common, there are two ways to do this.
The first way is to add the new control by selecting
a control in the Toolbox and drawing it into the page designer. Visual
Studio will indicate the cell into which it is being added by shading
the other cells in gray.
The second way is to add the control directly via the XAML editor. Controls added in this way should be placed inside the Grid element, but not inside the RowDefinitions or ColumnDefinitions
elements. The target cell is then specified by setting a property of
the grid as part of the new control's declaration. For example, the
code in Listing 12 places a Button
into the third row and the second column of the grid. If either the
grid row or column is unspecified, it will be assumed to be the Grid's
first row or column.
Example 12. Specifying the target Grid cell for a TextBlock
<Grid Name="grid1"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Button Content="Button" Grid.Column="1" Grid.Row="2" /> </Grid>
|
The resulting output from this XAML is shown in Figure 15.
To span content across multiple rows or columns, the contained control should set the Grid's RowSpan and/or ColumnSpan
properties. These properties should indicate how many rows or columns
should be included in the space (and both default to 1 if not
specified).
We can modify the button in the previous listing to span across the second and third rows by changing the XAML, as shown in Listing 13.
Example 13. Spanning a contained control across multiple grid rows
<Grid Name="grid1" Margin="0,0,0,333" ShowGridLines="True"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Button Content="Button" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" /> </Grid>
|
The resulting layout from this listing is shown in Figure 16.
Using the Grid control, you can get a high degree of control over the placement of controls within your page. Another use for the Grid control is to provide a host for complex layouts inside locations that do not directly permit them. For example, inside the ListBoxItem
elements that we looked at earlier we saw that only a single UI element
can be placed as the content for each list item. If we were to make
that single UI element a Grid, however, we could then place as many controls inside that Grid as we wanted.