DockPanel
DockPanel is useful for describing the overall layout
of a simple user interface. You can carve up the basic structure of your window
using a DockPanel and then use the other panels to manage the details.
A DockPanel arranges each child element so that it
fills a particular edge of the panel. If multiple children are docked to the
same edge, they simply stack up against that edge in order. You may also
optionally specify that the final child fills any remaining space not occupied
by controls docked to the panel's edges. Although this may sound like a small
feature set, the DockPanel provides a surprisingly flexible way of
laying out elements, particularly as DockPanels can be nested.
Example 2-2 shows
a simple DockPanel-based layout. Five buttons have been added to
illustrate each of the options. Notice that four of them have a DockPanel.Dock
attribute applied. This attached property is defined by DockPanel to
allow elements inside a DockPanel to specify their position.
Example 2-2. Simple DockPanel layout
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Top">Top</Button>
<Button DockPanel.Dock="Bottom">Bottom</Button>
<Button DockPanel.Dock="Left">Left</Button>
<Button DockPanel.Dock="Right">Right</Button>
<Button>Fill</Button>
</DockPanel>
Figure 2-3 shows
how the UI built in Example 2-2
looks onscreen. Notice how the Top and Bottom buttons have filled the entire
top and bottom edges of the window, and yet the Left and Right buttons do not
fill their edgesthe Top and Bottom buttons have taken control of the corners.
This is because Top and Bottom were added to the panel first.
If you swapped these over so the Left and Right buttons came
first in the markup, as shown in Example
2-3, they would fill their whole edges, including the corners, leaving
the Top and Bottom buttons with just the remaining space.
Figure 2-4 shows the results.
Example 2-3. Docking left and right before top and
bottom
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Left">Left</Button>
<Button DockPanel.Dock="Right">Right</Button>
<Button DockPanel.Dock="Top">Top</Button>
<Button DockPanel.Dock="Bottom">Bottom</Button>
<Button>Fill</Button>
</DockPanel>
Elements never overlap in a DockPanel, so each
successive child only gets to use space not already used by the previous
children. You can get the final child to take all of the remaining space by
setting the LastChildFill attribute of the DockPanel to true,
and not specifying a DockPanel.Dock attribute on the last child.
You may be wondering how the DockPanel decides how tall
to make an item docked to the top or bottom. It sets the width to fill the
space available, but how does it pick a height? And how does it choose the
width for items docked to the left or right?
The short answer is the child element gets to decide. When
docking to the top or bottom the DockPanel stretches the item
horizontally to fill the space, but lets it choose its own height. In
Figures 2-3 and 2-4,
the buttons at the top and bottom are the normal height for a button. What is
less obvious is that the buttons docked to the left and right are the default
width for a buttonthis is harder to see because they have been stretched
vertically. However, it becomes clearer if we put a lot more text into one of
the buttons. If the amount of text is wider than the default width for the
button, the button will try to expand in order to make the text fit. We can see
in Figure 2-5 that the DockPanel
is letting the button be exactly as wide as it wants to be.
The general rule with layout in WPF is that elements will tend
to be as large as they need to be, unless you force them to do something else.
This is a slight oversimplification, as we shall see when we start nesting
panels, but it is true most of the time. This behavior is sometimes called
size to content and it's what makes building flexible user-interface
layouts in WPF particularly easy. The way this works is that panels ask their
children how much space they require before deciding how to arrange them. (This
process is described in more detail in the "Custom Layout" section, later in
this chapter.)
We've used buttons in these examples because they make it easy
to see where the elements have been placed and how large they are. Now let's
look at a more realistic example.
Figure 2-6 shows
the user interface for a documentation viewer, similar to the MSDN Library
viewer. This has a number of typical user-interface features. It has a menu and
a toolbar at the top, a search panel at the side, search results and a status
bar at the bottom, and a main area containing a document. The DockPanel
is perfect for constructing this overall layout.
Example 2-4 shows the use of a DockPanel to provide the basic
layout.
Example 2-4. Documentation viewer basic layout
<Window xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="Documentation Viewer"
Width="350" Height="250"
>
<Window.Resources>
...omitted for clarity - full code available from book web site...
</Window.Resources>
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="File"/>
<MenuItem Header="Edit"/>
<MenuItem Header="View"/>
<MenuItem Header="Tools"/>
<MenuItem Header="Window"/>
<MenuItem Header="Help"/>
</Menu>
<ToolBarTray DockPanel.Dock="Top">
<ToolBar>
<ToolBar.Items>
<Button Content="{StaticResource back}" />
<Button Content="{StaticResource forward}" />
<Button Content="{StaticResource home}" />
<ComboBox />
<Button Content="{StaticResource sync}" />
<Button Content="{StaticResource prev}" />
<Button Content="{StaticResource next}" />
</ToolBar.Items>
</ToolBar>
</ToolBarTray>
<StatusBar DockPanel.Dock="Bottom">Ready</StatusBar>
<TextBlock DockPanel.Dock="Left" Background="White">
Search panel goes here
</TextBlock>
<TextBlock DockPanel.Dock="Bottom" Background="White">
Search results panel goes here
</TextBlock>
<TextBlock Background="Gray">
Content panel goes here
</TextBlock>
</DockPanel>
</Window>
So far we've only done the top-level layout, which you can see
in Figure 2-7. We will be
using other layout techniques inside the search, results, and content panels,
so Example 2-4 uses TextBlock
placeholders for these parts of the UI.
 |
Note that the buttons in
the ToolBar all have their Content properties set to StaticResource
references. These are references to the graphics for the buttons. They have not
been shown here because they take up quite a lot of space, and are not relevant
to the basic layout. However, they can be found in the full source code,
available for download from the book's web site. Resource references are
discussed in detail in Chapter
6.
|
|
This example shows how multiple elements can be docked to the
same edge. Both the Menu and the ToolBarTray have been docked
to the top, and they have simply been stacked on top of one another. Likewise,
the StatusBar and search results are docked to the bottom. The content
does not have a DockPanel.Dock property, but we have set the LastChildFill
property on the DockPanel itself to true, so the content
fills the remaining space.
The status bar and search results are both docked to the bottom,
and they seem to appear in the opposite order from the source code. The StatusBar
appears first in the markup, above the search results, but onscreen they have
appeared the other way up. This is a slightly counterintuitive feature of the DockPanel.
Elements are docked to their chosen edge in the order in which they appear.
This means that for elements docked to the bottom, whichever appears first in
the markup will be nearest to the bottom, hence the apparent reverse ordering.
Likewise, if multiple elements are docked to the right, they will be in
right-to-left order in the markup.
The DockPanel has enabled us to create the basic
structure of the user interface, but it is less well suited to arranging the
contents of these elements. For example, the search panel will consist of a set
of controls arranged in a vertical list. We could do this with a DockPanel,
but there is a more convenient panel available: StackPanel.
 |