Home > Articles > Home & Office Computing > Microsoft Windows Desktop

  • Print
  • + Share This
This chapter is from the book

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.

  • + Share This
  • 🔖 Save To Your Account