Layout

Taking another look at Example 1-22 with the TextBlock and the Image as content for the Button, we don't really have enough information to place them inside the area of the button. Should they be stacked left to right or top to bottom? Should one be docked on one edge and one docked to the other? How will things be stretched or arranged if the button resizes? These are questions best answered with a panel.

A panel is a control that knows how to arrange its content. WPF comes with the following general-purpose panel controls:

Canvas

Arranges content by position and size with no automatic rearrangement when the Canvas is resized

DockPanel

Arranges content according to the edge that each piece of content "docks" to, except for the last, which fills the remaining area

Grid

Arranges content in rows and columns as specified by the developer

StackPanel

Arranges content top to bottom or left to right according to the orientation of the panel

UniformGrid

Arranges content in a grid with the same number of rows and columns generated as needed to display the content

WrapPanel

Arranges things in a horizontal row until the next item won't fit, in which case it wraps to the next row

Grid Layout

The most flexible panel by far is the grid, which arranges content elements in rows and columns, including the ability to span multiple rows and/or multiple columns, as shown in Example 1-23.

Example 1-23. A sample usage of the Grid panel

<Window ...>
    <Grid>
     <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition />
      <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition />
      <ColumnDefinition />
      <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Button Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">A</Button>
    <Button Grid.Row="0" Grid.Column="2">C</Button>
    <Button Grid.Row="1" Grid.Column="0" Grid.RowSpan="2">D</Button>
    <Button Grid.Row="1" Grid.Column="1">E</Button>
    <Button Grid.Row="1" Grid.Column="2">F</Button>
    <Button Grid.Row="2" Grid.Column="1">H</Button>
    <Button Grid.Row="2" Grid.Column="2">I</Button>
   </Grid>
</Window>

Example 1-23 used the XAML property element syntax to define a grid with three rows and three columns inside the RowDefinition and ColumnDefinition elements. On each element, we've specified the Grid.Row and Grid.Column properties so that the grid knows which elements go where (the grid can have multiple elements in the same cell). One of the elements spans two rows and one spans two columns, as shown in Figure 1-13.

An example Grid panel in action

Figure 1-13. An example Grid panel in action

Using the grid, we can be explicit about how we want to arrange an image with a text caption (Example 1-24).

Example 1-24. Arranging an image and text in a grid

<Button Width="100" Height="100">
  <Button.Content>
    <Grid>
      <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="Auto" />
      </Grid.RowDefinitions>
      <Image Grid.Row="0" Source="tom.png" />
      <TextBlock
        Grid.Row="1"
        HorizontalAlignment="Center">Tom</TextBlock>
    </Grid>
  </Button.Content>
</Button>

Figure 1-14 shows how the grid arranges the image and text for us.

A grid arranging an image and a text block

Figure 1-14. A grid arranging an image and a text block

Because we're just stacking one element on top of another, we could've used the stack panel, but the grid is so general-purpose that many WPF programmers find themselves using it for most layout configurations.

XAML Attached Property Syntax

You may have noticed that in setting up the Grid.Row and Grid.Panel attributes of the Button elements, we used another dotted syntax, similar to the property element syntax, but this time on the attribute instead of on the element. This is the attached property syntax, and it is used to set a property as associated with the particular element (e.g., a Button), but as defined by another element (e.g., a Grid).

The attached property syntax is used in WPF as an extensibility mechanism. We don't want the Button class to have to know that it's being arranged in a Grid, but we do want to specify Grid-specific attributes on it. If the Button was being hosted in a Canvas, the Grid properties wouldn't make any sense, so building Row and Column properties into the Button class isn't such a great idea. Further, when we define our own custom panel that the WPF team never considered (e.g., HandOfCards), we want to be able to apply the HandOfCards-related attached properties to arbitrary elements it contains.

This kind of extensibility is what the attached property syntax was designed for and it is common when arranging content on a panel.

For the nitty-gritty of layout, including the other panels that I didn't show, you'll want to read Chapter 3.

Get Programming WPF, 2nd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.