Windows Presentation Framework, or WPF, is the graphics subsystem for .NET Framework 3.0. Currently supported on Windows XP SP2, Windows Server 2003, Vista, and Windows Server 2008 operating systems, WPF is designed to provide both a layered architecture approach to the graphics aspects of Windows applications as well as a programming approach for graphics. WPF is designed to replace WinForms, although the two can be used simultaneously. With the introduction of .NET 3.5, though, WPF is preferable to WinForms due to its better integration with Visual Studio 2008 and the Expression suite of tools.
In the .NET 3.0 architecture, the hardware interacts with the operating system, which itself sites below the .NET 3.0 Framework consisting of Windows Communication Foundation (WCF) for intercommunications between applications and operating system, WPF to handle the graphics requirements, CardSpace for handling identities, and Windows Workflow Foundation for managing and executing workflows. Also in this layer are the CLR, Base Class Libraries, WinForms, ASP.NET, and ADO.NET components. Windows .NET 3.0 applications sit on top of the .NET 3.0 layer and communicate through the layer to the operating system and the hardware.
WPF has been designed to allow an application to be deployed on either the desktop (a traditional Windows application, in other words) or through a Web browser. Microsoft Silverlight is a Web-based subset of WPF, designed to provide Flash-like capabilities to Web browsers and mobile devices. As part of the WPF programming tool set are a set of controls that make designing and deploying a solid user interface easier, without extensive coding. WPF also includes a set of drawing (2D and 3D) tools, vector and raster graphics components, as well as tools for animation, video and audio.
This article looks at the architecture of the Windows Presentation Framework, and then examines several aspects of the UI-design toolkit and how they can be used in typical applications. While it is impossible to be complete because of the breadth of Windows Presentation Framework, you will see the way in which WPF can be used in Windows applications.
Windows Presentation Framework provides a set of native code components as well as integrating with managed code components of Windows and .NET. From a developer's point of view, though, the public API is the only managed code to be concerned with. Most of the managed code that composes Windows Presentation Framework is contained internally.
When run, a WPF application launches two threads, a background thread for rendering and repainting, and a foreground thread for managing the UI. The background rendering and repainting thread handles these tasks automatically, with no intervention by the user or special coding required by the developer. The UI foreground thread involves the Dispatcher, which manages the queue of UI operations involved in an application. The Dispatcher organizes the pending operations by priority and can invoke handlers for each operation as needed. Once an object has been managed by the Dispatcher, it can be passed to the background task for rendering or repainting as needed.
Apart for very simple applications that can be created, compiled, and managed from the command line, almost all WPF application development will take place in Visual Studio (or similar tool). All WPF applications use the System.Windows namespace to create an instance of the Applications class. The Applications class contains all the "standard" application methods for launching and managing an application and the basic properties for the application.
In most WPF applications, there are two distinct chunks of code, one dealing with the appearance of the application, and the second with the functionality. Typically, the latter is taken care of by event handlers and programming logic. The appearance of the application can be coded inside the application, or, for larger and more complex applications, handled by a declarative languages called XAML, which we'll look at shortly.
In Visual Studio, a WPF application can be created using the WPF Application option in New Projects. A simple WPF application code looks like this:
using System;
using System.Windows;
namespace WpfApp1
{
class MyApp : Application
{
[STAThread]
static void Main(string[] args)
{
MyApp app = new MyApp();
app.Startup += app.AppStartup;
app.Run();
}
void AppStartup(object sender, StartupEventArgs e)
{
Window window = new myWindow();
window.Show();
}
}
}
which calls another file containing the myWindow class definition:
using System;
using System.Windows;
using System.Windows.Controls;
namespace WpfApp1
{
class myWindow : Window
{
public myWindow() {
this.Title = "A WPF Window";
Button button = new Button();
button.Content = "Click here!";
button.Width = 100;
button.Height = 25;
button.Click += button_Clicked;
this.Content = button;
}
void button_Clicked(object sender, RoutedEventArgs e)
{
MessageBox.Show("Thank you", "Clicked");
}
}
}
The new application, app, is created as an instance of myApp derived from the Application base class in Main, followed by the creation of a handler for the StartUp event, which is followed by a call to the Run method to launch the application. The myWindow class allows custom variations of the Windows class to be quickly created, instead of having to instantiate a new instance every time. In the myWindow instance created here, properties for the Content, Width and Height are set. The Click event is handled by an event handler that in this case simply pops up a message box. When run the code displays a dialog with the title "A WPF Window" with a single button inside. When the button is clicked, a message dialog appears:
In the code above, the appearance of the window is handled inside the application, dictating the dimensions and text involved in the windows and button. While this is the more traditional coding approach, WPF allows for the appearance of the interfaces to be moved outside the application code, allowing it to be employed with drag-and-drop actions and better integrated into the environment chosen by the user. To do this, WPF provides a declarative language called XAML.
XAML is a language based on XML specifically for creating .NET objects. Because XAML can be written by non-developers (although with a bit of coding experience), or through the use of other tools such as the Expression suite, it allows the appearance and "look and feel" of applications and application objects to be created separate from the application code itself. Another advantage is that XAML can handle not just WPF objects, but also other objects.
One of the limitations of using XAML files is finding an editor that alleviates the hassle of hand-editing. Visual Studio 2008 includes extensions for graphic design of XAML files, and the .NET Framework 3.0 extension does much the same for Visual Studio 2005. In those environments, a Design window acts like the older Windows Form Designer. Also available from the Windows SDK is the XAMLPad tool, which is a fairly simple visual editor.
Here's some simple XAML code for creating a Window with a button:
<Window
x:Class="WpfApp2.myWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="An XAML Dialog">
<Button
x:Name="myButton"
Width="100"
Height="25"
Click="button_Clicked"> Click here!
</Button>
</Window>
In this XAML code, the root Window element declares a portion of a class called myWPFApp. The two xmlns declarations retrieve the namespaces for XAML (the first for WPF and the second for XAML). Then, a button is defined with specific properties.
As you can see in the XAML code, there's a strong resemblance to the C# code shown earlier. If you think of XAML as providing a direct mapping from XML to .NET, the coding becomes more clear. In essence, each XAML element maps to a .NET class. XAML attributes are .NET properties or, in some cases, events (like a button click event). What XAML does not have is the details of the operations performed in events, for example. For this, a "code behind" file is used (so called because it implements the behavior behind the XAML). Code-behind files, by convention, are .xaml.ca files, and contain the operations for the XAML file, as the following code-behind file for the above XAML shows:
using System;
using System.Windows;
using System.Windows.Controls;
namespace WpfApp2
{
public partial class myWindow : Window
{
public myWindow() {
InitializeComponent();
}
void button_Clicked(object sender, RoutedEventArgs e)
{
MessageBox.Show("You Clicked");
}
}
}
The partial keyword for the myWindow class tells the compiler that an XAML-generated class is associated with this class, together making the complete class. In this code, myWindow calls the InitializeComponent method to handle the button click event. In fact, even more of the code could be moved into the XAML file, leaving only a skeleton in the code-behind file to launch the application. You could, for example, define the class in the XAML file and leave the xaml.cs file strictly for the event handler. Balancing the code between XAML and code-behind files is left for each developer to decide, some preferring to move as much as possible to the XAML file, while others put only the interface definitions in the XAML file.
As mentioned at the start, WPF applications can be standalone (like the two examples above) or can be Web browser based. XAML Browser Applications, or XBAPs as they are called, are created in the same way as stand-alone applications. An obvious advantage of XBAPS is that browsers use a single window, refreshed as needed, instead of providing multiple dialogs. For this reason, XBAPS use pages as a navigation unit (and in Visual Studio instead of filenames, page numbers are used).
An XBAP application is created in Visual Studio by selecting the WPF Browser Application option from New Project. The Page1.xaml file will look like this:
<Page
x:Class="XbapApp1.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Page1">
<TextBlock FontSize="18">
This is an XBAP page.
</TextBlock>
</Page>
After the usual schema definitions, the title of the page is set to "Page1". Then, a text block is define with font size 18, with a simple text string. The Page1.xaml.cs file behind this XBAF page looks like this:
using System;
using System.Windows;
using System.Windows.Controls;
namespace XbapApp1
{
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
}
}
}
When executed, this program will launch the default browser with the text string displayed.
Since navigation is by hyperlink and page-by-page, it's useful to see how to embed links. This is done quite easily by modifying the XAML code like this:
<Page
x:Class="XbapApp1.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Page1">
<TextBlock FontSize="18">
This is an XBAP page.
Click <Hyperlink NavigateUri="Page2.xaml"> here </Hyperlink> to go to Page 2.
</TextBlock>
</Page>
When executed, the link will appear in the browser:
When clicked, the link will lead to the Page2.xaml file (which, if it doesn't exist, will cause an error). To add a new page for Page2.xaml, use Solution Explorer and add a new "Page (WPF)". For example, this code will add new text:
<Page x:Class="XbapApp1.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Page2">
<TextBlock FontSize="18">
This is the second XBAP page.
Click <Hyperlink NavigateUri="Page1.xaml"> here </Hyperlink> to return to Page 1.
</TextBlock>
</Page>
When executed, clicking on the link on the first page will launch the second page in the browser window. The link there can then return to the first page. Obviously, this is a pretty easy way to build up a set of navigation pages with links. As you would expect, you can use any standard HTML tags to format the page, as well as embed different objects such as graphics. WPF lets you use images or any other object on the page as a button, for example, but only a single content piece can be used for that button (so you can't have image and text, for example) unless you use more advanced WPF layout tools like panels.
A panel is a control that allows for management of its contents, which can be multiple items. There are six generic panels supplied with WPF. The Canvas panel arranges content by position and size, with no automatic rearrangement of the contents if the panel is resized. The DockPanel panel arranges contents according to the panel edge each component is docked to, with the last component filling the remaining area. The Grid panel arranges content in rows and columns. The StackPanel panel arranges content from top to bottom or left to right. The UniformGrid panel arranges content in a symmetrical grid. Finally, the WrapPanel panel arranges content in a horizontal row, wrapping when a row is full.
The most widely used panel is the Grid panel, allowing content to be laid out in rows and columns. Since content can overlap more than one row or column, this allows considerable flexibility in designing interfaces. For example, if we lay out a grid of four rows and three columns, each grid cell can contain content. Multiple columns or rows can be spanned using the ColumnSpan and RowSpan parameters, as the following code shows:
<Window
x:Class="WpfApp2.myWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="An XAML Dialog">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0">Button A</Button>
<Button Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2">Button B</Button>
<Button Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">Button C</Button>
<Button Grid.Row="1" Grid.Column="3" Grid.RowSpan="2">Button D</Button>
<Button Grid.Row="2" Grid.Column="0">Button E</Button>
<Button Grid.Row="2" Grid.Column="1">Button F</Button>
<Button Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3">Button G</Button>
</Grid>
</Window>
In this code, the grid is set up with a RowDefinition or ColumnDefinition element for each row or column. Then, each cell can be populated based on its zero-origin offset from the top left corner. In this case, we've just put a button in each cell, but anything could be in the cells. The result shows the column spanning and row spanning of the buttons:
Of course, each cell could contain anything, not just buttons. For example, changing the line
<Button Grid.Row="1" Grid.Column="3" Grid.RowSpan="2">Button D</Button>
to
<Image Grid.Row="1" Grid.Column="3" Grid.RowSpan="2" Source="flower.jpg"></Image>
and providing a file called flower.jpg results in this:
Obviously, the sizing of the image in the cell is not perfect, but modifying the row and column definitions to include the automatic sizing parameter (since the image spans two rows, we need to resize both, but only the third column needs this option):
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
expands the image to full cell height:
Resizing the middle two rows to accommodate the image has caused problems with the sizes of the buttons, depending on the original size of the image, but these can be handled manually to fit properly around the image (which can be forced to a certain size, too.
Grids can help in more subtle ways. Suppose you want to show an image with text underneath it. This can be accomplished with code like this:
<Window x:Class="WpfApplication3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="A flower image" Height="200" Width="200">
<Button Width="150" Height="150">
<Button.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="flower.jpg" />
<TextBlock
Grid.Row="1" HorizontalAlignment="Center">
A Flower
</TextBlock>
</Grid>
</Button.Content>
</Button>
</Window>
Here we set up a grid of two rows (since no columns are explicitly defined, one is the default). The second row will adjust its height automatically. The Row 0 content is an image. Row 1 has a TextBlock as content, with automatic centering of some text. The result, when run, is:
which is a nicely framed element for an interface. You can easily see how this would apply to catalogs, layouts of photos, sound and video clips, and so on, each with a defined grid containing the content and a label.
The look of most of the controls provided by WPF is as you would expect, but you can override a control's appearance if you want. For example, instead of using the standard rectangular block for a button, you can change the button to be oval, octagonal, or any other shape you can define. This allows for interfaces to be heavily customized as needed. here's an example:
<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="150" Width="150">
<Button DockPanel.Dock="Bottom" x:Name="StopButton" >
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Polygon Points="50,25 25,50 25,75 50,100 75,100 100,75 100,50 75,25" Fill="Red" Stroke="Black" />
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Window>
When run, this will produce an octagonal button:
The entire octagonal shape is active as the button. As you would expect, WPF includes fairly standard graphics abilities for lines and shapes. For example, the code:
<Window x:Class="WpfApplication7.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="400">
<Rectangle Width="300" Height="100" Fill="Blue" Stroke="Red" StrokeThickness="10">
</Rectangle>
</Window>
will produce a blue rectangle with a red thick border:
The corners of the rectangle can be rounded like a buttons:
<Rectangle Width="300" Height="100" Fill="Blue" Stroke="Red" StrokeThickness="10"
RadiusX="10" RadiusY="10">
</Rectangle>
which results in:
The simplest panel provided by WPF is the StackPanel , which arranges its contents in a row or column. StackPanel is often useful for creating smaller subsections in a larger layout, but the inelegance of the StackPanel usually means it is not used for main interface design except in simple UI instances. A simple example of the StackPanel is:
<Window x:Class="WpfApplication9.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel Background="Gray" Focusable="True">
<TextBlock Margin="5">Select from the following options:</TextBlock>
<CheckBox Margin="5">Gold Trim</CheckBox>
<CheckBox Margin="5">Embossed Lettering</CheckBox>
<CheckBox Margin="5">Shadow Effect</CheckBox>
<TextBlock Margin="5">Select the primary color:</TextBlock>
<ComboBox Margin="5"></ComboBox>
<Button Margin="5">Begin!</Button>
</StackPanel>
</Window>
which produces a dialog like this:
Manipulating the properties of each item and the overall dialog can change the element's appearance in the interface. For example, the Begin button should not be full-width, as it looks silly, and the drop-down list for color should have a default selected. By default, StackPanel sizes all controls the full width of the panel (a horizontal StackPanel gives everything the same height). The size of the button could be set by using HorizontalAlignment, or specific sizes can be given.
This raises the issue of content sizing versus fixed sizing. By default, WPF objects will size according to the panels they are in (as the Begin button did above). If the dialog is resized, the button will resize in proportion, too. Providing specific sizes prevents this issue.
The WrapPanel panel is used to provide a horizontal line of objects until the panel width is reached, then a new line is started, so the code:
<Window x:Class="WpfApplication10.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="200" Width="100">
<WrapPanel>
<Button>ABC</Button>
<Button>DEF</Button>
<Button>GHI</Button>
<Button>JKL</Button>
<Button>MNO</Button>
<Button>PQR</Button>
</WrapPanel>
</Window>
will produce a dialog that looks like this:
WrapPanel contents can be stacked in vertical columns as well as horizontally. The size of the contents vary depending on the text in the buttons in the dialog above, but they could all be sized the same using the Width property, or, alternatively, by using a UniformGrid panel which forces all cells to be the same size:
<Window x:Class="WpfApplication11.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<UniformGrid TextBlock.TextAlignment="Center" Background="Gray">
<TextBlock Text="ABC" />
<TextBlock Text="DEF" />
<TextBlock Text="GHI" />
<TextBlock Text="JKL" />
<TextBlock Text="MNO" />
<TextBlock Text="PQR" />
<TextBlock Text="STU" />
</UniformGrid>
</Window>
which results in a uniform text grid layout:
While these panels are all relatively easy to work with, simply defining the content and letting the panel handle the physical layout, for those times when complete control over the layout is required the Canvas panel is used. The Canvas panel is essentially a formless panel where everything on the panel is explicitly defined relative to the four edges:
<Window x:Class="WpfApplication12.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Canvas Background="Gray" Width="200" Height="200">
<Button Canvas.Left="25" Canvas.Top="25" Content="Hello"></Button>
<Button Canvas.Left="25" Canvas.Top="50" Content="Goodbye"></Button>
<TextBlock Canvas.Right="50" Canvas.Bottom="25">Select One</TextBlock>
</Canvas>
</Window>
This results in a dialog like this:
Every item on the Canvas panel is explicitly defined for position. The problem with using definitions against top-left and bottom-right coordinates is that if the window is resized, there may be overlap of content elements.
Viewbox is a useful element that automatically scales content to fill available space. Viewbox is derived from Decorator, not from the panel classes, so it can have only a single child. To use the Viewbox element, you wrap the appropriate code in <Viewbox> tags.
Finally, if none of the panels or elements provided for interface layout suit your requirements, you can create your own. The entire layout system is extensible, so writing a custom panel is possible without extensive coding.
There are many control elements associated with WPF, most inherited from the earlier WinForm elements. WPF takes a slightly different approach to controls as far as the architecture is concerned. Consistent with the separation of design with function, WPF controls are not responsible for their appearance, only behavior. What this means in practical terms is that the behavior of practically every sort is available in the provided controls, and the appearance can be changed easily to suit your needs. This differs from typical coding where a new control is often needed if the appearance of, say, a button, needs to be modified.
Buttons are a good example of this separation of design and function. The typical button in most development environments is a set shape. With WPF, a button can be anything, such as a graphic, sound clip, text, or shape (such as octagon). This is obvious in the creation of a button in WPF. The XAML code is simply:
<Button >Button Text</Button>
and parameters can be added for the appearance and actions such as events. Three button styles are provided with WPF: push button, radio button, and checkbox. The traditional push button is the button everyone is familiar with, and the other two are simply variations on the button action. All three have only two states.
The GroupBox and Expander controls provide containers for content that is predefined, with a header at the top of the box. The format of the two is similar, but the visual appearance is slightly different. The content of both GroupBox and Expander can be any valid controls, such as checkboxes.
For showing lists, there are several variations of List controls available in WPF. ComboBox, ListBox, ListView and TabControl all present lists of items, while TreeView presents a list in a hierarchical view (like a directory listing). The ability to use any control within these lists makes WPF more flexible than developers usually expect.
Menus are supported in several formats, including the standard menu bar at the top of an application, beneath the title bar, or through pop-up menus that can be context sensitive. These are the Menu and ContextMenu controls respectively. Shortcuts for menus are easily added.
ProgressBar controls are used to indicate work progress and provide a rough idea of time to completion. Typically, if a process is going to take more than a few seconds, a progress bar element is a useful element to add to an interface, otherwise users may suspect the process has hung.
Sliding and scrolling controls allow for values to be selected from a range defined by the developer. Both show a track showing the range from one end of the scale to the other, as well as a draggable object on the track that can be adjusted. The only difference between the slider and scroll controls is visual. Traditionally, scrollbars are used in frames, whereas sliders are used in an interface element. WPF implements these are Slider and Scrollbar controls.
Text controls fall into a few elements in WPF. The standard TextBox control allows a single line of text and the text is editable. WPF actually allows TextBox to handle more than one line of text by setting the AcceptsReturn parameter to True. A variation is the RichTextBox control which allows a variety of content and recognizes rich text formats. For non-editing text, there is the TextBlock and Label controls.
WPF offers a ToolTip control which adds tooltip capabilities to any part of an interface. Tooltip is only useful when associated with another element. The ToolTip control is easy to use:
<Button>
Click here!
<Button.ToolTip>
<ToolTip Content="Click here"></ToolTip>
</Button.ToolTip>
</Button>
Toolbars can be added to any application instead of, or as well as, menu bars, A toolbar is based on icons and two controls, ToolBar and ToolBarTray, allow for the specification of parameters for the icons as images, drawing commands, or other content sources.
All in all, the controls available with WPF are flexible and easy to work with, the coding is straight-forward, and the use of WPF controls is much easier from a coding point of view than many other interface development systems.
Events in WPF are handled by the .NET Framework but with one important difference. The standard .NET Framework stipulates that if an event has no handlers registered, then the event is ignored; with WPF an event can be created with a handler. The importance of this is best shown with an example. If a dialog has a single button which consists of a rectangle shape and some text inside the rectangle, meaning there are two WPF elements to make up the button. When a mouse click on that button occurs, an event is generated. For .NET, to handle the event a MouseLeftButtonUp event handler is used for both the text and the rectangle. With WPF, the event handler can be just for the rectangle or for the text, leaving the other element without an explicit event handler. This is more useful when you consider a button with some content such as an image, a sound clip, animation, or a movie. Instead of registering those elements with the event handler, only the frame needs to be registered. The coding is much simpler.
Each event can be generated by a number of elements (subscribers to the event), or none as just discussed. WPF doesn't use "normal" events, but "routed" events. What this really means is that instead of simply calling the event handler attached to an element, WPF move through the interface tree and can call every handler for the event in any node up to the root level. This ability allows multiple event handlers to be triggered from one event.
There are, in fact, three types of routed events supported. Bubbling events look at the event handler connected to the element that triggered the event, then at its parent, then its parent, and so on up the tree to the root. Tunneling events follow the opposite direction, starting at the root and then moving down to the element that triggered the event. Direct event handlers work as you would expect, ignoring anything else in the tree and triggering only the one event handler associated with the element.
WPF associates both a bubbling and a tunneling event with each event except for direct events. The tunneling event is created first, and is preceded by the title Preview. After the tunneling event has been raised, the bubbling event is started. In most WPF applications you don't really want the tunneling events to be triggered unless they are precursors to the actual event and which may actually block the event handler from launching for some reason.
Laying out content on a page or application window is simple enough using panels, but adding the controls for the application is more involved. Before looking at controls in more detail, the next logical step is to deal with data handling. If you are building an application with a panel showing information, it may look great, but actually managing the data behind it requires some work.
With WPF it is important to keep the data synchronized over more than one control's view of the data, and this is where data binding comes into play. Data binding removes the need to manually write code updating each control in an application when the data in one control changes. More formally, data binding creates and manages the connections between the interface and the business logic.
Binding is done between a source object and a target object. The source object has a property that binds to the target object's dependent property. Target properties must be dependency properties to allow data binding, but most are except for read-only properties. The connection between source property and dependency property may be one way either way, or two way. Obviously two-way bindings allow a change in one property (target or source) to update the other, while the one-way bindings are called "one way" (source to target) or "one way to source" (target to source). There is a variation called "one time" binding which causes the source property to initialize the target property, which can subsequently change without updating the source property.
Changes to data, either on target or source, trigger an update. Bindings to the source have an UpdateSourceTrigger property. When a value changes on the target property the UpdateSourceTrigger property value is changed to PropertyChanged (there are updates only for focus, and when an explicit update is called for, as well). A conversion of data types can be added to the binding, if necessary.
Bindings are created in WPF using the Binding object, which typically requires four components for binding target, target property, binding source, and the value of the source used initially. A simple example helps show this:
<DockPanel
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:BindingExample">
<DockPanel.Resources>
<c:MyData x:Key="mySource"/>
</DockPanel.Resources>
<DockPanel.DataContext>
<Binding Source="{StaticResource mySource}"/>
</DockPanel.DataContext>
<Button Background="{Binding Path=BkgdImage}"
Width="100" Height="25">Binding to a Tartan background.</Button>
</DockPanel>
In this example, the binding source is set using DataContext of a DockPanel panel element. The panel's Button inherits from the parent DockPanel. The class MyData has a property called BkgdImage which is a string. The key in this code is the "Binding Path" statement, which tells the Button to use the content of BkgdImage to set the value of the Background property. How the value in BkgdImage gets set doesn't really matter to the Button property, as long as there is some value there. The value gets set at the source, in this case. The source for the binding is set up with code like this:
<DockPanel.Resources>
<c:MyData x:Key="mySource"/>
</DockPanel.Resources>
<Button Width="100" Height="25"
Background="{Binding Source={StaticResource mySource},
Path=BkgdImage}"> Binding to a Tartan background.</Button>
The "Binding Source" statement sets the binding to the BkgdImage value. The Path parameter must be used when the binding source is an object. Alternatively, if you are binding to XML data, the XPath parameter is used.
The code for setting the binding source above sets a resource. A resource is a named piece of data which is usually defined outside the application code, but is bundled with the application as a separate file. Resources can define properties, but can also be used to define style elements. For example, suppose we have a block of code that defines a style used several times:
<TextBlock VerticalAlignment="Center">Text A</TextBlock>
<TextBlock VerticalAlignment="Center">Text B</TextBlock>
<TextBlock VerticalAlignment="Center">Text C</TextBlock>
<TextBlock VerticalAlignment="Center">Text D</TextBlock>
we could use the resource file to define a style that has the alignment set to Center already. To do this, the Style element is used in the Resources:
<Window.Resources>
<Style x:Key="TextCenter" TargetType="{x:Type TextBlock}">
<Setter Property="VerticalAlignment" Value-"Center" />
The Style element in the resource file uses a set of Setter elements to specify the property and value. The new TextCenter style can now be used in the application code:
<TextBlock Style={StaticResource TextCenter}">Text A</TextBlock>
<TextBlock Style={StaticResource TextCenter}">Text B</TextBlock>
<TextBlock Style={StaticResource TextCenter}">Text C</TextBlock>
<TextBlock Style={StaticResource TextCenter}">Text D</TextBlock>
In this case, not much typing has been saved, but if the TextCenter style also defined many other properties (such as font, font size, margins, font color, and so on) you could remove the need to place all these parameters in the application code, replacing them with a call to the style.
The Style object is a useful way to define global styles, especially across many application projects for common look-and-feel implementations (so all text blocks, headers, images, and so on are rendered the same in multiple dialogs and applications). WPF supports both inline styles (those that are defined in part of the application code) and names styles (defined in a resource Style element). For simple applications, inline styles work well, but as application complexity increases, named styles are a better solution, simplifying both the business logic and the application of consistent styles. Names styles can be overridden, if needed, inline, so there remains enough flexibility for developers.
Styles can be tied to events, which are called property triggers. For example, when a button is clicked, a change in the style of the button can be called (perhaps to make it darker, show a depressed 3D effect, and so on). Property triggers have Boolean values with defined styles for each state. As a variation, a trigger can also be bound to data, allowing a change in a value to trigger a change in style.
WPF applications that do not run in a browser use the standard Windows dialogs. When an application launches, the primary window is the top-level window, which is not contained or owned by any other window. By default, the first window created by a WPF application is the main window, and has the Application.Current property set, but you can override the main window setting in code. A WPF application main window can create other windows either as part of the application coding, or at user command using a window menu like this:
<Window x:Class="WpfApplication8.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Menu>
<MenuItem Header="Windows" x:Name="myWindowMenu">
<MenuItem Header="Window Two" />
<MenuItem Header="Window Three" />
</MenuItem>
</Menu>
</Grid>
</Window>
Which, when run, will produce a main window with a menu bar item called Windows (from the MenuItem Header) and two options underneath:
In this way, you can build up menu bar titles and menu items quite easily.
Instancing with WPF can be handled in a straight-forward manner. Normally, multiple instances of the same application can be run by a user, simply by launching new instances within the operating system. To prevent multiple instances of an application, you can check for the existence of an existing application of the same name and, if one exists, shut down the newly launched instance. This relies on the Mutex class, which is derived from the WaitHandle class in .NET. The code looks like this:
public partial class myApp : System.Windows.Application {
Mutex mutex;
protected override void OnStartup(StartupEventArgs e) {
base. OnStartup(e);
string mutexName = "myCompany, myApplication";
bool createdNewInstance;
mutex = new Mutex(true, mutexName, out createdNewInstance);
if ( !createdNewInstance ) { Shutdown(); }
}
}
Replacing myCompany and myApplication with the relevant items for the application, when the application is launched if there is an existing application with the same data, the if conditional evaluates as True and the Shutdown() method terminates the new instance. This works only when the Mutex has a unique identifier to tag the instances with, in this case myCompany and myApplication.
We’ve only scratched the surface of the capabilities of WPF and the use of XAML files. However, we’ve seen the way in which WPF breaks the business logic of an application from the presentation aspect, and allows both parts to be manipulated easily enough. Indeed, the WPF separation of the logic from the presentation allows for relatively novel interface components such as buttons that change graphic when clicked, lists that affect other parts of the interfaces, and so on. In many ways, these features can negate the need for an external interface tool or multimedia capability, such as that in Flash.
For most developers, once they have tried coding with XAML and WPF, the simplicity and elegance of the approach becomes addictive. When used in Visual Studio with its code completion capabilities, as well as the graphic design tools, developing both interfaces and business logic becomes a fast process. Indeed, most developers will find they can produce finished applications considerably faster with WPF than they could with the older .NET coding approaches.