- WPF as the New GUI
- A Brief Look at the XAML Programming Model
- A Tour of WPF
- Tools for Building Applications
A Brief Look at the XAML Programming Model
One of the major, and often misunderstood, features of .NET 3.0 is the new XAML programming model. XAML provides a set of semantics on top of raw XML that enables a common interpretation. To oversimplify slightly, XAML is an XML-based instantiation script for CLR objects. There is a mapping from XML tags to CLR types, and from XML attributes to CLR properties and events. The following example shows an object being created and a property being set in both XAML and C#:
<!-- XAML version --> <MyObject SomeProperty='1' /> // C# version MyObject obj = new MyObject(); obj.SomeProperty = 1;
XML tags are always defined in the context of a namespace. That namespace determines what tags are valid. In XAML we map XML namespaces to collections of CLR namespaces and assemblies. To make the simple example that was just illustrated work, we need to map in the required namespaces. In XML, we use the xmlns attribute to define new namespaces:
<!-- XAML version --> <MyObject xmlns='clr-namespace:Samples' SomeProperty='1' /> // C# version using Samples; MyObject obj = new MyObject(); obj.SomeProperty = 1;
In C#, the list of assemblies where types are found is always determined by the project file or the command-line arguments to csc.exe. In XAML, we can specify the location of the source assembly for each namespace:
<!-- XAML version --> <MyObject xmlns='clr-namespace:Samples;assembly=samples.dll' SomeProperty='1' /> // C# version csc /r:samples.dll test.cs using Samples; MyObject obj = new MyObject(); obj.SomeProperty = 1;
In XML the world is divided into two spaces: elements and attributes. In terms of objects, properties, and events, the XAML model is more closely aligned with the CLR. The encoding to attributes or child elements for property values is flexible. We can rewrite the previous example using a child element instead:
<MyObject xmlns='clr-namespace:Samples;assembly=samples.dll'> <MyObject.SomeProperty> 1 </MyObject.SomeProperty> </MyObject>
Every property element is qualified with the type that defines the property, allowing properties to contain arbitrarily complex structured data. For example, suppose we have a second property that takes a Person object with FirstName and LastName properties. We can easily write the code in XAML using the property element syntax:
<MyObject xmlns='clr-namespace:Samples;assembly=samples.dll'> <MyObject.Owner> <Person FirstName='Chris' LastName='Anderson' /> </MyObject.Owner> </MyObject>
XAML was created to be a markup language that integrated well with the CLR and provided for rich tool support. A secondary goal was to create a markup format that was easy to read and write. It may seem a little rude to design a feature of the platform that is optimized first for tools, then for humans, but the WPF team felt strongly that WPF applications would typically be authored with the assistance of a visual design tool like Microsoft Visual Studio or Microsoft Expression. To walk the line between tools and humans, WPF allows the type author to define one property to be the content property.2
In our example, if we make the Owner property of MyObject the content property,3 then the markup can be changed to omit the property element tag:
<MyObject xmlns='clr-namespace:Samples;assembly=samples.dll'> <Person FirstName='Megan' LastName='Anderson' /> </MyObject>
For further readability, XAML has a feature known as markup extensions. This is a general way to extend the markup parser to produce simpler markup. Markup extensions are implemented as CLR types, and they work almost exactly like CLR attribute definitions. Markup extensions are enclosed in curly braces, { }. For example, to set a property value to the special value null, we can use the built-in Null markup extension:
<MyObject xmlns='clr-namespace:Samples;assembly=samples.dll'> <Person FirstName='Megan' LastName='{x:Null}' /> </MyObject>
Table 1.1 lists all of the built-in XAML features.
Table 1.1. Built-in XAML Features
XAML Namespace Directive |
Meaning |
Example |
x:Array |
Creates a CLR array. |
<x:Array Type='{x:Type Button}'> <Button /> <Button /> </x:Array> |
x:Class |
Specifies the name of the type to define (used only in markup compilation). |
<Window x:Class='MyNamespace.MyClass'>... </Window> |
x:ClassModifier |
Specifies the modifiers ("public," "internal," etc.) of the type to define (used only in markup compilation). |
<Window x:Class='...' x:ClassModifier='Public'> ... </Window> |
x:Code |
Delineates a block of in-line code (used only in markup compilation). |
<Window x:Class='...'> <x:Code> public void DoSomething() { ... } </x:Code> ... </Window> |
x:Key |
Specifies the key to use for an element (supported only on elements contained in a dictionary). |
<Button> <Button.Resources> <Style x:Key='Hi'>...</Style> </Button.Resources> </Button> |
x:Name |
Specifies the programmatic name of an element (typically used when an element doesn't have a built-in name property). |
<sys:Int32 xmlns:sys='clr-namespace: System;...' x:Name='_myIntegerValue'> 5</sys:Int32> |
x:Null |
Creates a null value. |
<Button Content='{x:Null}' /> |
x:Static |
Creates a value by accessing a static field or property from a type. |
<Button Command='{x:Static ApplicationCommands.Close}' /> |
x:Subclass |
Provides a base type for markup compilation for languages that don't support partial types. |
|
x:Type |
Provides a CLR type (equivalent to Type.GetType). |
<ControlTemplate TargetType='{x:Type Button}'> ... </ControlTemplate> |
x:TypeArguments |
Specifies the generic type arguments for instantiating a generic type. |
<gc:List xmlns:gc='clr- namespace:System.Collections. Generic;...' x:TypeArguments='{x:Type Button}' /> |
x:XData |
Delineates a block of in-line XML; may be used only for properties of type IXmlSerializable. |
<XmlDataSource> <x:XData> <Book xmlns='' Title='...' /> </x:XData> </XmlDataSource> |
Markup extensions are resolved exactly like object tags, which means that we must declare the "x" XML prefix for this markup to be parsed. XAML defines a special namespace for dealing with the parser built-in types:
<MyObject xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' xmlns='clr-namespace:Samples;assembly=samples.dll'> <Person FirstName='Megan' LastName='{x:Null}' /> </MyObject>
It is also possible for any CLR assembly (or set of assemblies) to define a URI-based name for a collection of CLR namespaces and assemblies. This is the equivalent of the old #include 'windows.h' statement that C/C++ developers know. The WPF assemblies use this mechanism, so we can use either format to import WPF into a XAML file:
<!-- option 1: import by CLR namespace --> <Window xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' xmlns= 'clr-namespace:System.Windows;assembly=presentationframework.dll'> </Window> <!-- option 2: import by URI --> <Window xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'> </Window>
The nice thing about the URI-based method is that it imports several CLR namespaces and assemblies, meaning that your markup is more compact and easier to work with.
The final feature of XAML is the ability to extend types with properties provided by other types; we call this feature attached properties. In effect, attached properties are just type-safe versions of the JavaScript expando properties. In the WPF version of XAML, attached properties work only if the type defining the property and the type whose property is being set both derive from the DependencyObject type, but the specification for XAML doesn't have this requirement.
In the following example the property Dock is defined by the type DockPanel. Attached properties are always prefixed by the name of the type providing the property, even when they appear as attributes:
<Window xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'> <DockPanel> <Button DockPanel.Dock='Top'>Top</Button> <Button> <DockPanel.Dock>Left</DockPanel.Dock> Left </Button> <Button>Fill</Button> </DockPanel> </Window>
XAML is a fairly simple language with relatively few rules. In the .NET Framework 3.0 release of XAML, all the definitions for XAML tags are in CLR types—the goal being to ensure that anything we can do in markup we can also do in code. Throughout this book I will switch back and forth between using markup4 and using code, depending on whichever one more easily demonstrates a particular concept.
Now that we have a grounding of XAML under our belts, we can begin looking at the main parts of WPF itself.