In a previous post I wrote about how cool XAML Markup Extensions were. As I was writing that post, it occurred to me that I should put together a tutorial on writing one from scratch. So here it is...

Why Are Markup Extensions Important?

All properties in XAML are set using strings - even when the property in question is not a string type.

<Label FontSize="Micro" TextColor="Red" Text="Hello World" />  

Neither FontSize nor TextColor are typed as strings, but yet I can set them equal to strings in XAML.

That's accomplished because there are several type converters for individual properties that get run automatically when the XAML is being parsed. The converter is able to take the string representation of Red and turn it into the Color structure's Red value.

But what about when there isn't a built-in converter for a property... one that takes a string and moves it to the correct data type? That's where Markup Extensions come in handy, and important.

For example, data binding - or the process of looking up a value from a view model, and then potentially setting that value back to a view model's property - is accomplished through a Markup Extension.

So these are important, and it pays to understand how they work.

What is a Markup Extension

But what exactly is a Markup Extension?

The quick answer ... it's anything with a curly brace being set to a property in XAML. Something like a binding:

<Entry Text="{Binding FirstName}" />  

So the Binding keyword here is the Markup Extension.

And there are several other built-in markup extensions in the Xamarin.Forms flavor of XAML.

  • x:Array
  • x:Null
  • x:Reference
  • x:Static
  • x:Type
  • ConstraintExpression
  • DynamicResource
  • StaticResource

The ones that start with x: are all native to the XAML specification, the others were introduced either with WPF or Xamarin.Forms.

OK great - but I still haven't answered the question - what is a markup extension?

A markup extension provides a means to run some code before setting the value on a property in XAML and then return a value with an appropriate type for that property.

How Markup Extensions Work

Markup Extensions adhere to an interface - IMarkupExtension (I know, you would never have guess that)...

The IMarkupExtension only has one member to implement:

public object ProvideValue(IServiceProvider serviceProvider)  

When the XAML is being parsed, and the compiler comes across a Markup Extension, it is going to invoke that function before setting the property the Markup Extension is on.

By default, there is no input into the ProvideValue function, in other words from the example above, the IServiceProvider does not bring in the Micro or Red. (And in fact, the IServiceProvider input comes as a XamlServiceProvider, which does not have any public APIs to get at, but it's used internally by the XAML compiler for things like ... data binding).

That means to get values into the IMarkupExtension class we're going to have to use properties.

The good news, is that the properties are the same 'ol properties that we've been using since the dawn of C#!

So, let's build a Markup Extension!

Building a Markup Extension

For this demo, I am going to build a Markup Extension that interprets words as thicknesses - so I can set things like Margin and Padding as FatLeft or SkinnyTop.

So my class name will be FriendlyThickness and it'll have a definition like this:

public class FriendlyThickness : IMarkupExtension  

It's going to need to take in an input value ... the FatLeft or SkinnyTop, so I need to make that a property.

public string FriendlyName { get; set; }  

Now there's an attribute in XAML that allows one to specify that a property be the "default" property... in other words, it's the one whose value gets set without having to specify it by name. And that's called ContentProperty. (And this applies to anything you would build that can be used in XAML.)
I want to use that to make calling this markup extension easier.

It needs to be set at the class level, so the class definition now looks like this:

[ContentProperty("FriendlyName")]
public class FriendlyThickness : IMarkupExtension  

Then on to the implementation of the ProvideValue. In this case, the implementation will be pretty trivial, but it should illustrate the point of what you can do.

public object ProvideValue(IServiceProvider serviceProvider)  
{
    switch (FriendlyName?.ToLower())
    {
        case "fatleft":
            return new Thickness(50, 0, 0, 0);
        case "fatright":
            return new Thickness(0, 0, 50, 0);
        case "skinnytop":
            return new Thickness(0, 10, 0, 0);
        case "skinnybottom":
            return new Thickness(0, 0, 0, 10);
        default:
            return new Thickness();
    }
}

Possible values that could come in are FatLeft, FatRight, SkinnyTop, SkinnyBottom - and of course something else or nothing and that would return the default value.

Consuming it is pretty straight forward. First make sure to reference the namespace:

xmlns:local="clr-namespace:MarkupExtensions"  

Then use it!

<Label Text="Big Left Margin" Margin="{local:FriendlyThickness FatLeft}" />  

Or to specify the FriendlyName property explicitly:

<Label Text="Left Margin" Margin="{local:FriendlyThickness FriendlyName=FatLeft}" />  

And that's all there is to building and using a Markup Extension! The entire source can be found here.

Wrapping Up

Markup Extensions provides a very powerful means to set properties on objects when there is not a built-in converter to move a string representation of the value to the correct type.

A Markup Extension gives the developer a chance to run some code before a value is set on a property, thus all manner of manipulation can be performed - like data binding!

You can also create your own custom Markup Extensions by implementing the IMarkupExtension interface, just remember that you need to create properties to accept any input.