Today is Day #17 of the 31 Days of Silverlight, and today we are going to focus on layout of your Silverlight application. There are several layout controls that I have used in the past tutorials, and I have completely glossed over how each of these work. Today we will discuss those options available to you.
The Canvas
In this first part of three, we’re going to be talking about the <Canvas> option. I equate this to a more familiar concept in HTML known as absolute positioning. Each element will be given its own specific location on the page, and nothing but code can move them. This is also one of the downfalls of the <Canvas>, but for many apps, this may not matter.
With elements absolutely positioned, things just don’t adjust. If a user resizes their browser, the elements don’t move. If a user tries to see your application on a smaller screen than you anticipated, part of the app is going to be hidden. (Think mobile phones, for example…if you position everything out to fit in a window 800 x 600, that 320 x 200 screen is not going to show very much).
However, this is still one of the fastest ways to get your elements positioned on a page, and the taboos that came with absolute positioning in CSS are erased, because we’re now developing in a universal plugin, not 17 flavors of browser and platform combinations.
So how do we do it? Here’s some example code:
<UserControl x:Class="SilverlightLayoutOptions.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">
<Canvas x:Name="LayoutRoot" Background="White">
<Ellipse Width="100" Height="100" Fill="Red" Canvas.Left="100" Canvas.Top="100"/>
<Rectangle Width="100" Height="100" Fill="Blue" Canvas.Left="200" Canvas.Top="200"/>
<Ellipse Width="100" Height="100" Fill="Green" Canvas.Left="300" Canvas.Top="0"/>
</Canvas>
</UserControl>
As you can see, it’s pretty straightforward. We have a button, an ellipse, and a rectangle, and their positions are defined by the Canvas.Left and Canvas.Top attributes. You can also nest <Canvas> tags, and the Left and Top attributes apply to the immediate parent of the element you are positioning.
Click here to see the code above running in another browser.
The StackPanel
We are now looking at a second way to lay out your interface in Silverlight. This will be far more familiar to the CSS fanatics out there. The primary concept is simple…we’ll be using a StackPanel control to position our elements.
For those of you less familiar with CSS, this is more of a flow-based layout. As the size of the Silverlight container grows and shrinks, you will find that the elements will move to accomodate that space. I, personally, would not recommend approaching your entire interface with this technique, but it certainly makes sense for small portions, like navigations and lists of data.
Most of our positions will be determined by manipulating the margins of the individual elements on the page. In doing so, we can place our elements specific distances apart without having to specify their exact position on that page.
In the example below, I have 6 buttons that I want to arrange in a 3×2 block. I start with an outer stack panel that, by default, will stack my elements vertically. However, I want to have two rows of 3 buttons each, so I am going to nest two more stack panels. Each of these will have the Orientation attribute of the StackPanel set to Horizontal, to create the rows.
<UserControl x:Class="SilverlightLayoutOptions.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">
<StackPanel x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Horizontal">
<Button Content="Top Left" Width="100" Height="50" Margin="0,0,0,0" />
<Button Content="Top Center" Width="100" Height="50" Margin="0,0,0,0" />
<Button Content="Top Right" Width="100" Height="50" Margin="0,0,0,0" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="Bottom Left" Width="100" Height="50" Margin="0,10,10,0" />
<Button Content="Bottom Center" Width="100" Height="50" Margin="0,10,10,0" />
<Button Content="Bottom Right" Width="100" Height="50" Margin="0,10,10,0" />
</StackPanel>
</StackPanel>
</UserControl>
For demonstration purposes, I also modified the margins of a few of the buttons, just to show that we are merely stacking these elements. This is not a grid, or table, and certainly not absolute positioning. These elements are merely stacked, either horizontally or vertically, with margins as the leverage to move things around a bit.
NOTE: I have a major complaint about how margins were implemented in Silverlight.
Again, for those of you familiar with CSS, the de-facto standard for specifying margin sizes was one of two formats:
margin-left:10px;
margin-right:10px;
etc.
OR
margin:0 10px 0 10px;
Each of those examples would have given you a 10 pixel margin on both the left and right of the element you were styling. In the second example, we are using a bit of shorthand to specify ALL of the margins in one line. They start with the top, and go clockwise. margin:Top Right Bottom Left;
In the margin implementation for Silverlight, it would appear that they also offer a shorthand version for margins (actually, the only version is shorthand), and they did not follow the standard convention mentioned above. Instead, they chose to use Margin=”Left, Top, Right, Bottom”. Since this is a web technology, I would have liked to see them stick with the web convention for margins.
The Grid
In the third and final section of Silverlight Layout, we are going to be discussing my preferred option for interface creation: The Grid. In many ways, I would compare this to table based layout in HTML (which is generally a bad word), but I will come to its defense in Silverlight today.
There are many reasons why table-based layouts should not be used in HTML. A few of them are:
1) Tables are very heavy. With all of the <tr> and <td> tags you need, the page bloats quickly.
2) CSS is the right way to do it. Once you harness the power of CSS, you have far greater control of your layout (not to mention accessibility) than you do with tables.
3) Tables are very difficult to manipulate once they are in place. If you’ve ever tried to move things around in a legacy table layout, you know what I’m talking about.
The reason I am recommending this type of layout is because those problems have been addressed in Silverlight with the <Grid>. So let’s take a look at the code.
<UserControl x:Class="SilverlightLayoutOptions.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="White" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="100" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle Fill="Red" Grid.Row="0" Grid.Column="0" />
<Rectangle Fill="Orange" Grid.Row="0" Grid.Column="1" />
<Rectangle Fill="Yellow" Grid.Row="0" Grid.Column="2" />
<Rectangle Fill="Green" Grid.Row="1" Grid.Column="0" />
<Rectangle Fill="Blue" Grid.Row="1" Grid.Column="1" />
<Rectangle Fill="Purple" Grid.Row="1" Grid.Column="2" />
<Rectangle Fill="Black" Grid.Row="1" Grid.Column="3" />
</Grid>
</UserControl>
You’ll notice that this doesn’t really look like HTML table layout that you’ve seen in the past. Instead, we’ve seperated (still in the same file, mind you) the content from the layout. We don’t have to wrap each element in a set of table tags. We define a grid, and then we assign each element to a cell of that grid. I’ve also turned on the ShowGridLines attribute, so we can see exactly what the grid looks like. We can also assign height and width values to the cells of the grid, up to and including the wildcard “*” character. This tells the application to use the rest of the remaining space for that cell. If you have more than one wildcard cell, it will split the unclaimed space evenly between them. Please note that the grid starts with 0,0, not 1,1. So our Red Rectangle is assigned to Grid.Cell=”0″ and Grid.Row=”0″ which puts it in the top left corner. Each of the respective rectangles after that is assigned to their respective grid location as well. The design pane of Visual Studio 2008 looks like this with the XAML I just used:
You’ll notice that the Black rectangle is missing. That is the one assigned to the wildcard space. In the design pane, there’s not a Canvas width and height defined. In the XAML, I did not specify a width and height for my Grid. This causes it to fill the entire space of the window it is rendered in. When we run the application, however, you’ll see that the black box is, in fact, there, and takes up the rest of your browser window.
Summary
Today we talked about the layout controls available to you in Silverlight. They each serve specific purposes, and I wouldn’t say that one is necessarily better than the other. If you’d like to download the sample code for this project, you can download the StackPanel, Canvas, and Grid Layout Options for Silverlight sample here.
Leave a Reply