Ramblings of General Geekery

Almost everything you need to know about XAML serialization (part 2)

In part one of this little simple series, we saw how to use XAML as a file format for our own custom types. However, we wanted to reduce the verbosity of XML for specifying objects with only a few properties. This can be done with MarkupExtensions.

We saw that the XAML serializer is asking if our types can be converted to the MarkupExtension class. Let’s give our CustomizableEngine class the ability to do that:

    [TypeConverter(typeof(CustomizableEngineTypeConverter))]
    public class CustomizableEngine : Engine

The implementation of the type converter is a pretty simple skeleton:

public class CustomizableEngineTypeConverter : TypeConverter
{
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        if (destinationType == typeof(MarkupExtension))
            return true;
        return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(MarkupExtension))
        {
            throw new NotImplementedException();
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
    
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (sourceType == typeof(MarkupExtension))
            return true;
        return base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        if (value is CustomizableEngineExtension)
        {
            throw new NotImplementedException();
        }
        return base.ConvertFrom(context, culture, value);
    }
}

Now we just need to know what’s a MarkupExtension and how to build one. Since this is well explained in MSDN there’s no need to detail it here, but to summarize, it’s a class that, by convention, ends in "Extension", and whose each instance can provide a value of another type when asked for it. The XAML serializer will create and initialize a MarkupExtension based on some standard curly braces based syntax.

So it really all boils down to creating our own MarkupExtension class for the CustomizableEngine class:

public class CustomizableEngineExtension : MarkupExtension
{
    public int Power { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return new CustomizableEngine() { PowerSetting = Power };
    }
}

Now we can fix the type converter class:

public class CustomizableEngineTypeConverter : TypeConverter
{
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        if (destinationType == typeof(MarkupExtension))
            return true;
        return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(MarkupExtension))
        {
            CustomizableEngine engine = (CustomizableEngine)value;
            CustomizableEngineExtension extension = new CustomizableEngineExtension();
            return extension;
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
    
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (sourceType == typeof(MarkupExtension))
            return true;
        return base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        if (value is CustomizableEngineExtension)
        {
            CustomizableEngineExtension extension = (CustomizableEngineExtension)value;
            return new CustomizableEngine() { PowerSetting = extension.Power };
        }
        return base.ConvertFrom(context, culture, value);
    }
}

Our XAML markup now looks like this:

<Robot Name="Tony Stark" Engine="{CustomizableEngine Power=0}" xmlns="https://ludovic.chabant.com/xaml" />

And we can change the "Power=0" bit to adjust the Power property of the engine.

MarkupExtensions are very handy to make the XAML markup shorter and more readable, but some people find it ugly when you start nesting them:

<Robot
    xmlns="https://ludovic.chabant.com/xaml"
    Name="Tony Stark"
    Engine="{CustomizableEngine Power=0}"
    Weapon="{MachinGun AmmoType={HollowPoint Diameter=7.62}, Model={SteyrAug Scope={LaserScope}}, ClipCount=10}"
    />

I personnally don’t mind too much (unless it starts looking ridiculous, which the previous example is getting pretty close). As a rule of thumb, you probably should not define a MarkupExtension for a type if:

  • The type "contains" another type: the MarkupExtension syntax is really for property initialization. If the type is a container, like a collection, a UI panel, a decorator or facade, or any other thing that "logically contains" another object, there’s no point in giving the ability to declare an instance of this type in one line, as there’s a good chance the contained type won’t be that concise.
  • The type has properties that are not "trivial": by trivial, I mean values that are either basic types (int, float, bool, string, etc.), or moderately simple structures. In that latter case, there might be a need for a small nested MarkupExtension, but it’s okay if there’s only a couple of those properties, and their types have only a couple of properties themselves.

Of course, as always in "should"-based sentences, there are exceptions. Most of the time, usage and design dictate this kind of decision. For example, your type might have several reference type properties, but what they represents means that in nominal cases, initialization is short and simple. The Binding class in WPF comes to mind: it’s a fairly complex class, but most of the time, you only set the "Path" property anyway.

Now you can start using XAML as your own serialization format! You might want to read a bit on attributes like ContentPropertyAttribute or DependsOnAttribute, too. They’re simple enough to understand, and will come in handy when you start mapping your object model to XAML.

The last thing to do is package all your XAML and resource files in an OpenXML package, and you will truly have a 21st century file format you can be proud of!