Skip Navigation LinksHome > View Post

Reason 6. Layout

Earlier I introduced a series of posts entitled 10 reasons to consider WPF for your next desktop application. If you haven't read the intro yet, better head on back and read it first.

It's a good time to make it clear that the 10 reasons are being presented in no particular order, otherwise this post would certainly have been further up the list.

I'm a huge fan of the new way in which layout works in WPF even though it's probably the biggest part of the learning curve I've named "The WTF of WPF". In fact, when discussing layout in WPF I coined my first law of WPF (we met my zeroth law in Reason 2. Databinding):

"WPF abhors a vacuum (by default)"

What do I mean by this? Well, it's all down to the fact that all elements in WPF having a default HorizontalAlignment and VerticalAlignment of 'Stretch'. This means that an element will expand to fit any space it is given by it's container. Here's some proof for you:

<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Button>Click me</Button>
</Page>

Big Button

Why not view the xaml above to see what I mean?

You're probably wondering how this is possible? How does the control know how big it can be? This is all thanks to the wonders of the Measure and Arrange dance. I don't intend to explain this in full here, suffice to say it makes the amazing layout features of WPF happen.

You're probably even more curious as to why I think this 'big button' is a good thing? Admittedly, this takes a bit of getting used to but once you do, it provides an extremely useful way of approaching layout. Hopefully we'll demonstrate this shortly.

Now seems like a good time to point out that WPF controls aren't, deep in their heart of hearts, all bloaters. In fact, all controls have a desired size. That is, a size they would like to be if their container can't give them a definitive width or height to fill.

This can be demonstrated with a simple StackPanel.

Vertical StackPanel



<StackPanel Orientation="Vertical">
    <Button>Button1</Button>
    <Button>Button2</Button>
</StackPanel>

This vertically oriented StackPanel (shown above) has expanded to fill the space provided by the Page. As you've probably noticed the same doesn't happen for its children: Button1 and Button2.

Here, when the measure and arrange dance takes place the StackPanel passes some information to its children about how much space they have to play with. In this case, they have the whole width of the Page to play with, but what about height?

Well, a StackPanel can just keep on stacking, and stacking which means, in the case of a vertically oriented stackpanel like this, they have infinite height to play with. And given a control can't render itself over infinite pixels (a sure performance killer!) they render at their desired size.

Of course, all of this can be overriden by specifying an actual Width and Height for your controls but I rarely do this and recommend you try to avoid it too.

This is demonstrated even more clearly with a WrapPanel.

WrapPanel with lots of buttons

A WrapPanel couldn't allow its child controls to expand to fit the width of the container, or it would look exacly like a StackPanel! Therefore, the controls are allowed to size themselves (both horizontally and vertically) and the WrapPanel takes care of making sure the wrapping magic happens and specifies where each control should be placed.

I've probably failed to excite you about these specific features yet, but if we look at a slightly more involved example maybe we can win you over.

One I prepared earlier

If you've been following this series you'll have seen the slow evolution of our (very simple) example application, which looks something like this when stripped of its 'style':

New Basic Form

You may have noticed that I've added two extra fields to the form called "Summary" and "Author's Notes". As you can imagine, these tend to be somewhat lengthy fields so we really want to make the most of our screen real estate. And look what happens when we expand the form.

New Basic Form with expandoness!

The Grid IS the Daddy

This was achieved using a grid inside the GroupBox to layout our textboxes and labels.

The concept is simple: Get the rows and columns right and let the controls expand to fill the cells.

Here's the row and column definitions for the grid in question:

<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="*" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="*" />
    <RowDefinition Height="Auto" />
</Grid.RowDefinitions>

Which looks like this (with some assistance from ShowGridLines="True").

New Basic Form with expandoness!

When sizing a Grid's Rows and Columns you have three choices: Fixed, '*' or Auto:

Fixed

A fixed definition does exactly what it says on the tin - specifies a number of pixels (or device independent units). Again, I tend to avoid using this and would encourage you to do the same.

'*'

A '*' basically takes a proportion of the remaining space when shared with other '*'s. Make sense? Here's some quick examples:

<Grid.RowDefinitions>
    <RowDefinition Height="*" />
    <RowDefinition Height="*" />
<Grid.RowDefinitions>

In this example, each Row would have 50% of the space available to the Grid.

<Grid.RowDefinitions>
    <RowDefinition Height="450" />
    <RowDefinition Height="*" />
<Grid.RowDefinitions>

In this example, the first row will take up 450 pixels and the second Row will expand to take up any remaining space.

<Grid.RowDefinitions>
    <RowDefinition Height="2*" />
    <RowDefinition Height="450" />
    <RowDefinition Height="*" />
<Grid.RowDefinitions>

In this final example, the second row will measure 450 pixels high and the first and third row will soak up the remaining space with the first row being twice as tall as the third.

Easy peasy.

Auto

So far you could have achieved all this in WinForms with a combination using one of the out-of-the-box layout panels and some appropriate anchoring. However, WinForms has no answer to the Auto option in WPF's Grid which 'sizes to content', i.e. matching the size of the largest child in this row or column.

This is powerful for a number of reasons:

  1. It means I don't have to worry about getting the size of the 'column' for labels just right. WPF takes care of it.
  2. My layout will expand when the user has customised windows and implemented a much larger font as shown in the example below (as shown below)
  3. My layout will stretch to fit if I translate my form into another language - this was always a problem with Windows Forms apps.

Font nonsense

Why not try the Reason 6 ClickOnce Sample.

Tags: WPF

 
Josh Post By Josh Twist
1:05 AM
06 Nov 2007

» Next Post: How binding to the Current Item works
« Previous Post: Using Binding to position a Collection of elements

Comments are closed for this post.

Posted by Scott @ 18 Dec 2007 11:54 AM
Are there large differences between WPF's layouts and Java's layout managers? They seem very similar, yet your post comes across as touting this as a never-before-seen feature.

Posted by Josh @ 19 Dec 2007 1:57 AM
Hi Scott, no idea. Never seen Java's layout manager. Indeed, windows forms has some 'layout controls' to help achieve something close to what I've talked about here but it isn't the same. Nonetheless Java may have something to offer in this space but my comparisons are purely against other and previous offerings from the Microsoft camp.

Posted by Scott @ 31 Dec 2007 10:21 AM
Ah okay. Take a look: http://java.sun.com/javase/6/docs/api/java/awt/LayoutManager.html

I definitely agree with you about MS never doing something like this before. It's a step in the right direction. Now I just have to convince my company to move from VB6 to WPF! :)

© 2005 - 2014 Josh Twist - All Rights Reserved.