BUY THIS BOOK
Add to Cart

Print Book $39.95


Add to Cart

Print+PDF $51.94

Add to Cart

PDF $31.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £28.50

What is this?

Looking to Reprint or License this content?


Programming Windows Presentation Foundation
Programming Windows Presentation Foundation By Chris Sells, Ian Griffiths
September 2005
Pages: 447

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Hello, WPF
Windows Presentation Foundation (or WPF, as we will refer to it throughout this book) is a completely new presentation framework, integrating the capabilities of those frameworks that preceded it, including User, GDI, GDI+, and HTML, and heavily influenced by toolkits targeted at the Web, such as Macromedia Flash, and popular Windows applications such as Microsoft Word. This chapter is meant to give you the basics of WPF from scratch and then take you on a whirlwind tour of the things you'll read about in detail in the chapters that follow.
I know that "Avalon" is now officially the "Windows Presentation Foundation," but that's quite a mouthful and "WPF" is something I'm still getting used to, so during this difficult transition, please don't think less of me when I use the term "Avalon."
Example 1-1 is pretty much the smallest WPF application you can write in C#.
Example 1-1. Minimal C# WPF application
// MyApp.cs
using System;
using System.Windows; // the root WPF namespace

namespace MyFirstAvalonApp {
  class MyApp {
    [STAThread]
    static void Main() {
      // the WPF message box
      MessageBox.Show("Hello, Avalon");
    }
  }
}
If you're not familiar with the STAThread attribute, it's a signal to .NET that when COM is initialized on the application's main thread, to make sure it's initialized to be compatible with single-threaded UI work , as required by WPF applications.
Building this application is a matter of firing off the C# compiler from a command shell with the appropriate environment variables, as in Example 1-2.
Example 1-2. Building a WPF application manually
                  C:\1st>csc /target:winexe /out:.\1st.exe
  /r:System.dll
  /r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\WindowsBase 
.dll
  /r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\PresentationCore 
.dll
  /r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\PresentationFramework 
.dll
  MyApp.cs
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
WPF from Scratch
Example 1-1 is pretty much the smallest WPF application you can write in C#.
Example 1-1. Minimal C# WPF application
// MyApp.cs
using System;
using System.Windows; // the root WPF namespace

namespace MyFirstAvalonApp {
  class MyApp {
    [STAThread]
    static void Main() {
      // the WPF message box
      MessageBox.Show("Hello, Avalon");
    }
  }
}
If you're not familiar with the STAThread attribute, it's a signal to .NET that when COM is initialized on the application's main thread, to make sure it's initialized to be compatible with single-threaded UI work , as required by WPF applications.
Building this application is a matter of firing off the C# compiler from a command shell with the appropriate environment variables, as in Example 1-2.
Example 1-2. Building a WPF application manually
                  C:\1st>csc /target:winexe /out:.\1st.exe
  /r:System.dll
  /r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\WindowsBase 
.dll
  /r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\PresentationCore 
.dll
  /r:c:\WINDOWS\Microsoft.NET\Windows\v6.0.4030\PresentationFramework 
.dll
  MyApp.cs

Microsoft (R) Visual C# 2005 Compiler version 8.00.50215.44
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50215
Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.
Here, we're telling the C# compiler that we'd like to create a Windows application (instead of a Console application, which we get by default), putting the result, 1st.exe, into the current folder, bringing in the three main WPF assemblies (WindowsBase, PresentationCore and PresentationFramework), along with the core .NET System assembly, and compiling the MyApp.cs source file.
Running the resulting 1st.exe produces the world's lamest WPF application, as shown in Figure 1-1.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Navigation Applications
If you create a new WPF application using Visual Studio, you may notice that a few icons down from the Avalon Application icon is another project template called Avalon Navigation Application, as shown in Figure 1-6.
Figure 1-6: The Avalon Navigation Application project template in Visual Studio
WPF itself was created as a unified presentation framework, meant to enable building Windows applications with the best features from existing Windows application practice and existing web application practice. One of the nice things that web applications generally provide is a single window showing the user one page of content/functionality at a time, allowing for navigation between the pages. For some applications, including Internet Explorer, the Shell Explorer, Microsoft Money and a bunch of Control Panels, this is thought to be preferable to the more common Windows application practice of showing more than one window at a time.
To enable more of these kinds of applications in Windows, WPF provides the NavigationApplication in Example 1-14 to serve as the base of your custom application class instead of the Application class.
Example 1-14. The C# portion of a navigation application
// MyApp.xaml.cs
using System;
using System.Windows;
using System.Windows.Navigation;

namespace MyNavApp {
  public partial class MyApp : NavigationApplication {}
}
The NavigationApplication itself derives from the Application class and provides additional services such as navigation, history, and tracking the initial page to show when the application first starts, which is specified in the application's XAML file, as in Example 1-15.
Example 1-15. The XAML portion of a navigation application
<!-- MyApp.xaml.cs -->
<NavigationApplication
  x:Class="MyNavApp.MyApp"
  xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
  xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
  
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Content Model
While the different kinds of WPF application styles are interesting, the core of any presentation framework is in the presentation elements themselves. Fundamentally, we have "bits of content and behavior" and "containers of bits of content and behavior." We've already seen both kinds; e.g., a Button is a control, providing content and behavior and a Window is a container. There are two things that may surprise you about content containment in WPF, however.
The first is that you don't need to put a string as the content of a Button; it will take any .NET object. For example, you've already seen a string as a button's content, which looks like Figure 1-9, created with the code in Example 1-19.
Figure 1-9: A button with string content
Example 1-19. A button with string content
<Window ...>
  <Button Width="100" Height="100">Hi</Button>
</Window>
However, you can also use an image, as in Figure 1-10 and implemented in Example 1-20.
Figure 1-10: A button with image content
Example 1-20. A button with image content
<Window ...>
  <Button Width="100" Height="100">
    <Image Source="tom.png" />
  </Button>
</Window>
You can even use an arbitrary control, like a TextBox, as shown in Figure 1-11 and implemented in Example 1-21.
Figure 1-11: A button with control content
Example 1-21. A button with control content
<Window ...>
  <Button Width="100" Height="100">
    <TextBox Width="75">edit me</TextBox>
  </Button>
</Window>
Further, as you'll see in Chapters 2 and 5, you can get fancy and show a collection of nested elements in a
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Layout
Taking another look at Example 1-24 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 are things 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 general-purpose panel controls listed in Table 1-1.
Table 1-1: Main panel types
Panel type
Usage
DockPanel
Allocates an entire edge of the panel area to each child; useful for defining the rough layout of simple applications at a coarse scale.
StackPanel
Lays out children in a vertical or horizontal stack; extremely simple, useful for managing small scale aspects of layout.
Grid
Arranges children within a grid; useful for aligning items without resorting to fixed sizes and positions. The most powerful of the built-in panels.
Canvas
Performs no layout logic—puts children where you tell it to; allows you to take complete control of the layout process.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Controls
While the layout panels provide the container, the controls are the important things you'll be arranging. So far, you've already seen examples of creating instances of controls, setting properties, and handling events. You've also seen the basics of the content model that makes controls in WPF special. However, for the details of event routing, command handling, mouse/keyboard input and an enumeration of the controls in WPF, you'll want to check out Chapter 3. Further, for information about packaging up custom UI and behavior, as well as the techniques discussed in the rest of this chapter and the rest of this book, you'll want to read Chapter 9.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Data Binding
Once we've got a set of controls and a way to lay them out, we still need to fill them with data and keep that data in sync with wherever the data actually lives. (Controls are a great way to show data but a poor place to keep it.)
For example, imagine that we'd like to build an actual WPF application for keeping track of people's nicknames. Something like Figure 1-14 would do the trick.
Figure 1-14: Data binding to a collection of custom types
In Figure 1-14, we've got two TextBox controls, one for the name and one for the nickname; the actual nickname entries in a ListBox in the middle; and a Button to add new entries. The core data of such an application could easily be built with a class, as shown in Example 1-27.
Example 1-27. A custom type with data binding support
               public class Nickname : INotifyPropertyChanged {
  // INotifyPropertyChanged Member
  public event PropertyChangedEventHandler PropertyChanged;
  protected void OnPropertyChanged(string propName) {
    if( PropertyChanged != null ) {
      PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
  }

  string name;
  public string Name {
    get { return name; }
    set {
      name = value;
      OnPropertyChanged("Name"); // notify consumers
    }
  }

  private string nick;
  public string Nick {
    get { return nick; }
    set {
      nick = value;
      OnPropertyChanged("Nick"); // notify consumers
    }
  }

  public Nickname() : this("name", "nick") { }
  public Nickname(string name, string nick) {
    this.name = name;
    this.nick = nick;
  }
}
This class knows nothing about data binding, but it does have two public properties that expose the data, and it implements the standard INotifyPropertyChanged interface to let consumers of this data know when it has changed.
In the same way that we have a standard interface for notifying consumers of objects when they change, we also have a standard way to notify consumers of collections of changes called
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Dependency Properties
While our data-source Nickname object made its data available via standard .NET properties, we need something special to support data binding on the target element. While the TextContent property of the TextBlock element is exposed with a standard property wrapper, for it to integrate with WPF services such as data binding, styling and animation, it also needs to be a dependency property . A dependency property provides several features not present in .NET properties, including the ability to inherit its value from a container element, support externally set defaults, provide for object-independent storage (providing a potentially huge memory savings), and change tracking.
Most of the time, you won't have to worry about dependency properties versus .NET properties, but when you need the details, you can read about them in the Chapter 9.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Resources
Resources are named chunks of data defined separately from code and bundled with your application or component. .NET provides a great deal of support for resources , a bit of which we already used when we referenced tom.png from our XAML button earlier in this chapter. WPF also provides special support for resources scoped to elements defined in the tree.
As an example, let's declare some default instances of our custom Nickname objects in XAML in Example 1-32.
Example 1-32. Declaring objects in XAML
<!-- Window1.xaml -->
<?Mapping XmlNamespace="local" ClrNamespace="DataBindingDemo" ?>
<Window
    x:Class="DataBindingDemo.Window1"
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
    xmlns:local="local"
    Text="Nicknames">
  <Window.Resources>
    <local:Nicknames x:Key="names">
      <local:Nickname Name="Don" Nick="Naked" />
      <local:Nickname Name="Martin" Nick="Gudge" />
      <local:Nickname Name="Tim" Nick="Stinky" />
    </local:Nicknames>
  </Window.Resources>
  <DockPanel DataContext="{StaticResource names}">
    <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
      <TextBlock VerticalAlignment="Center">Name: </TextBlock>
      <TextBox Text="{Binding Path=Name}" />
      <TextBlock VerticalAlignment="Center">Nick: </TextBlock>
      <TextBox Text="{Binding Path=Nick}" />
    </StackPanel>
    ...
  </DockPanel>
</Window>
Notice the Window.Resources, which is property-element syntax to set the Resources property of the Window1 class. Here, we can add as many named objects as we like, with the name coming from the Key attribute and the object coming from the XAML elements (remember that XAML elements are just a mapping to .NET class names). In this example, we're creating a Nicknames collection named names to hold three Nickname objects, each constructed with the default constructor, and then setting each of the Name and Nick properties.
Also notice the use of the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Styles and Control Templates
One of the major uses for Resources is for styles . A style is a set of property/value pairs to be applied to one or more elements. For example, recall the two TextBlock controls from our Nickname sample, each of which was set to the same VerticalAlignment (see Example 1-35).
Example 1-35. Multiple TextBlock controls with the same settings
<!-- Window1.xaml -->
<Window ...>
  <DockPanel ...>
    <StackPanel ...>
      <TextBlock VerticalAlignment="Center">Name: </TextBlock>
      <TextBox Text="{Binding Path=Name}" />
      <TextBlock VerticalAlignment="Center">Nick: </TextBlock>
      <TextBox Text="{Binding Path=Nick}" />
    </StackPanel>
    ...
  </DockPanel>
</Window>
If we wanted to bundle the VerticalAlignment setting into a style, we could do this with a Style element in a Resources block, as shown in Example 1-36.
Example 1-36. An example TextBlock style
<Window ...>
  <Window.Resources>
    <Style x:Key="myStyle" TargetType="{x:Type TextBlock}">
      <Setter Property="VerticalAlignment" Value="Center" />
      <Setter Property="FontWeight" Value="Bold" />
      <Setter Property="FontStyle" Value="Italic" />
    </Style>
  </Window.Resources>
  <DockPanel ...>
    <StackPanel ...>
      <TextBlock Style="{StaticResource myStyle}">Name: </TextBlock>
      <TextBox Text="{Binding Path=Name}" />
      <TextBlock Style="{StaticResource myStyle}">Nick: </TextBlock>
      <TextBox Text="{Binding Path=Nick}" />
    </StackPanel>
    ...
  </DockPanel>
</Window>
The style element is really just a named collection of Setter elements for a specific class (specified with the Type markup extension). The TextBlock myStyle style centers the vertical alignment property and, just for fun, sets the text to bold italic as well. With the style in place, it can be used to set the Style property of any TextBlock that references the style resource. Applying this style as in Example 1-36 yields Figure 1-18.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Graphics
If no element or set of elements provides you with the look you want in your application, you can build it up from the set of graphics primitives that WPF provides, including rectangles, polygons, lines, ellipses, etc. WPF also lets you affect the way it renders graphics in any element, offering facilities that include bordering, rotating, or scaling another shape or control. WPF's support for graphics is engineered to fit right into the content model we're already familiar with, as shown in Example 1-38. from Chapter 7.
Example 1-38. Adding graphics to a Button
<Button LayoutTransform="scale 3 3">
  <StackPanel Orientation="Horizontal">
    <Canvas Width="20" Height="18" VerticalAlignment="Center">
      <Ellipse Canvas.Left="1" Canvas.Top="1" Width="16" Height="16"
        Fill="Yellow" Stroke="Black" />
      <Ellipse Canvas.Left="4.5" Canvas.Top="5" Width="2.5" Height="3"
        Fill="Black" />
      <Ellipse Canvas.Left="11" Canvas.Top="5" Width="2.5" Height="3"
        Fill="Black" />
      <Path Data="M 5,10 A 3,3 0 0 0 13,10" Stroke="Black" />
    </Canvas>
    <TextBlock VerticalAlignment="Center">Click!</TextBlock>
  </StackPanel>
</Button>
Here we've got three ellipses and a path composed inside a canvas, which is hosted inside a stack panel with a text block that, when scaled via the LayoutTransform property on the button, produces Figure 1-21.
Figure 1-21: A scaled button with a collection of graphic primitives
Notice that there's nothing special about the graphic primitives in XAML; they're declared and integrated as content just like any of the other WPF elements we've discussed. The graphics and the transformation are integrated into the same presentation stack as the rest of WPF, which is a bit of a difference for User/GDI programmers of old.
Further, graphics in WPF are not limited to 2-D; Figure 1-22 shows an example of a simple 3-D figure that was defined declaratively just like a 2-D graphic.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Application Deployment
Once you've packed all of the features that WPF supports into your application, you still have the challenge of getting it to your users. As part of its effort to merge the best of Windows and the Web, WPF leverages the ClickOnce application-deployment support built into .NET 2.0 to enable WPF applications to be deployed over the Web.
You can deploy your WPF application to a web server for their enjoyment by right-clicking on the project in the Solution Explorer and choosing the Publish option, which brings up the Publish Wizard, which asks you where and how you would like to publish the output of your project.
If you choose the defaults, you'll have chosen to publish a ClickOnce "local install" application to the local machine's web server. A successful publication will bring up a Visual Studio-generated HTML file for you to test your application, as shown in Figure 1-23.
Figure 1-23: Visual Studio-generated publish.htm
This version of publish.htm uses the default settings, including the name of the company of the author who built this sample (which is only coincidently the name of the company that developed WPF), the project name of the application, and the version number for the initial publication of this application.
Clicking on this link deploys the application. The first step is downloading the files that make up the application, as shown in Figure 1-24.
Figure 1-24: Launching a ClickOnce application
Once the files are downloaded, they're checked for a certificate signature from a known publisher. Since this requires nondefault settings, what you'll see is Figure 1-25.
Figure 1-25: Launching a ClickOnce application from an unknown publisher
The Security Warning dialog is shown when the user hasn't already awarded a publisher the permissions to take certain liberties on their computer or when the publisher isn't known at all. If the user presses the Cancel button at this point, no potentially "evil" code has been downloaded or executed. If the user presses the Install button, the application's files are put into the right place, a Start menu folder is established and the application itself is executed, just like a normal WPF application.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Where Are We?
The Application object forms the initial piece on which to build your WPF applications. The Application definition, along with any Window or Page objects you may have, are most often split between a declarative XAML file for the look and an imperative code file for the behavior. Your applications can be normal, like a standard Windows application, or navigation-based, like the browser. In fact, the latter can be integrated into the browser, and both can be deployed and kept up to date over the Web using ClickOnce.
Building your application is a matter of grouping controls in containers: either single content containers, such as windows or buttons, or multiple content containers that provide layout capabilities, such as the canvas and the grid.
When bringing your controls together, you'll want to populate them with data that's synchronized with the in-memory home of the data, which is what data binding is for, and keep them pretty, which is what styles are for. If you want to declare data or styles in your XAML, you can do so using resources, which are just arbitrary named objects that aren't used to render WPF UI directly.
If no amount of data or style property settings makes you satisfied with the look of your control, you can replace it completely with control templates, which can be made up of other controls or graphics primitives. In addition, you can apply graphic operations—such as rotating, scaling, or animation—to graphic primitives or controls in WPF's integrated way.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Layout
All applications need to present information to users. For this information to be conveyed effectively, it should be arranged onscreen in a clear and logical way. WPF provides a powerful and flexible array of tools for controlling the layout of the user interface.
There is a fine line between giving the developer or designer enough control over the user interface's layout, and leaving them to do all the work. A good layout system should be able to automate common scenarios such as resizing, scaling, and adaptation to localization but should allow manual intervention where necessary.
WPF provides a set of panels : elements that handle layout. Each individual panel type offers a straightforward and easily understood layout mechanism. As with all WPF elements, layout objects can be composed in any number of different ways, so while each individual element type is fairly simple, the flexible way in which they can be combined makes for a very powerful layout system. And you can even create your own layout element types should the built-in ones not meet your needs.
In this chapter, we will look at where each of the basic layout panels fits into a typical UI design. We will also examine some of the text-layout features of WPF.
Layout in WPF is managed by panels. A panel is a special-purpose user-interface element whose job is to arrange the elements it contains. The type of panel you choose determines the style of UI layout within that panel. Table 2-1 describes the main panel types built into WPF. Whichever panel you use, the same basic rule always applies: an element's position is always determined by the containing panel. Some panels also manage the size of their children.
Table 2-1: Main panel types
Panel type
Usage
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Layout Basics
Layout in WPF is managed by panels. A panel is a special-purpose user-interface element whose job is to arrange the elements it contains. The type of panel you choose determines the style of UI layout within that panel. Table 2-1 describes the main panel types built into WPF. Whichever panel you use, the same basic rule always applies: an element's position is always determined by the containing panel. Some panels also manage the size of their children.
Table 2-1: Main panel types
Panel type
Usage
DockPanel
Allocates an entire edge of the panel area to each child; useful for defining the rough layout of simple applications at a coarse scale.
StackPanel
Lays out children in a vertical or horizontal stack; extremely simple, useful for managing small scale aspects of layout.
Grid
Arranges children within a grid; useful for aligning items without resorting to fixed sizes and positions. The most powerful of the built-in panels.
Canvas
Performs no layout logic—puts children where you tell it to; allows you to take complete control of the layout process.
Using a panel is simple: just put the children you need to lay out inside it. Example 2-1 shows several elements inside a
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
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 edges—the Top and Bottom buttons have taken control of the corners. This is because Top and Bottom were added to the panel first.
Figure 2-3: Simple DockPanel layout
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.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
StackPanel
StackPanel is a very simple panel. It simply arranges its children in a row or a column. We've seen it once already in Example 2-1, but that was a somewhat unrealistic example. You will rarely use StackPanel to lay out your whole user interface. It is at its most useful for small-scale layout—you use DockPanel or Grid to define the overall structure of your user interface, and then StackPanel to manage the details.
For example, we used DockPanel in Example 2-4 for the basic layout of our documentation viewer. We will now use StackPanel to arrange the contents of the search panel on the left-hand side. The markup in Example 2-5 replaces the placeholder TextBlock that contained the text "Search panel goes here."
Example 2-5. StackPanel search layout
<StackPanel DockPanel.Dock="Left" Orientation="Vertical"
            Background="#ECE9D8">
    <TextBlock>Look for:</TextBlock>
    <ComboBox />
    <TextBlock>Filtered by:</TextBlock>
    <ComboBox />
    <Button>Search</Button>
    <CheckBox>Search in titles only</CheckBox>
    <CheckBox>Match related words</CheckBox>
    <CheckBox>Search in previous results</CheckBox>
    <CheckBox>Highlight search hits (in topics)</CheckBox>
</StackPanel>
This first attempt is a particularly straightforward layout—we have simply provided a list of the controls. Figure 2-8 shows the results.
Figure 2-8: Documentation viewer with search panel
This is approximately what we require, but there are a few rough edges to the layout. The controls are all rather close to the edge of the panel, and to each other. This makes the user interface feel rather cramped, and it would look better with some breathing space.
This problem can be solved using the Margin property, which is present on all WPF elements. It indicates the amount of space that should be left around the edges of the element when it is laid out. The
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Grid
Consider the document properties dialog shown in Figure 2-11. This is from the Microsoft SDK help viewer application, and we are going to add a similar dialog to our document viewer. Notice how the main area of the form is arranged as two columns. The column on the left contains labels, and the column in the middle contains information.
Figure 2-11: Document properties dialog
Achieving this kind of layout with a StackPanel is difficult, because it is not designed with two-dimensional alignment in mind. We could try to use nesting: Example 2-7 shows a vertical StackPanel with three rows, each with a horizontal StackPanel.
Example 2-7. Ineffective use of StackPanel
<StackPanel Orientation="Vertical">
    <StackPanel Orientation="Horizontal">
        <TextBlock>Protocol:</TextBlock>
        <TextBlock>Unknown Protocol</TextBlock>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBlock>Type:</TextBlock>
        <TextBlock>Not available</TextBlock>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBlock>Connection:</TextBlock>
        <TextBlock>Not encrypted</TextBlock>
    </StackPanel>
</StackPanel>
The result, shown in Figure 2-12, is not what we want at all. Each row has been arranged independently, and there is no consistency.
Figure 2-12: Inappropriate use of StackPanel
The Grid panel solves this problem. Rather than working a single row or a single column at a time, it aligns all elements into a grid that covers the whole area of the panel. This allows consistent positioning from one row to the next. Example 2-8 shows the same elements as Example 2-7, but arranged with a Grid rather than StackPanels.
Example 2-8. Grid Layout
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Canvas
Occasionally, the automatic layout offered by DockPanel, StackPanel, or Grid will not enable the look you require, and it will be necessary to take complete control of the precise positioning of every element. For example, when you want to build an image out of graphical elements the positioning of the elements is dictated by the picture you are creating, not by any set of automated layout rules. For these scenarios, you will want to use the Canvas.
The Canvas is the simplest of the panels. It allows the location of child elements to be specified precisely relative to the edges of the canvas. The Canvas doesn't really do any layout at all—it simply puts things where you tell it to.
If you are accustomed to working with fixed layout systems such as those offered by Visual Basic 6, MFC, and the most basic way of using Windows Forms, then the Canvas will seem familiar and natural. However, it is strongly recommended that you avoid it unless you really need this absolute control. The automatic layout provided by the other panels will make your life very much easier, because they can adapt to changes in text and font. They also make it far simpler to produce resizable user interfaces. Moreover, localization tends to be much easier with resizable user interfaces, because different languages tend to produce strings with substantially different lengths. Don't opt for the Canvas simply because it seems familiar.
When using a Canvas, you must specify the location of each child element. If you don't, all of your elements will end up at the top left-hand corner. Canvas defines four attached properties for setting the position of child elements. Vertical position is set with either the Top or Bottom property, and horizontal position is determined by either the Left or Right property, an approach that is illustrated in Example 2-16.
Example 2-16. Positioning on a Canvas
<Canvas Background="Yellow">
  <TextBlock Canvas.Left="10" Canvas.Top="20">Hello</TextBlock>
  <TextBlock Canvas.Right="10" Canvas.Bottom="20">world!</TextBlock>
</Canvas>
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Viewbox
The Viewbox element automatically scales its content to fill the space available. Viewbox is not strictly speaking a panel—it derives from Decorator. This means that, unlike most panels, it can only have one child. However, its ability to adjust the size of its content in order to adapt to its surroundings make it a useful layout tool.
Figure 2-24 shows a window that doesn't use a Viewbox but probably should. The window's content is a Canvas containing a rather small drawing. The markup is shown in Example 2-17.
Figure 2-24: Canvas without Viewbox
Example 2-17. Canvas without Viewbox
<Window xmlns="http://schemas.microsoft.com/winfx/avalon/2005">

    <Canvas Width="18" Height="18" VerticalAlignment="Center">
        <Ellipse Canvas.Left="1" Canvas.Top="1" Width="16" Height="16"
                 Fill="Yellow" Stroke="Black" />
        <Ellipse Canvas.Left="4.5" Canvas.Top="5" Width="2.5" Height="3"
                             Fill="Black" />
        <Ellipse Canvas.Left="11" Canvas.Top="5" Width="2.5" Height="3"
                             Fill="Black" />
        <Path Data="M 5,10 A 3,3 90 0 0 13,10" Stroke="Black" />
    </Canvas>

</Window>
We can use a Viewbox to resize the content automatically. The Viewbox will expand it to be large enough to fill the space, as shown in Figure 2-25. (If you're wondering why the drawing doesn't touch the edges of the window, it's because the Canvas is slightly larger than the drawing it contains.)
Figure 2-25: Canvas with Viewbox
All we had to do to get this automatic resizing was to wrap the Canvas element in a Viewbox element, as is done in Example 2-18.
Example 2-18. Using Viewbox
<Window xmlns="http://schemas.microsoft.com/winfx/avalon/2005">

    <Viewbox>
        <Canvas Width="20" Height="18" VerticalAlignment="Center">

            ...as before

        </Canvas>
    </Viewbox>

</Window>
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Text Layout
The simplest element for presenting text is TextBlock, which is efficient but has limited functionality. TextFlow offers more advanced typography and layout functionality but with slightly more overhead.
TextBlock is useful for putting short blocks of text into a user interface. If you don't require any special formatting of the text, you can simply wrap your text in a TextBlock:
<TextBlock>Hello, world!</TextBlock>
However, even though TextBlock is the simplest text-handling element, it is capable of a little more than this example shows. For example, it has a set of properties for controlling the font, shown in Table 2-2.
Table 2-2: TextBlock font properties
Property
Usage
FontFamily
Typeface name; e.g., "Times New Roman" or "Lucida Console"
FontSize
Size of the font
FontStretch
Used to indicate condensed or expanded versions of a typeface, where available
FontStyle
Normal, Oblique, or Italic
FontWeight
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Common Layout Properties
All user-interface elements have a standard set of layout properties, inherited from the FrameworkElement base class. We have seen a few of these in passing in the preceding section, but we will now look at them all in a little more detail.
You can set these properties to specify an exact width and height for your element. You should try to avoid using these—in general it is preferable to let elements determine their own size where possible. Allowing elements to size to content makes changing your user interface less effort. It can also simplify localization. However, you will occasionally need to provide a specific size.
If you specify a Width or Height, the layout system will always attempt to honor your choices. Of course if you make an element wider than the screen, WPF can't make the screen any wider, but as long as what you request is possible, it will be done.
These properties allow you to specify upper and lower limits on the size of an element. If you need to constrain your user interface's layout, it is usually better to use these than Width and Height where possible. By specifying upper and lower limits, you can still allow WPF some latitude to automate the layout.
It is possible to mandate limits that simply cannot be fulfilled. For example, if you request a MinWidth of 10000, WPF won't be able to honor that request unless you have some very exotic display hardware. In these cases, your element will be truncated to fit the space available.
These properties control how an element is placed inside a parent when there is more room available than necessary. For example, a vertical StackPanel will be as wide as the widest element, meaning that any narrower elements are given excess space. A DockPanel will provide enough space for an element to fill an edge or all the remaining space. Alignment is for these sorts of scenarios, enabling you to determine what the child element does with the extra space.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
When Content Doesn't Fit
Sometimes, WPF will not be able to honor your requests because you have asked the impossible. Example 2-34 creates a StackPanel with a Height of 100, which contains a Button with a Height of 195.
Example 2-34. Asking the impossible
<StackPanel Height="100" Background="Yellow" Orientation="Horizontal">
    <Button>Foo</Button>
    <Button Height="30">Bar</Button>
    <Button Height="195">Spong</Button>
</StackPanel>
Clearly that last button is too big to fit—it is taller than its containing panel. Figure 2-42 shows how WPF deals with this.
Figure 2-42: Truncation when content is too large
The StackPanel has dealt with the anomaly by truncating the element that was too large. When confronted with contradictory hardcoded sizes like these, most panels take a similar approach and will crop content where it simply cannot fit.
There is some variation in the way that panels handle overflow in situations where sizes are not hardcoded but there is still too much content to fit. Example 2-35 puts the three copies of a TextBlock and its content into a StackPanel, a DockPanel, and a Grid cell.
Example 2-35. Handling overflow
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>

    <StackPanel Height="100" Background="Yellow" Orientation="Horizontal">
        <TextBlock TextWrap="Wrap" FontSize="20">
            This is some text that is too long to fit.
        </TextBlock>
    </StackPanel>

    <DockPanel Grid.Row="1" Height="100" Background="Yellow" LastChildFill="False">
        <TextBlock TextWrap="Wrap" FontSize="20">
            This is some text that is too long to fit.
        </TextBlock>
    </DockPanel>

    <TextBlock Grid.Row="2" TextWrap="Wrap" FontSize="20">
        This is some text that is too long to fit.
    </TextBlock>
</Grid>
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Custom Layout
Content preview·Buy PDF of this chapter|