WPF Control Layout

One of the most important decisions when designing a WPF application is how to lay out the application’s controls. Controls need to be put in the application in a way that makes sense and makes the application easy to use. Control placement is facilitated through a number of layout elements, such as Grid and StackPanel. Using these, developers can easily lay out controls to achieve the desired look. WPF offers several layout elements designed for various purposes, and in this article, we’ll take a look at each of them.

Note that the examples in this article are all created as part of browser-hosted applications for the purpose of taking simple screenshots. Furthermore, the dimensions of the pages may be modified in order to make the result small and visually appealing, or to show off a particular feature (e.g. wrapping with the WrapPanel).

Canvas

We’ll start by looking at the Canvas control. This control is a bit unique in that it doesn’t arrange its children controls in any way. Rather, each child can be positioned individually using an absolute positioning system. So, in one light, Canvas can be seen as primitive, and in another light, it can be seen as flexible, since you’re not confined to a particular layout scheme when using it. The Canvas control, then, can be used to create some fancy applications.

Here’s an example of an interesting layout that can be created with Canvas:



You can arrange controls by specifying their Canvas.Top, Canvas.Left, Canvas.Bottom, and Canvas.Right attributes, which map to positions on the canvas. Canvas.Top specifies the distance from the top of the Canvas, Canvas.Left specifies the distance from the left side of the Canvas, Canvas.Bottom specifies the distance from the bottom of the Canvas, and Canvas.Right specifies the distance from the right side of the canvas. So, a control with a Left value of 10 and a Top value of 20 would have its top left corner 10 pixels from the left side of the Canvas and 20 pixels from the top of the canvas. The above layout, then, can be created like this:


<Canvas>

 <Button Canvas.Left="10" Canvas.Top="10"

 Content="Button 1" />

 <Button Canvas.Left="60" Canvas.Top="60"

 Content="Button 2" />

 <Button Canvas.Left="110" Canvas.Top="110"

 Content="Button 3" />

 <Button Canvas.Left="60" Canvas.Top="160"

 Content="Button 4" />

 <Button Canvas.Left="10" Canvas.Top="210"

 Content="Button 5" />

</Canvas>


Above, Canvas.Left and Canvas.Top are specified. However, let’s use Canvas.Right and Canvas.Bottom to create a mirror image:



<Canvas>

 <Button Canvas.Right="10" Canvas.Bottom="210"

 Content="Button 1" />

 <Button Canvas.Right="60" Canvas.Bottom="160"

 Content="Button 2" />

 <Button Canvas.Right="110" Canvas.Bottom="110"

 Content="Button 3" />

 <Button Canvas.Right="60" Canvas.Bottom="60"

 Content="Button 4" />

 <Button Canvas.Right="10" Canvas.Bottom="10"

 Content="Button 5" />

</Canvas>

{mospagebreak title=StackPanel}

Perhaps the simplest layout involves placing controls in a vertical or horizontal line. This can also be seen as “stacking” controls—putting one on top of another, or beside another as it’s added to the page. This type of layout is possible through a StackPanel. Controls placed within a StackPanel tag will display stacked in the order that you create them. The StackPanel features an Orientation property that can be set to either Vertical or Horizontal. A vertical orientation can produce something like this:



And a horizontal orientation can produce something like this:



The two examples above use almost the exact same code. The only difference is that the Orientation properties are set to different values.

The first example can be reproduced with the following XAML:


<StackPanel Orientation="Vertical">

 <Button Content="Button 1" />

 <Button Content="Button 2" />

 <Button Content="Button 3" />

 <Button Content="Button 4" />

</StackPanel>


Above, we explicitly set the Orientation property. However, when stacking things vertically, this actually isn’t necessary, since the default value is Vertical. We could have achieved the same results by leaving it out altogether:


<StackPanel>

 

</StackPanel>


The second example can be reproduced simply by changing the value of Orientation:


<StackPanel Orientation="Horizontal">

 <Button Content="Button 1" />

 <Button Content="Button 2" />

 <Button Content="Button 3" />

 <Button Content="Button 4" />

</StackPanel>


It’s also possible to put one StackPanel within another, if necessary. Using this approach, something like this could be achieved:



<StackPanel Orientation="Vertical">

 <Button Content="Button 1" />

 <StackPanel Orientation="Horizontal">

 <Button Content="Button 2" />

 <Button Content="Button 3" />

 <Button Content="Button 4" />

 </StackPanel>

</StackPanel>


However, for more advanced layouts requiring distinct rows and columns, the Grid layout element, which will be covered later, is a much better choice.

The default value for HorizontalAlignment and VeritcalAlignment for controls within a StackPanel is Stretch. This can produce some odd results (such as the example with a horizontal orientation). So, in some situations, it may be best to change this to a more natural look:



<StackPanel Orientation="Horizontal">

 <Button Content="Button 1" VerticalAlignment="Center" />

 <Button Content="Button 2" VerticalAlignment="Center" />

 <Button Content="Button 3" VerticalAlignment="Center" />

 <Button Content="Button 4" VerticalAlignment="Center" />

</StackPanel>


A better way to do this would be to create a style within the StackPanel:
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</StackPanel.Resources>

{mospagebreak title=WrapPanel}

WrapPanel is similar to StackPanel. The difference is that, whereas StackPanel puts each new control in a new row or column, WrapPanel keeps adding controls in the desired direction until there is no more space. Then, it puts the next control into a new row or column, “wrapping” it similar to the way text may be wrapped. Also, like StackPanel, WrapPanel has an Orientation property, except it defaults to Horizontal.

A WrapPanel confined to a width of 300 pixels will render eight buttons like this:



<WrapPanel>

 <Button Content="Button 1" />

 <Button Content="Button 2" />

 <Button Content="Button 3" />

 <Button Content="Button 4" />

 <Button Content="Button 5" />

 <Button Content="Button 6" />

 <Button Content="Button 7" />

 <Button Content="Button 8" />

</WrapPanel>


The above WrapPanel has a horizontal orientation (the default, remember), but switching the Orientation property to Vertical and confining the WrapPanel to a height of 100 pixels would render the same eight buttons differently:



<WrapPanel Orientation="Vertical">

 

</WrapPanel>

{mospagebreak title=DockPanel}

Next, we’ll look at the DockPanel control. DockPanel is a bit odd in that its child controls are arranged relative to one another. That is, controls may be placed to the top, bottom, left, and right of other controls. This relative arrangement is governed by the DockPanel.Dock attribute. For example, if one control has its DockPanel.Dock attribute set to Top in the XAML, then it will be positioned above the control that follows it. Two buttons in this situation would look something like this:



And the corresponding XAML would look like this:


<DockPanel>

 <Button DockPanel.Dock="Top" Content="Button 1" />

 <Button Content="Button 2" />

</DockPanel>


Note that a DockPanel will use up all of the available space. In the above picture, the DockPanel is confined to a Page with a width of 200 pixels and a height of 100 pixels. Both buttons are stretched horizontally to take up the entire available width, and the second button is stretched vertically to take up the rest of the available height. You can, however, get rid of the default stretch behavior in the same way as with StackPanel:


<DockPanel>

 <Button DockPanel.Dock="Top" Content="Button 1"

 HorizontalAlignment="Center" />

 <Button Content="Button 2" HorizontalAlignment="Center"

 VerticalAlignment="Center" />

</DockPanel>


More controls can be added to the application to produce something more complex:



<DockPanel>

 <Button DockPanel.Dock="Top" Content="Button 1" />

 <Button DockPanel.Dock="Left" Content="Button 2" />

 <Button DockPanel.Dock="Bottom" Content="Button 3" />

 <Button Content="Button 4" />

</DockPanel>

{mospagebreak title=Grid}

The final layout control we’ll be looking at is the Grid control. The Grid control behaves exactly as its name implies: it consists of a grid with rows and columns defined by the user, and each control occupies a cell within the grid. Each control has a row and a column, and a control can also have a row span and a column span in order to occupy multiple cells.

Below, four buttons are arranged in a Grid with two rows and two columns:



There are two steps involved in creating a Grid of controls. First, the rows and columns must be defined. Then, the controls are created. Controls can be placed in an individual cell using Grid.Row and Grid.Column attributes. The controls and layout in the above picture can be produced with the following XAML, which shows both steps necessary to create a functional Grid:


<Grid>

 <Grid.RowDefinitions>

 <RowDefinition />

 <RowDefinition />

 </Grid.RowDefinitions>

 <Grid.ColumnDefinitions>

 <ColumnDefinition />

 <ColumnDefinition />

 </Grid.ColumnDefinitions>

 

 <Button Grid.Row="0" Grid.Column="0" Content="Button 1" />

 <Button Grid.Row="0" Grid.Column="1" Content="Button 2" />

 <Button Grid.Row="1" Grid.Column="0" Content="Button 3" />

 <Button Grid.Row="1" Grid.Column="1" Content="Button 4" />

</Grid>


Note that in the above example, the Grid is stretched (by default) to fit the entire Page, and the controls are stretched to fit the entire cell of the Grid. This can, of course, be changed with the HorizontalAlignment and VerticalAlignment process, but I won’t bother spending time on that, since you should know the process by now. Also, the first row is the zero row, and the first column is the zero column.

Notice how the RowDefinition and ColumnDefinition attributes have no attributes. Ordinarly, however, the rows are given a height, and the columns are given a width. Below, we make rows 30 pixels high and columns 100 pixels wide:



<Grid>

 <Grid.RowDefinitions>

 <RowDefinition Height="30" />

 <RowDefinition />

 </Grid.RowDefinitions>

 <Grid.ColumnDefinitions>

 <ColumnDefinition Width="100" />

 <ColumnDefinition Width="100" />

 </Grid.ColumnDefinitions>

 

 <Button Grid.Row="0" Grid.Column="0" Content="Button 1" />

 <Button Grid.Row="0" Grid.Column="1" Content="Button 2" />

 <Button Grid.Row="1" Grid.Column="0" Content="Button 3" />

 <Button Grid.Row="1" Grid.Column="1" Content="Button 4" />

</Grid>


Instead of providing a specific measurement, however, a control can be made to take up all of the available space (or divide it up among other controls) with this setting:



<Grid>

 <Grid.RowDefinitions>

 <RowDefinition Height="30" />

 <RowDefinition Height="30" />

 </Grid.RowDefinitions>

 <Grid.ColumnDefinitions>

 <ColumnDefinition Width="50" />

 <ColumnDefinition Width="*" />

 </Grid.ColumnDefinitions>

 

 <Button Grid.Row="0" Grid.Column="0" Content="Button 1" />

 <Button Grid.Row="0" Grid.Column="1" Content="Button 2" />

 <Button Grid.Row="1" Grid.Column="0" Content="Button 3" />

 <Button Grid.Row="1" Grid.Column="1" Content="Button 4" />

</Grid>


As mentioned earlier, a control can also span multiple rows:



<Grid>

 <Grid.RowDefinitions>

 <RowDefinition Height="30" />

 <RowDefinition Height="30" />

 </Grid.RowDefinitions>

 <Grid.ColumnDefinitions>

 <ColumnDefinition Width="50" />

 <ColumnDefinition Width="50" />

 </Grid.ColumnDefinitions>

 

 <Button Grid.Row="0" Grid.RowSpan="2" Grid.Column="0"

 Content="Button 1" />

 <Button Grid.Row="0" Grid.Column="1" Content="Button 2" />

 <Button Grid.Row="1" Grid.Column="1" Content="Button 3" />

</Grid>


A control can also span multiple columns (the row and column definitions are the same for this example):



<Grid>

 

 

 <Button Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"

 Content="Button 1" />

 <Button Grid.Row="1" Grid.Column="0" Content="Button 2" />

 <Button Grid.Row="1" Grid.Column="1" Content="Button 3" />

</Grid>


Now you’ve learned about five layout elements in WPF: Canvas, StackPanel, WrapPanel, DockPanel, and Grid. Using one of these five elements, or a combination of several of them, you can achieve some complex control arrangements that will fit any application. The only thing left is to decide what elements to apply, but I’ll leave that up to you.

2 thoughts on “WPF Control Layout

  1. Hello, all,

    As was said in the article, the layout of controls plays a very large role in making graphical applications intuitive. In this article, we take a look at some of the ways to lay out controls.

    Feel free to post any questions or comments.

[gp-comments width="770" linklove="off" ]