standard set doesn’t suffice, but now you have a reasonable base to work from.
Although the controls set for WPF is somewhat comparable to that of Windows Forms, you will
note that their properties are quite different to their counterparts. For example, there is no longer a
Text property on many controls, although you will find a Content property instead. The Content
property is used to assign content to the control (hence its name). You can for the most part treat
this as you would the Text property for a Windows Forms control and simply assign some text
to this property to be rendered. However, the Content property can in fact accept any WPF
element, allowing almost limitless ability to customize the layout of a control without necessarily
having to create your own custom control — a very powerful feature for designing complex user
interfaces. You may also note that many controls don’t have properties to accomplish what was
pretty straightforward in Windows Forms, and you may find this somewhat confusing. For example,
there is no Image property on the WPF Button control to assign an image to a button as there is
in Windows Forms. This may initially make you think WPF is very limited in its capabilities, but
you would be mistaken because this is where the Content property comes into its own. Because
the Content property can have any WPF control assigned to it to define the content of its control
you can assign a StackPanel (discussed in the next section) containing both an Image control and
a TextBlock control to achieve the same effect. Though this may initially appear to be more work
than it would be to achieve the same outcome in Windows Forms, it does enable you to easily lay
out the content of the button in whatever form you choose (rather than how the control chooses to
implement the layout), and demonstrates the incredible flexibility of WPF and XAML. The XAML
for the button in Figure 18-3 is as follows:
<Button HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Height="30">
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="/Chapter18Sample;component/Images/save.png" Width="16"
Height="16" />
<TextBlock Margin="5,0,0,0" Text="Save" VerticalAlignment="Center" />
</StackPanel>
</Button.Content>
</Button>
Other notable property name changes from Windows Forms include the IsEnabled property (which
was simply Enabled in Windows Forms) and the Visibility property (which was
Visible in Windows Forms). Like IsEnabled, you will notice that most Boolean
properties are now prefixed with Is (for example, IsTabStop, IsHitTestVisible,
fiGure 18-3
358 .
chaPter 18 WindoWS preSenTATion FoundATion (WpF)
and so on), conforming to a standard naming scheme. The Visibility property, however, is no
longer a Boolean value — instead it is an enumeration that can have the value Visible, Hidden,
or Collapsed.
Keep an eye on the WPF Toolkit at http://wpf.codeplex.com because new
controls for WPF will continue to be developed and hosted there that you may
find useful.
the wPf layout controls
Windows Forms development used absolute placement for controls on its surface (that is, each
control had its x and y coordinates explicitly set), although over time the TableLayoutPanel and
FlowLayoutPanel controls were added, in which you could place controls to provide a more
advanced means of laying out the controls on your form. However, the concepts around positioning
controls in WPF are slightly different than how controls are positioned in Windows Forms. Along
with controls that provide a specific function (for example, buttons, textboxes, and so on), WPF
also has a number of controls that are used specifically for defining the layout of your user interface.
Layout controls are invisible controls that handle the positioning of controls upon their surface. In
WPF there isn’t a default surface for positioning controls as such — the surface you are working
with is determined by the layout controls further up the hierarchy, with a layout control generally
used as the element directly below the root node of each XAML file to define the default layout
method for that XAML file. The most important layout controls in WPF are the Grid, the Canvas,
and the StackPanel, so this section takes a look at each of those. For example, in the default XAML
file created for the MainWindow class provided earlier, the Grid element was the element directly
below the Window root node, and thus would act as the default layout surface for that window.
Of course you could change this to any layout control in order to suit your requirements, and use
additional layout controls within it if necessary to create additional surfaces that change the way
their containing controls are positioned.
The next section looks at how to layout your forms using the designer surface, but look at the
XAML to use these controls first.
In WPF, if you want to place controls in your form using absolute coordinates (similar to the default
in Windows Forms) you would use the Canvas control as a “surface” to place the controls on.
Defining a Canvas control in XAML is very straightforward:
< Canvas >
< /Canvas >
To place a control (for example, a TextBox control) within this surface using given X and Y
coordinates (relative to the location of the top-left corner of the canvas) we need to introduce the
concept of attached properties within XAML. The TextBox control doesn’t actually have properties
to define its location, because its positioning within the layout control it is contained within is
totally dependent on the type of control. So correspondingly, the properties that the TextBox
control requires in order to specify its position within the layout control must come from the layout
Getting started with WPf .
359
control itself (because it will be handling the positioning of the controls within it). This is where
attached properties come in. In a nutshell, attached properties are properties that are assigned a
value on a control, but the property is actually defined on and belongs to another control higher
up in the hierarchy. When using the property, the name of the property is qualified by the name of
the control that the property is actually defined on, followed by a period, and then the name of the
property on that control you are using (for example, Canvas.Left). By setting that value on another
control that is hosted within it (such as your textbox), the Canvas control is actually storing that
value, and will manage that textbox’s position using that value. For example, this is the XAML
required to place the textbox at coordinates 15, 10 using the Left and Top properties defined on the
Canvas control:
<Canvas>
<TextBox Text="Hello" Canvas.Left="15" Canvas.Top="10" />
</Canvas>
While absolute placement is the default for controls in Windows Forms, best practice in WPF is
to actually use the Grid control for laying out controls. The Canvas control should be used only
sparsely and where necessary, because the Grid control is actually far more powerful for defining
form layouts and is a better choice in most scenarios. One of the big benefits of the Grid control
is that its contents can automatically resize when its own size is changed. So you can easily design
a form that automatically sizes to fill all of the area available to it — that is, the size and location
of the controls within it are determined dynamically. The Grid control allows you to divide its
area into regions (cells) into which you can place controls. These cells are created by defining
a set of rows and columns on the grid, and are defined as values on the RowDefinitions and
ColumnDefinitions properties on the grid. The intersections between rows and columns become
the cells that you can place controls within.
To support defining rows and columns, you need to know how to define complex values in XAML.
Up until now you have been assigning simple values to controls, which map to either .NET primitive
data types, the name of an enumeration value, or have a type converter to convert the string
value to its corresponding object. These simple properties had their values applied as attributes
within the control definition element. However, complex values cannot be assigned this way because
they map to objects (which require the value of multiple properties on the object to be assigned),
and must be defined using property element syntax instead. Because the RowDefinitions and
ColumnDefinitions properties of the Grid control are collections, they take complex values that
need to be defined with property element syntax. For example, here is a grid that has two rows and
three columns defined using property element syntax:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="150" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
360 .
chaPter 18 WindoWS preSenTATion FoundATion (WpF)
Note how in order to set the RowDefinitions property using property element syntax you need
to create a child element of the Grid to define it. Qualifying it by adding Grid before the property
name indicates that the property belongs to a control higher in the hierarchy (as with attached
properties), and making the property an element in XAML indicates you are assigning a complex
value to the specified property on the Grid control.
The RowDefinitions property accepts a collection of RowDefinitions so you are instantiating
a number of RowDefinition objects that are then populating that collection. Correspondingly,
the ColumnDefinitions property is being assigned a collection of ColumnDefinition objects.
To demonstrate that ColumnDefinition (like RowDefinition) is actually an object, the Width
property of the ColumnDefinition object has been set on the first two column definitions.
To place a control within a given cell you again make use of attached properties, this time telling the
container grid which column and row it should be placed in:
<CheckBox Grid.Column="0" Grid.Row="1" Content="A check box" IsChecked="True" />
The StackPanel is another important container control for laying out controls. It stacks the controls
contained within it either horizontally or vertically (depending on the value of its Orientation
property). For example, if you had two buttons defined within the same grid cell (without a
StackPanel) the grid would position the second button directly over the first. However, if you put the
buttons within a StackPanel control it would control the position of the two buttons within the cell
and lay them out next to one another.
<StackPanel Orientation="Horizontal">
<Button Content="OK" Height="23" Width="75" />
<Button Content="Cancel" Height="23" Width="75" Margin="10,0,0,0" />
</StackPanel>
the wPf desiGner and xaMl editor
The WPF designer and XAML editor have had a
number of improvements since Visual Studio 2008,
including stability improvements (the Visual Studio
2008 WPF designer was notoriously unstable), and
most notably the designer now supports drag
and drop binding.
The WPF designer is similar in layout to Windows
Form’s designer, but supports a number of unique
features. To take a closer look at some of these,
Figure 18-4 isolates this window so you can see in
more detail the various components.
First you will notice that the window is split into
a visual designer at the top and a code window at
the bottom. If you prefer the other way around
you can simply click the up/down arrows between
fiGure 18-4
The WPf Designer and XaMl editor .
361
the Design and XAML tabs. In Figure 18-4 the second icon on the right side is highlighted to
indicate that the screen is being split horizontally. Selecting the icon to its left instead splits the
screen vertically.
You will probably find that working in split mode is the best option when
working with the WPF designer because you are likely to find yourself directly
modifying the XAML regularly but want the ease of use of the designer for
general tasks.
If you prefer not to work in split screen mode, you can
double-click either the Design or XAML tab. This
makes the relevant tab fill the entire editor window
as shown in Figure 18-5, and you can click the tabs
to switch between each view. To return to split screen
mode you just need to click the Expand Pane icon,
which is the right-most icon on the splitter bar.
In the designer you’ll note the zoom control in the visual
designer portion of the editor space. The zoom control
allows you to easily zoom in or out on the window or
control being edited, which can be extremely handy
when making small fiddly adjustments or to get an
overview of the whole XAML layout. In this case the
screen is zoomed out to 90 percent. There is a mark
where 100 percent is on the zoom scale and the button
at the bottom of the zoom control allows you to easily
size the XAML layout so that it expands (or contracts) to fit the designer surface.
The last thing worth noting is the cookie-crumb tracker that is at the bottom of the visual designer
window, to the right of the Design and XAML tabs. In this case it only has a single Window element,
but you will see that as you add more elements to the window this feature becomes quite useful in
determining and navigating the control hierarchy for the selected control.
working with the xaMl editor
Working with the XAML editor is somewhat similar to working
with the HTML editor in Visual Studio. Numerous IntelliSense