Wednesday, July 15, 2009

[2009.07.15] WPF Centric XAML Markup Extensions [I/IV]

Markup Extension Summary

The first time I encountered XAML extensions when I started learning WPF was certainly a wtf moment for me. It was also a bit more difficult to understand since there were several variations of doing the same thing, but the books I read didn't really go into details about what's what. Instead, it was basically, here is an example of binding the Text property to another dependency property, or to some resource. Well enough with the background, here I will try to lay out the most commonly used WPF centric (see further down) markup extensions and show examples how to use them.

I am using the WPF Documentation for Visual Studio 2010 Beta 1 as a guide to lay out all the information. After all, who really wants to read through tons of documentation! Hopefully this will save you some time.

To set a property in XAML, you just have to use a literal string value and the XAML processor will parse the value you entered and figure out if it is correct by either interpreting the string as a literal value or use a type converter to convert it to an object that is specific to the property you are setting. For instance, to set the Text property of the TextBlock element, it is simply:

<TextBlock Text="Hello" /><!--setting a property 'normally'-->

However, there are times when a simple literal value for a property is not enough or even available at the time. This is where markup extensions come into play. Basically, XAML markup extensions let you set the property by escaping the standard literal values used for element attributes and replace it with either another lookup method or a computation. You need to use this strategy when the value for the property is:
  • difficult to express as a simple string
  • needs to be set a different way such as using resources or the results of a computation
  • not currently available and must be set dynamically
  • beyond the parsing capability of the XAML processor

Markup extensions can be split into two categories:

  1. WPF-Specific Markup Extensions: Commonly used for resource reference and data binding.
  2. XAML-Defined Markup Extensions: Defined as part of the XAML language specification and are not specific to how WPF interacts with XAML. A quick way to tell them apart is that this type of extension usually starts with the x: prefix, such as {x:StaticExtension }.

In XAML, a markup extension is indicated by an attribute value enclosed in a pair of braces, { }. Once the XAML parser encounters that, it knows to treat the attribute as a markup extension and not as a literal value. WPF uses the naming convention of adding the word "Extension" to all markup extension classes. However, when used with WPF centric extensions, that word is dropped.

A markup extension may take several forms, depending on which type of extension is being used (WPF or XAML centric) as well as how it is being used (referring to a type, resource or binding). Generally, it will take the form:

<!-- setting a property using XAML extension-->
<ElementName PropertyName=
"{Markup[Extension] [PositionalParameter][NamedParameters]}" />
  • Markup[Extension] is the name of the markup class and the "Extension" name is used only for XAML extensions. For example, the WPF StaticResourceExtension is given as { StaticResource } whereas a XAML extension is more verbose like { TypeExtension }
  • [PositionalParameter] The arguments passed to the public constructor of a class are the positional parameters for that class. Of course, overloaded constructors may have a variety of positional parameters. For example, the StaticResourceExtension class has an overloaded constructor that takes a single Object as a parameter, so this is the positional parameter for this extension. However it also exposes a public property, ResourceKey which is the named parameter for this class.

    I agree, it is a bit confusing! So here is how you can reference a StaticResourc called "scb" using the positional parameter of its constructor:

    <Button Background="{StaticResource scb}" />
  • [NamedParameters] are basically the publicly exposed properties of the extension class which can be matched in XAML. For example, the StaticResourceExtension class has the property ResourceKey which can be used like:

    <Button Background="{StaticResource ResourceKey=scb}" />

Note 1: As of WPF 4.0, the markup extensions that were in PresentationFramework.dll has been forwarded to WindowsBase.dll assembly, where the majority of extensions live.

The XAML specific extensions such as StaticExtension, NullExtension, ArrayExtension can be found under the System.Windows.Markup namespace in the WindowsBase.dll assembly. On the other hand, PresentationFramework.dll is home to the WPF centric markup extensions such as Binding which is found in the System.Windows.Data namespace, and StaticResourceExtension and TemplateBindingExtension are under the System.Windows namespace.

Note 2: The Binding markup extension is the only one that actually does not follow the the naming convention for markup extensions by adding "Extension" to the name of the class. So there is no BindingExtension, just Binding

The abstract MarkupExtension class in the WindowsBase.dll assembly is the base class for markup extensions.

As mentioned in my earlier post, [2009.06.10] Visual Studio 2010 B1, WPF and XAML Improvements, the XAML IntelliSense has been vastly improved for application of markup extensions.

In the posts to follow, I will look at these commonly used (at least to me) WPF centric extensions namely:

  • Binding
  • StaticResource
  • DynamicResource
  • TemplateBinding
  • RelativeSource

No comments: