XAML, or eXtensible Application Markup Language, is the lingua franca of both WPF and Silverlight. It is an XML dialect that was designed to represent hierarchical object graphs in a fashion that is both human-readable and easy to parse. In WPF, XAML is used to represent the composition of the user interface, its style, animations and almost any declarative aspect of the user experience. Let's see an example:
<Window x:Class='XamlSamples.Window1'
xmlns='http://schemas.microsoft.com/winfx/2006/xamlpresentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
Title='Window1'
Height='300'
Width='300'>
<Grid x:Name='layoutRoot'>
</Grid>
</Window>
If you create a new WPF project in Visual Studio, the XAML listed will be generated for you. It shows some common features of the markup language. The first thing you should notice is the use of XML namespaces, denoted by xmlns. These namespaces tell the XAML parser where to find the elements declared in the markup. The first declaration makes WPF the default namespace. This allows the majority of elements to be declared without explicit namespaces. The second namespace is that of the XAML parser itself, which enables some special cases, such as naming elements. x:Name is an example of this. Notice that the root node is Window and it has attributes of Title, Height and Width. In XAML, elements correspond to instances of objects that will be created, and attributes specify what the instances' properties should be set to. The XAML translates to the following code:
var layoutRoot = new Grid();
var window = new Window
{
Title = 'Window1',
Width = 300,
Height = 300,
Content = layoutRoot
};
This sample also demonstrates an important concept in WPF: the Content Model. Notice that Window in the XAML has a Grid as its child element, but it translates to setting the Content property on the Window in code. Most WPF elements declare a default content property so that XAML creation is made more intuitive. Let's discover some more features of XAML by putting some elements in our Grid:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height='*' />
<RowDefinition Height='*' />
</Grid.RowDefinitions>
<Button Grid.Row='0'
Content='Grid Row 0'/>
<Button Grid.Row='1'
Content='Grid Row 1'/>
</Grid>
Knowing that elements map to instances and attributes
map to properties, you may be wondering how you would set a property with a complex value; one that could not be expressed as an attribute. This is accomplished by using Property Element Syntax, demonstrated by the Grid.RowDefinitions element. A Grid is a Panel composed of children arranged in columns and rows. To declare the rows in a Grid, you set its RowDefinitions property, which is a collection of RowDefinition objects. In XAML, you can turn any property into an element using the syntax TypeName.PropertyName, as we have done with Grid.RowDefinitions. By combining Property Attribute Syntax and Property Element Syntax with the conventions of the Content Model you can represent almost any object hierarchy. Additionally, there is another common markup usage demonstrated: Attached Properties. You can see them on the Button declarations. WPF enables containing elements to attach information to their children using the pattern ParentType.AttachedProperty. In this case, the Grid.Row properties tell the parent Grid where to place each Button.
Despite the flexibility of the XAML syntax, there are still many scenarios that are tricky to accomplish with standard XML. To address this issue, XAML offers Markup Extensions. Here's a typical example:
<Window x:Class='MarkupExtensionSamples.Window1'
xmlns='http://schemas.microsoft.com/winfx/2006/xamlpresentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
Title='Window1'
Height='300'
Width='300'>
<Window.Resources>
<SolidColorBrush x:Key='myBrush'Color='Red' />
</Window.Resources>
<Grid>
<Button Background='{StaticResource myBrush}'
Margin='10'
Content='Background from StaticResource'/>
</Grid>
</Window>
Often, a designer likes to declare colors, brushes and styles for consistent use throughout an application. These can be stored in a ResourceDictionary. In this example, we have declared a SolidColorBrush which we would like to later use as our button's background. The StaticResourceExtension class is the perfect tool for the task. We use it by enclosing the words 'StaticResource' in braces, followed by the Key of the resource we want to reference. There are several things common to all markup extensions that we should note:
- Markup extensions are enclosed in braces.
- XAML allows us to drop the word 'Extension' from the declaration, even though it is part of the class name.
- 3. Most extensions have a default value, which will be passed into the extension's constructor. In this example that value is the resource key, 'myBrush.'
Any read/write property on the extension class can also be set in XAML. These properties can even be set using other extensions! Here's a typical databinding sample:
<TextBox Text='{Binding Source={StaticResource myDataSource},
Path=FirstName, UpdateSourceTrigger=PropertyChanged}'
/>
Notice that we set the Source property of the BindingExtension using another MarkupExtension, StaticResourceExtension. We then set two additional properties on the BindingExtension.
Table 1 shows several of the most common built-in extensions along with their purpose:
ExtensionDescription{StaticResource}Injects a previously defined resource into markup.{DynamicResource}Creates a dynamically updatable link to a resource.{Binding}Enables data binding.{TemplateBinding}Simplifies binding inside a ControlTemplate.{x:Static}References static variables.{x:Type}References instances of a Type object.{x:Null}Represents a Null value.
In addition to what XAML provides out-of-the-box, you can create your own markup extensions. All you have to do is derive your class from MarkupExtension. For example:
public class HelloExtension : MarkupExtension
{
private readonly string _name;
public HelloExtension(string name)
{
_name = name;
}
public override object ProvideValue(IServiceProvider
serviceProvider)
{
return 'Hello ' + _name + '! Nice to meet you.';
}
}
And you use it in code like so:
<Window x:Class='MarkupExtensionSamples.Window1'
xmlns='http://schemas.microsoft.com/winfx/2006/xamlpresentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:MarkupExtensionSamples'
Title='Window1'
Height='300'
Width='300'>
<TextBox Text='{local:Hello Fauntleroy }' />
</Window>
Whenever you create custom extensions, or any class not defined by the framework, you must remember to add its namespace before you use it in XAML. A common convention is to use the name 'local' for classes defined in the project.
Controls
WPF ships with a number of the standard controls that you would expect. Here's a short list of the most common:
- Label
- Button
- TextBox
- ListBox
- ComboBox
- CheckBox
- Slider
These controls all look and behave according to traditional desktop UI metaphors. However, the nature of controls in WPF is very different from traditional UI frameworks. You can completely redefine the look of a control in WPF without altering its behavior. You can do this using Control Templates.
The most common scenario for manipulating a control template is changing the way a button looks. All controls have a Template property. Let's examine the following XAML:
<Button Content='Click Me'>
<Button.Template>
<ControlTemplate TargetType='Button'>
<StackPanel Orientation='Horizontal'
Margin='4'>
<Ellipse Width='16'
Height='16'
Fill='Blue'
Margin='0 0 4 0' />
<Border Background='LightBLue'
CornerRadius='4'
Padding='4'>
<ContentPresenter />
</Border>
</StackPanel>
</ControlTemplate>
</Button.Template>
</Button>
Using the Property Element Syntax, we set the button's template to an instance of ControlTemplate. The TargetType is necessary to tell the control template what sort of control it will be applied to. Control templates require this because they typically exist independent of the controls themselves. The content of a control template can be just about anything. This demonstrates the compositional nature of WPF. A control template can be composed of any other WPF elements, even another Button control.
Below is an example of how this XAML will render compared to a plain button.
There are additional elements that are used to compose an interface in WPF which technically are not controls. Some examples from the XAML are StackPanel, Ellipse, Border, and ContentPresenter.
StackPanel is part of a family of elements called panels that assist us with layout. We'll talk about them in the next section.
Ellipse and its siblings are shapes. Shapes are a convenient set of classes for drawing basic shapes within the UI.
Border is an element that you will encounter frequently. It is used to decorate other elements with a border. It is also the quick way to put rounder corners around an element.
ContentPresenter is a special element that is used when you are constructing control templates. It is a placeholder for the actual content of the control. For example, we set the Content property of our Button to 'Click Me'. This content was injected into our template where we placed the ContentPresenter. It's also interesting to note, that if we had omitted the ContentPresenter then our control template would simply have ignored the content.
Control templates are especially powerful when they are used in combination with WPF's styles and resources.
Missing Controls
There are a number of controls that you might expect to be present in WPF that are not. The most obvious examples are DataGrid, DatePicker, Calendar, and charting controls. Microsoft has chosen to release these controls separately from the .NET platform itself. They are available as part of the WPF Toolkit on CodePlex (http://www.codeplex.com/wpf). You'll also find the official Ribbon control on CodePlex. Look for most of these controls to join the platform in version 4.0.
Lookless Controls
Two of the most important controls in WPF are the ContentControl and the ItemsControl. These controls do not have any defined look. They rely on a concept called Data Templates. Data templates are similar to control templates, except that instead of targeting a specific control, they target a specific type.
With a ContentControl, you can set the Content property to an instance of any class. Then you can define a data template that is specific to that class. Say that we have a simple class like this one:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
We can define a data template is our resources:
<DataTemplate x:Key='personTemplate'>
StackPanel>
<TextBlock Text='{Binding FirstName}' />
<TextBlock Text='{Binding LastName}' />
<TextBlock Text='{Binding Age}' />
</StackPanel >
You can also define an instance of Person in the resources:
<ContentControl Content='{Binding Source={StaticResource myPerson}}'
ContentTemplate='{StaticResource personTemplate}' />
You might be wondering why this is valuable. Why not simply create a user control that has the same content as the data template? The ContentControl has no inherent behavior beyond rending its content. With a user control, you could at least add behavior specific to the Person class. We'll come back to this question.
Another way to associate the data template is to provide a DataType. Here's same template using this approach:
<DataTemplate DataType='{x:Type local:Person}'>
<StackPanel>
<TextBlock Text='{Binding FirstName}' />
<TextBlock Text='{Binding LastName}' />
<TextBlock Text='{Binding Age}' />
</ StackPanel>
</DataTemplate>
Depending on where this data template was stored, you could now do the following:
<ContentControl Content='{Binding Source={StaticResource myPerson}}' />
We'll discuss DataContext in the section on Data Binding.
The ItemsControl is very similar to ContentControl, except that it binds to a collection of objects using the ItemsSource property. In fact, if we bound it to a collection of Person instances, the same data template would be applied.
<ItemsControl ItemsSource='{Binding Source={StaticResource
myPersonCollection}}' />
These two controls combined with data templates are very powerful. They are an essential part of Separated Presentation patterns such as MVVM or Presentation Model.
{{ parent.title || parent.header.title}}
{{ parent.tldr }}
{{ parent.linkDescription }}
{{ parent.urlSource.name }}