Xaml Markup Extensions

Markup extensions for xaml are a lot of fun. There are a bunch of freebies that come with the framework but you can easily create your own as well. Here’s a brief list of common markup extensions that come with the framework:

  • Binding
  • TemplateBinding
  • StaticResource
  • DynamicResource

As a WPF developer you’ll probably run into these every day. Markup extensions are powerful and also very simple to master. If you’re not sure what I’m talking about here is a sample of what a markup extension looks like inside of Xaml:

<TextBlock Text="{Binding Name}" />

If we break it down there are only a few basic rules. The first section of the markup extension is the Type (as in .NET type) following that is a comma separated list of Properties and values. The first property name is optional, each markup extension may specify what the default property is and if a value appears first and without a property name it is assigned to that default property. So this means that the markup extension:

{Binding Name}

Is a class called “Binding” and has a default property, the value of which is “Name”. Also, it’s interesting to note that, like Attributes in C#, the “Extension” part of the type name is optional. If you create a markup extension called “TestExtension” you would reference it in Xaml as “{Test}”.

Also it is possible to use other markup extensions as values for properties. Therefore it’s possible to have a more complex markup extension such as:

{Test Name, Color={StaticResource color}}

For this markup extension there are two properties, one is default and is being assigned the value “Name” while the other is called Color and is being assigned a color from this controls resources.

To create your own markup extension you simply create a class than inherits from the abstract base class MarkupExtension. Here is an example of a custom markup extension for rotating images:

namespace WpfApplication1
{
    using System;
    using System.Windows.Markup;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;

    [MarkupExtensionReturnType(typeof(BitmapSource))]
    public sealed class RotateExtension : MarkupExtension
    {
        public RotateExtension() { }

        public RotateExtension(BitmapSource source)
        {
            this.Source = source;
        }

        public BitmapSource Source { get; set; }

        public double Angle { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            IXamlTypeResolver resolver = (IXamlTypeResolver)serviceProvider
                .GetService(typeof(IXamlTypeResolver));
            IProvideValueTarget target = (IProvideValueTarget)serviceProvider
                .GetService(typeof(IProvideValueTarget));
            BitmapSource rotatedSource = null;
            if (this.Source != null)
            {
                RotateTransform rotation = new RotateTransform(this.Angle);
                rotatedSource = new TransformedBitmap(this.Source, rotation);
            }

            return rotatedSource;
        }
    }
}

Adding the MarkupExtensionReturnType attribute on the class is optional. You’ll need at least one public parameterless constructor and one optional constructor with your default parameter. Then just implement the ProvideValue method! The service provider contains (usually) at least the two services listed here. There is no guarantee they will be there but they are pretty much always there. My example applies a very simple RotateTransform to the bitmap before providing it. Here is the accompanying xaml.

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    x:Class="WpfApplication1.MainWindow"
    x:Name="Window"
    Title="MainWindow">
    <Border HorizontalAlignment="Center" VerticalAlignment="Center">
        <StackPanel Orientation="Horizontal">
            <Image Source="img15.png" Stretch="None" />
            <Image Source="{local:Rotate img15.png, Angle=90}" 
                   Stretch="None" />
        </StackPanel>
    </Border>
</Window>

Notice the need to add the namespace “local:” to my extension. This is sort of unfortunate but necessary. I would recommend creating fairly terse names for you extensions to help save you some typing. Also notice that I could just use “img15.png”, which is the path of a file added to my project as a resource instead of trying to create some type of a bitmap source object. The xaml deserializer will realize that the optional parameter on my constructor is a BitmapSource and it will try to use the TypeConverter associated with that type to convert it before giving it to me. Type converters will be the topic of another blog post however.

EDIT:

Sorry folks I had to disable comments on this post due to tons of spam coming in from somewhere. Also, somehow the image above here got overwritten by an image from another post so I deleted it.

Author: justinmchase

I'm a Software Developer from Minnesota.

Leave a Reply

%d bloggers like this: