Chapter 1
IN THIS CHAPTER
Taking a first look at WPF and what it can do for you
Working with XAML
Building your first WPF application
Comparing XAML to C#
WPF, or Windows Presentation Foundation, is a graphical system for rendering user interfaces. It provides great flexibility in how you can lay out and interact with your applications. With Common Language Runtime (CLR) at its core, you can use C# or any other CLR language to communicate with user-interface elements and develop application logic. The advantages of WPF for your application are its rich data binding and visualization support and its design flexibility and styling.
WPF enables you to create an application that is more usable to your audience. It gives you the power to design an application that would previously take extremely long development cycles and a calculus genius to implement. Now you can implement difficult things like graphics and animations in as few as three lines of code! This chapter introduces you to key WPF concepts as well as common application patterns used in the software industry today.
WPF's graphics capabilities make it the perfect choice for data visualization. Take, for instance, the standard drop-down list (or combo box). Its current use is to enable the user to choose a single item from a list of items. For this example, suppose you want the user to select a car model for purchase.
The standard way of displaying this choice is to display a drop-down list of car model names from which users can choose. There is a fundamental usability problem with this common solution: Users are given only a single piece of information from which to base their decision — the text that is used to represent the item in the list.
For the power user (or car fanatic), this may not be an issue, but other users need more than just a model name to make an educated decision on the car they want to purchase. This is where WPF and its data visualization capabilities come into play.
You can provide a template to define how each item in the drop-down list is rendered. The template can contain any visual element, such as images, labels, text boxes, tooltips, drop shadows, and more!
Figure 1-1 shows a typical display of a combo box. This control has limitations: It can relay to the user only a single piece of information, the text used to represent the car model (yes, you could add graphics to the combo box using low-level programming techniques beyond the ken of mere mortals, but most people wouldn’t want to). It’s possible for you to display images of the car models in a separate control based on the selection in the list, but this still mandates users to make their selection before seeing exactly what it is they are choosing. In contrast, WPF has the flexibility to display many pieces of information in each combo box item, like a one-stop shop for all the information the user will need to make their decision. (See Figure 1-2 for a WPF combo box.)
Figure 1-2 shows a sample combo box in WPF. The way the combo box item is rendered is defined using a data template. (Chapter 3 of this minibook covers data templates.) Each item in this combo box is rendered to provide the user with a visual cue along with multiple data fields. Displaying all this information enables users to make an educated decision about the choice they are making.
WPF enables you to build user interfaces declaratively. Extensible Application Markup Language (XAML; pronounced zammel) forms the foundation of WPF. XAML is similar to HTML in the sense that interface elements are defined using a tag-based syntax.
By default, when creating a WPF application in Visual Studio 2010 and above, the following schemas are represented in generated XAML files:
http://schemas.microsoft.com/winfx/2006/xaml/presentation
: This schema represents the default Windows Presentation Framework namespace.http://schemas.microsoft.com/winfx/2006/xaml
: This schema represents a set of classes that map to CLR objects.Most CLR objects can be expressed as XAML elements (with the exception of abstract base classes and some other nonabstract base classes used strictly for inheritance purposes in the CLR). XAML elements are mapped to classes; attributes are mapped to properties or events.
At runtime when a XAML element is processed, the default constructor for its underlying class is called, and the object is instantiated; its properties and events are set based on the attribute values specified in XAML. The next section reviews more XAML basics and gets you started on the path of WPF application development.
Now it's time to get comfortable, so stop for a moment, go grab a caffeinated beverage, sit in a comfortable chair, pull up to your computer, and get ready to go! To create your first project, follow these steps:
Click Create a New Project.
You see the list of available projects.
Select C#, Windows, and Desktop in the filter list.
Visual Studio presents a list of Windows Desktop project types.
Highlight WPF App (.NET Framework) and click Next.
You see the Configure Your New Project page of the wizard, shown in Figure 1-3.
Type MyFirstWPFApplication in the Project Name field, select Place Solution and Project in the Same Directory, and then click Create.
Visual Studio creates a new WPF project for you and displays both the designer and the Code Editor with a default application defined. The WPF Application template creates two XAML files along with their respective code-behind files: App.xaml
(App.xaml.cs
) and MainWindow.xaml
(MainWindow.xaml.cs
), as shown in Figure 1-4. At this point, you can start adding code to the example application.
App.xaml
represents the entry point of the application. This is where application-wide (globally scoped) resources and the startup window are defined (see Listing 1-1).
LISTING 1-1 App.xaml
<Application x:Class="MyFirstWPFApplication.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyFirstWPFApplication"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
Listing 1-1 displays the XAML that was generated by the WPF App (.NET Framework) template in Visual Studio. Note that the WPF namespaces are defined. The namespace that represents the CLR objects will be distinguished in the XAML file with the x
prefix.
The StartupUri
value defines the window that will be displayed before the application is executed. In this case, the MainWindow.xaml
window will be displayed.
The x:Class
attribute defines the C# code-behind file of this XAML file. If you open App.xaml.cs
, you see that its class name is App
and it inherits from the Application
class (which is the root element of the XAML file).
For instance, if you want to add the namespace MyTemplates.DataTemplates from the assembly MyTemplates.dll
, you could define the namespace as
xmlns:myDTs="clr-namespace:MyTemplates.DataTemplates;assembly=MyTemplates.dll"
You are then able to instantiate an object from the MyTemplates.DataTemplates namespace as follows:
<myDTs:myClass></myDTs:myClass>
To help you understand the creation and use of a global application-scoped resource, this section shows you how to create a resource that holds a string used in the application. An application-scoped resource is available to all Windows and user controls defined in the project. Follow these steps:
System
namespace located in the mscorlib.dll
assembly by adding the following namespace to the App.xaml
root element (see Listing 1-2):
xmlns:sys="clr-namespace:System;assembly=mscorlib"
The mscorlib.dll
assembly is where the String
class is located. The String
class is now available for use throughout the App.xaml
document.
Application.Resource
tags; add the following String
class element:
<sys:String x:Key="Purpose">Hello WPF World!</sys:String>
This element instantiates an object of type String
, initialized to the value Hello WPF World!
, and keyed off of the key Purpose
. This resource is now available throughout the MyFirstWPFApplication
application by requesting the resource “Purpose” (see Listing 1-2).
LISTING 1-2 Updated App.xaml with Resource and System Namespace Defined
<Application x:Class="MyFirstWPFApplication.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyFirstWPFApplication"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
StartupUri="MainWindow.xaml">
<Application.Resources>
<sys:String x:Key="Purpose">Hello WPF World!</sys:String>
</Application.Resources>
</Application>
This type of tag is called a property element, an XML element that represents a property (or attribute) of an object. Property elements are used when complex objects are assigned to a property of an object that can't be expressed as a simple string value. Property elements must be contained within the tags of the parent element — in this case, within the Application
tags.
If you run the application as is, not much happens beyond the display of an empty window. The empty window is the one defined in MainWindow.xaml
.
Add a label to MainWindow.xaml
that displays the purpose of the String you defined in Resources. Just follow these steps:
MainWindow.xaml
.Grid
tags, define a grid with a single row and single column by adding the following XAML markup:
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
Each column and row is defined by the ColumnDefinition
and RowDefinition
element contained within the Grid.ColumnDefinitions
and Grid.RowDefinitions
properties, respectively. If you want to add more columns, you simply add another ColumnDefinition
element to the Grid.ColumnDefinitions
Property Entity. The same goes for adding rows: You add an additional RowDefinition
element to the Grid.RowDefinitions
Property Entity.
Grid.RowDefinitions
Property entity, create a label using the following XAML:
<Label x:Name="lblPurpose" Content="{StaticResource Purpose}"
FontSize="25" Grid.Row="0" Grid.Column="0"/>
This markup instantiates a WPF Label
object accessible to the code-behind file (MainWindow.xaml.cs
) using the variable lblPurpose
. The Content
attribute contains the text that is to be displayed in the label; in this case, you use the Application Resource that you defined in the preceding section by retrieving it using its key value, which is Purpose
. The label text is rendered with a font size of 25 units and is to be located in the grid in the first row and first column.
x:Name
: This attribute assigns a variable name to the object being created by the XAML tag. This enables you to access the object from the code-behind file. In this case, the variable name of the label object being instantiated is lblPurpose
.Content
: The value assigned to this attribute can be of any type. By default, you can assign it a string
value, and it will render as you would think a standard label would render. In the WPF reality, Content
can be composed of anything: a string, an image, an instance of a user control, a text box, and so on. For more info, see Chapter 2 of this minibook.FontSize
: The size of the font of the label. It is important to note that the size isn't denoted in points; it’s expressed in Device Independent Units. WPF gets away from the concepts of pixels and points and moves to a universal sizing strategy. Think of Device Independent Units as more of a relative size than a pixel. For instance, if the containing element of the label were 100 units by 100 units, the label would render as ¼ of that size.Grid.Row
: Identifies the grid row in which to render the label. Grid row collections are zero based, meaning that the first row is row 0, the second row is row 1, and so on. You should also note that the Label
class doesn't contain a property named Grid
. What you see here is the concept of attached properties. An attached property is a way to assign the context of a current control relative to the properties of a containing control. In this case, you assign the label to appear in the first row (row index 0) of its containing grid. Also observe that the label is located within the Grid
tags; this is how the containing Grid
element is located.Grid.Column
: Similar to Grid.Row
, this attached property identifies the grid column in which to render the label. Together with Grid.Row
, both properties identify the cell where the label is located. In this case, you assign the label to render in the first column of its containing grid. Grid column collections are also zero based.Go ahead and run your application. You now see Hello WFP World! displayed in the label on your Window. Congratulations! You have just created your first WPF application!
Anything that you can implement using XAML can be implemented in C#. This is not true in reverse; not everything you can do in C# can be done in XAML. C# is the obvious choice for performing business logic tasks with procedural code that can't be expressed in XAML. In the following steps, you create an identical WPF application to the one you created in the preceding section, this time using C# to implement its functionality:
Click Create a New Project.
You see the list of available projects.
Select C#, Windows, and Desktop in the filter list.
Visual Studio presents a list of Windows Desktop project types.
Highlight WPF App (.NET Framework) and click Next.
You see the Configure Your New Project page of the wizard, shown in Figure 1-3.
Type MyFirstCodeOnlyWPFApplication in the Project Name field, select Place Solution and Project in the Same Directory and then click Create.
Visual Studio creates the Solution and Project structure.
App.xaml.cs
.OnStartup
method to include the creation of the Purpose application resource by adding the following code to the App
class:
protected override void OnStartup(StartupEventArgs e)
{
// Create and add the Purpose application resource
string purpose = "Hello WPF World, in C#";
this.Resources.Add("Purpose", purpose);
base.OnStartup(e);
}
MainWindow.xaml
and give the Grid
element a name by adding the following attribute shown in bold:
<Grid x:Name="gridLayout">
</Grid>
MainWindow.xaml.cs
, and in the default constructor, after the InitializeComponents
method call, add the following code shown in bold:
public MainWindow()
{
InitializeComponent();
// Define grid column and row
this.gridLayout.ColumnDefinitions.Add(new ColumnDefinition());
this.gridLayout.RowDefinitions.Add(new RowDefinition());
// Obtain label content from the application resource, Purpose
string purpose = this.TryFindResource("Purpose") as string;
Label lblPurpose = new Label();
lblPurpose.Content = purpose;
lblPurpose.FontSize = 25;
// Add label to the grid
this.gridLayout.Children.Add(lblPurpose);
// Assign attached property values
Grid.SetColumn(lblPurpose, 0);
Grid.SetRow(lblPurpose, 0);
}
Run the application and observe that the resulting product is similar to that obtained in the section “Diving In! Creating Your First WPF Application,” earlier in this chapter.