Website Links

Tuesday, 9 September 2014

Overview of Layouts for WPF

Layout Containers

Layout containers are what you put your content in. There are 5 main types and each have situations where they are advantageous.

Stack Panel

This is one of the most simple layouts to use, but does not give you much control over how elements are laid out. It allows you to layout items horizontally or vertically by setting the 'Orientation' attribute of the StackPanel. By default, orientation is Vertical. Its children are laid out in the order in which they are specified in the xaml and sizing is handled automatically. Here's an example:

   <StackPanel Orientation="Horizontal">            
      <TextBlock>Name</TextBlock>
      <TextBox Width="200" />
   </StackPanel>


Wrap Panel

The WrapPanel is very similar to the StackPanel, it will lay its children out either vertically or horizontally but when it runs out of space to do so it will continue to lay out its children on the next column or row respectively. It is also a simple layout to use and is advantageous when you want your layout to adjust to resizing and you aren't concerned about the positioning of your elements relative to the frame of the WPF application. Here's an example:

   <WrapPanel Orientation="Horizontal">            
      <Button>Buttons will wrap!</TextBlock>
      <Button>Buttons will wrap!</TextBlock>
      <Button>Buttons will wrap!</TextBlock>
      <Button>Buttons will wrap!</TextBlock>
      <Button>Buttons will wrap!</TextBlock>
      <Button>Buttons will wrap!</TextBlock>
      <Button>Buttons will wrap!</TextBlock>
      <Button>Buttons will wrap!</TextBlock>
      <Button>Buttons will wrap!</TextBlock>
   </WrapPanel>


Grid

This is the layout that is most frequently used throughout our Spelling Bee app, and is quite powerful while still being simple to use. When using a Grid layout you are able to specify the rows and columns, and you are also able to specify the height or width of these. We have found that we most commonly set these to either '*' or 'Auto'. Auto means that the row or column will size itself such that it fits its child, whereas * means that the remaining space will be consumed. If there are multiple rows or columns set to *, then the remaining space will be divided evenly amongst them. The * may also be prefixed by a number which will result in the corresponding column having a width: ([remaining space]/[n + other column * total]) * n, where n is the number prefixing the *. The same applies for rows, here is an example:

   <Grid>
      <Grid.RowDefinitions>
         <RowDefinition Height="Auto" />
         <RowDefinition Height="*" />
         <RowDefinition Height="3*" />
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
         <ColumnDefinition Width="Auto" />
         <ColumnDefinition Width="*" />
      </Grid.ColumnDefinitions>

      <TextBlock Grid.ColumnSpan="2">My Form!</TextBlock>
      <TextBlock Grid.Row="1" Grid.Column="0">Name</TextBlock>
      <TextBox Grid.Row="1" Grid.Column="1" 
         HorizontalAlignment="Stretch" />

      <TextBlock Grid.Row="2" Grid.Column="0">Comment</TextBlock>
      <TextBox Grid.Row="2" Grid.Column="1"
         HorizontalAlignment="Stretch" 
         VerticalAlignment="Stretch" 
         TextWrapping="Wrap" />
   </Grid>

In the above example, the 3rd row will take up 3/4 of the remaining space. Additionally, we specified which row and column elements would be in using Grid.Row and Grid.Column! Finally, another feature of a grid is that we can set a column or row span using Grid.ColumnSpan or Grid.RowSpan respectively. Grids are useful in many situations, but are particularly useful for laying out forms. It is important to note that you can programmatically define new rows and columns which may be useful for laying out data, but there are other elements which may make this easier.

Dock Panel

The DockPanel is one of the layouts we use less frequently, due to our unfamiliarity with it. However, it can be useful, specifically when you want some elements relative to the edges and then an element to take up the remaining central space. For example, many websites such as Facebook have a top bar, this would be set in a DockPanel by listing the top bar element first and setting the attribute DockPanel.Dock on it to "Top". Websites such as Facebook often also have a left sidebar for navigation, in a DockPanel this would be done by setting the attribute DockPanel.Dock to "Left". We could continue this for the ad view, chat bar and news feed, but it is simpler to show you the xaml for it:

   <DockPanel LastChildFill="True">
      <StackPanel Name="topBar" DockPanel.Dock="Top">
         ...
      <StackPanel>

      <StackPanel Name="navSideBar" DockPanel.Dock="Left">
         ...
      <StackPanel> 

      <StackPanel Name="chatBar" DockPanel.Dock="Right">
         ...
      <StackPanel> 

      <StackPanel Name="adBar" DockPanel.Dock="Right">
         ...
      <StackPanel> 

      <StackPanel Name="newsFeed" DockPanel.Dock="Top">
         ...
      <StackPanel> 
   </DockPanel>

The LastChildFill attribute of the DockPanel means that the last child will take up the remaining central space. The above xaml shows a simplified example of how Facebook is laid out, it is important to note that the ordering of child elements is important in a DockPanel whereas it isn't in a Grid.

Canvas

Last but not least, we have the Canvas! When using a Canvas you position children elements using explicit coordinates. The coordinates are specified relative to the sides of the canvas, for example, x coordinates are specified relative to the left and right sides and y coordinates are specified relative to the bottom and top sides. A canvas is typically used for grouping 2D graphic elements together, and it is not considered good practice to use it to layout UI elements. The following is an example:

   <Canvas>
      <Rectangle Canvas.Left="30" Canvas.Top="70" 
         Width="100" Height="100" Canvas.ZIndex="1" />
      <Rectangle Canvas.Right="270" Canvas.Bottom="130"
         Width="200" Height="100" />
   </Canvas>

Another cool feature of the Canvas is that by setting the Canvas.ZIndex you can set the Z-Ordering. By default they are ordered from back to front in the order they are specified in the XAML and this is the way we would do it rather than setting the Canvas.ZIndex but it could be useful for updating the Z-Ordering programmatically.

No comments:

Post a Comment