We will continue our journey to become a Silverlight guru by covering the different aspects of laying out a user interface in Silverlight. In this chapter we will get some hands-on experience of laying out, displaying, and creating different controls. We have a lot to cover, so let's get going and write some code!
In this chapter we will cover the following topics:
Imagine the following scenario:
You are responsible for building your company's new BI dashboard. You get the requirements from your superior, and you notice one of the requirements is that your application must support all of the different screen resolutions in the office. This is exactly the point of knowing the different panels that Silverlight offers and how they come into play. Silverlight, being a rich UI framework, offers six different kinds of layout panels for different causes, and as nothing beats seeing things for yourself, we will now create our first Silverlight application to see how the panels differentiate from one another.
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
To begin, you will need to complete the following steps:
Right now, our new application might look like a blank white box, but under the hood you might notice the first layout panel we are going to discuss—Grid
.
The Grid
control can be considered as Silverlight's equivalent to the good old HTML table element. Just like the HTML table, the Grid
control allows you to arrange controls in rows and columns, span a control across multiple rows or columns, and define different heights and widths for each row or column. Grid
is the control you're most likely to use the most. Not only is it the default root element control for the UserControl
and page templates, but it is also the best control to use when you want your content to extend/shrink according to the space available for it. In order to define rows and columns in a grid, we use the grid's Element
properties—RowDefinitions
and ColumnDefinitions
—respectively.
These properties act as collections for the RowDefinition
and ColumnDefinition
elements, which as you might have guessed, define a single row or column. Let's take this theory to practice, and add two rows and two columns to our LayoutRoot
grid. Add the following code snippet between the starting and closing tags for your LayoutRoot:
<Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions>
In order to see the boundaries of each cell, add the inline property—ShowGridLines
—to the Grid
element, and give it the value of True
, so your Grid
element should look as follows:
<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
Press F5 to build and launch the application. You should now have a grid divided into two columns and two rows, as shown in the following screenshot:
As we didn't specify fixed values for the grid's dimensions, the grid will resize itself according to the space available to it. If you resize the window where the grid is rendered, you will see that the grid resizes itself according to the new window's size.
Setting the dimensions of a grid or almost any other Silverlight control, is done by specifying a fixed number of pixels to the Width
and Height
properties; setting these properties for a RowDefinition
or ColumnDefinition
element is quite different.
Unlike most Silverlight controls, the Width
and Height
properties of ColumnDefinition
and RowDefinition
are represented as the GridLength
values. GridLength
allows you to select between three different ways to allocate the available space, and as the default selection is star sizing we will start with it.
Star sizing is used when you wish to distribute the available space of the grid equally between its rows or columns. Star sizing is calculated based on the amount of space a grid has; but if a grid also uses other forms of the GridLength
values, they will take precedence when the space is calculated.
But, what if you want one column to always have, for example, twice the space as another column? This is when multiplier star sizing comes into the picture. Take a look at the following code snippet:
<Grid.ColumnDefinitions> <ColumnDefinition Width="2*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions>
Can you notice the first column has a number before the star? That means it would take n times the space as the other column. You aren't limited to whole numbers when using a multiplier with star sizing. You can also define a column to be half the size of other columns by using 0.5 as the multiplier. Using a multiplier with star sizing is the perfect way to make sure a specific area of the grid is always n times larger or smaller than the other star-sized areas of the grid.
The following screenshot shows the result of our grid using the ColumnDefinitions
property from the preceding code snippet:
Absolute sizing, as the name implies, is used when you wish to give a column or a row a fixed size in pixels. The pixel value is of the double type, which means you are not limited to a value of a whole number, and you can use a floating point as your value as well. The value you set using the absolute sizing is fixed, and it won't change regardless of whether the content of the row or column is larger in size than the space you allocated to it. If, for example, you put a button with the height of 40 pixels into a fixed-size row with the height of 20 pixels, the button will get cut off. Absolute sizing takes precedence over the other two sizing options and it is always the first one to get calculated.
The following code snippet demonstrates how to set up a row with the height of 40 pixels using absolute sizing:
Auto sizing is the last of the three possible options of GridLength
. When a row or column size is set using the auto sizing option, the size of the row or column will be primarily dictated by the content within it. Why primarily? Because other than content, the following factors take part in calculating a row or column that is auto sized:
To use auto sizing, all you have to do is assign a value of Auto
to the Height
or Width
properties of a row or column in your grid. In the following screenshot, two rows were defined with the auto sizing option. Notice how the larger button dictates the height of the row and how the remaining space is just allocated for the last row of the grid, regardless of the content inside of it:
Now that we have understood how to size up our grid, it's time to put some content into it. Before we begin, make sure the code for your grid is as follows:
<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="30"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> </Grid>
If you are unsure about any of the sizing in this code, please refer to the previous section.
Positioning an element within a grid is done by using the following four attached properties:
Row:
The grid row in which the element will be positionedColumn:
The column in which the element will be positionedRowSpan:
The amount of rows an element will be spanned overColumnSpan:
The amount of columns an element will be spanned over
Attached properties are a specialized kind of properties that give a child element the ability to specify a value for a property that is actually defined in another element, which is mostly their parent element. Attached properties have a very easy-to-spot syntax—TypeName.AttachedProperty
. The primary use of attached properties is for layout purposes, but attached properties can also be found in Silverlight's animation engine and other places within the Silverlight framework. An example of an attached property can be Grid.Row
, where Grid
is the type and Row
is the name of the attached property.
Numbering of rows and columns begins at 0, and if you don't specify any value to an element within the grid, it will default to the first row (0) and the first column (again, 0).
Let's add an image to the first row of our grid but on the second column (remember: our grid is divided into two rows and two columns with different-sized schemes). Copy the following line of code to your project, just below the closing Grid.ColumnDefinitions
tag:
<Image Source="http://www.packtpub.com/sites/default/files/banners/ Learning%20jQuery%201.3.jpg" Grid.Row="0" Grid.Column="1" />
Recognizing the attached properties? Using Grid.Row
and Grid.Column
, we told our new image element that we want to position it in the first row and the second column. The result should look like the following screenshot:
To stretch the image across both columns we simply replace the Grid.Column
attached property with the Grid.ColumnSpan
attached property and provide it with the number of columns we wish to span across as the value. Setting the Grid.ColumnSpan
property with the value of 2
will give the result, as shown in the following screenshot:
To demonstrate the RowSpan
property, change the value of the first row's height from Auto
to 30
, so it will have a valid height; and change the Image
element as follows:
<Image Source="http://www.packtpub.com/sites/default/files/banners/ Learning%20jQuery%201.3.jpg" Grid.RowSpan="2" Stretch="UniformToFill" />
The preceding line of code sets the image to span over two rows and sets its stretching mode to UniformToFill
, so it would fill all the available space. This change will result in the following screenshot:
Our image now spans over two rows, each having 30 pixels of height, and as we have set its Stretch
property to UniformToFill
, it also fills up all the available space.
When we were introduced to XAML in the previous chapter, we looked at everything that could be done with it and also how it could be done in code, which is not as easy. While designing our grid is usually done in XAML, there may be times when you wish to add rows or columns dynamically using code. The process involves exactly two lines of code—one for defining the RowDefinition
or ColumnDefinition
object, and the other for actually adding that new definition to our grid. Let's add a new row to our grid (which is named LayoutRoot
) dynamically. In Visual Studio 2010, right-click on MainPage.xaml and click on View Code. Add the following code snippet right after InitializeComponet()
in the MainPage
constructor method:
RowDefinition myNewRow = new RowDefinition(); LayoutRoot.RowDefinitions.Add(myNewRow);
The first line of code defines a new RowDefinition
object and the second line adds the new row definition to the grid's row definitions collection using the Add
method.
Build and run the application, and you should see the new row added to your grid.
To remove a row or column, we use the same scheme. First, we declare a row or column we wish to remove, and then we tell the grid control to remove it from the collection. The following code snippet will remove the last row from a grid:
int lastRowIndex = LayoutRoot.RowDefinitions.Count - 1; RowDefinition rowToDelete = LayoutRoot.RowDefinitions[lastRowIndex]; LayoutRoot.RowDefinitions.Remove(rowToDelete);
The first line of code specifies which row we wish to delete. As we want to delete the last row of the grid, we can count the number of rows in the grid and subtract 1 (remember, collections start with index 0). Once we have the index number, we get the desired RowDefinition
object from the collection, and finally we remove that RowDefinition
object from the collection using the Remove
method.
StackPanel
is one of the easier layout controls to use. The StackPanel
control is used when you wish to position the elements in a horizontal or vertical layout spanning across a single row or column. Using StackPanel
is very straightforward. The following code snippet demonstrates how to use it:
<StackPanel> <Rectangle Fill="#ff80bca3" Height="20" /> <Rectangle Fill="#fff6f7bd" Height="20" /> <Rectangle Fill="#ffe6ac27" Height="20" /> </StackPanel>
The preceding XAML code demonstrates the use of a StackPanel
control with three 20-pixel high rectangles inside of it. It will render as follows:
The StackPanel
control's default orientation is to vertically align its child controls, but by specifying the orientation to horizontal, StackPanel
is capable of arranging its controls horizontally. The following code snippet changes the orientation of StackPanel
:
<StackPanel Orientation="Horizontal" Height="60"> <Rectangle Fill="#ff80bca3" Width="33" /> <Rectangle Fill="#fff6f7bd" Width="33" /> <Rectangle Fill="#ffe6ac27" Width="33" /> </StackPanel>
The preceding code snippet will render as follows:
Nesting layout controls is an important concept that you should always keep in mind. While there is no shame in nesting a StackPanel
control inside other StackPanel
controls, you should always make sure there is no better suited layout control for your desired layout.
Imagine yourself sitting in art class. A blank drawing board is positioned in front of you. As an artist you have the freedom to draw (or position) anything you want anywhere on the drawing board. This is what the Canvas
layout inspires to mimic. The Canvas
layout is the highest performing layout panel in Silverlight, and its purpose is to provide you with a freeform layout environment for your UI needs. Using the Canvas
control will look familiar to anyone, who's been doing HTML. We control how far from the left and how far from the top an object will be placed.
In your Silverlight application, replace the LayoutRoot
node with the following code snippet:
<Canvas x:Name="LayoutRoot" Background="White"> <TextBlock Text="Look ma! I'm at the default position!"/> </Canvas>
Build and run your application, and you should see that the text is positioned at the top-left corner.
Controlling the position of an element inside the canvas is done by using the following three attached properties:
Left
: This is a value, in pixels, that specifies how far an element should be positioned from the left border of the canvas. A positive value moves the element away from the left border (in other words, it moves an element to the right), a negative value moves the element closer to the left border.Top
: This is a value, in pixels, that specifies how far an element should be positioned from the top border of the canvas. A positive value moves the element away from the top border (down) while a negative value moves the element closer to the top border (up).ZIndex
: This controls the stacking order of elements. The bigger an element's ZIndex
value is, the closer that element is to the foreground. In simple words, if two elements are positioned in the same place, the element whose ZIndex
property is higher will be on top of the other element.To demonstrate the use of the Canvas
properties, replace the content of your canvas with the following code snippet:
<Rectangle Height="60" Width="60" Fill="Blue"/> <Rectangle Height="60" Width="60" Fill="Red" Canvas.Left="10" Canvas.Top="20"/>
Build and run your application, and you should get the result, as shown in the following screenshot:
As expected, the red rectangle is 10 pixels to the right-hand side of the left border and 20 pixels down from the top border of the blue rectangle. The red rectangle is on top of the blue one, because it was declared after the blue rectangle. To draw the blue rectangle on top of the red one, change the blue rectangle declaration as follows:
<Rectangle Height="60" Width="60" Fill="Blue" Canvas.ZIndex="2"/>
Build and run your application, and you should see the blue rectangle is now on top of the red one. Because, the blue rectangle's ZIndex
property has a higher value than the red one (which uses the default 0), it is drawn on top of it.
While this topic is out of the scope of this book, it is entirely possible to set an element's Top, Left
, or ZIndex
properties using code as well. When you wish to set one of the Canvas
properties on an element from code behind, you should use the Canvas
static class and then either the SetLeft, SetTop
or SetZIndex
methods. The following code will move an element 20 pixels to the right using code:
Canvas.SetLeft(blueRectangle, 20);
The syntax is quite simple. The first argument we pass is the name of the UI element we wish to set the property to, for example blueRectangle
. The second argument we pass is the value (or amount) we wish to set. In our example, we use the SetLeft
method of the Canvas
static class to set the blueRectangle
element 20
pixels to the right. Wish to set it 20 pixels to the left? No problem! Just set the value of the second argument to -20. Because, the value property is of the double type, we are not limited to whole numbers only.
The Border
control is one of the simplest layout controls, and its job is to add a border to its child element. The Border
control can hold exactly one child element at a time and while this seems limiting, just remember that this one child element can also be another layout panel, such as Grid
or StackPanel
. Using the Border
control usually involves the BorderBrush
and BorderThickness
properties. BorderBrush
dictates the color of the border, which can be one of the color enumerators (Red, Green
, and so on) or the hex value of a color (ARGB—Alpha, Red, Green
, and Blue
). You could also use LinearGradientBrush
for your BorderBrush
value, but that means that you will have to use BorderBrush
as an Element
property. To see an example of this approach, have a look at Silverlight Show's border article located at http://www.silverlightshow.net/items/Using-the-Border-control-in-Silverlight-2-Beta-1-.aspx. The BorderThickness
property dictates how wide the border will be. If you set one value for this property (for example, 4), then all four sides of the border will have the width of 4 pixels, but if you wish to have a different width for each side of the Border
control, you need to supply four values for this property, separated with a space. To demonstrate this, replace the Canvas
element in your Silverlight application with the following code snippet:
<Border BorderBrush="Black" BorderThickness="4 10 4 10" > <Image Source="http://www.packtpub.com/sites/default/files/banners /Learning%20jQuery%201.3.jpg" Stretch="UniformToFill"/> </Border>
Build and run your application, and you should see the result, as shown in the following screenshot:
The left and right borders of the image are 4 pixels wide while the top and bottom are 10 pixels wide.
The ScrollViewer
control has one purpose in life; to scroll content. Imagine you have built a gorgeous dashboard application for your office. On your 23-inch wide screen, the dashboard is shown well, but the boss's secretary's monitor is smaller and uses a lower resolution and, thus, your gorgeous dashboard is cut off and looks weird. To make sure your application will fit all screen sizes, you can use the ScrollViewer
control. With the ScrollViewer
control in hand, you have the full control on both the horizontal and vertical scroll bars. Controlling the scroll bars is done via two properties—HorizontalScrollBarVisibility
and VerticalScrollBarVisibility
. Possible values for these properties are—Auto, Disabled, Hidden
, and Visible
. To demonstrate the ScrollViewer
control, replace the code for Border
with the following code snippet:
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <Image Source="http://www.packtpub.com/sites/default/files/banners /Learning%20jQuery%201.3.jpg" Stretch="UniformToFill" Width="1000" Height="1000"/> </ScrollViewer>
Build and run your application. If your browser's window dimensions are smaller than 1000 pixels, you should get the result, as shown in the following screenshot:
If you play around with the browser's dimensions, you will see that the scroll viewer automatically adjusts its scroll bars as well.
The larger the window size is, the shorter the scroll bars are, and vice versa of course.
Let's assume we wish to hide the vertical scroll bar, no matter what the size of the window is. All we have to do is change the value for the VerticalScrollBarVisibility
property from Auto
to Hidden
.
If you're coming from a WPF background, the ViewBox
control should look familiar to you, as it's been part of WPF since the beginning. If not, don't worry; you'll know all about ViewBox
in a second. The main purpose of the ViewBox
control is to scale its child control's content to the full available space that the ViewBox
control itself is occupying. That means that if the ViewBox
control is occupying the width of 300 pixels and height of 300 pixels, any control we will place inside of it will also scale to occupy the same dimensions. If at any point, we resize the ViewBox
control, the content inside of it will resize as well. To demonstrate the control, replace the code for ScrollViewer
with the following code snippet:
<Viewbox> <TextBlock Text="Look ma! I can stretch!"/> </Viewbox>
Build and run your application. As you play around with the browser's window size, you will notice that the text inside the TextBlock
control is always positioned at the center of the window, and that the text shrinks and grows as you change the size of the window, as shown in the following two screenshots:
Every UI element in Silverlight, be it TextBox
, an image, or even a layout control have the ability to be visible or hidden. We control the visibility of an element using the Visibility
property. Visibility
has two possible values—Visible
(default) or Collapsed
. It is important to remember that if an element's visibility property is set to Collapsed
, that element will not render on the page and, thus, won't take up any space. To read more about visibility, please visit the UIElement.Visibility Property page on MSDN at http://msdn.microsoft.com/en-us/library/system.windows.uielement.visibility%28v=vs.95%29.aspx.
18.117.138.104