Custom Markup Extensions in XAML

A markup extension can be implemented to provide values for properties in an attribute usage, properties in a property element usage, or both. When used to provide an attribute value, the syntax that distinguishes a markup extension sequence to a XAML processor is the presence of the opening and closing curly braces ({ and }). The type of markup extension is then identified by the string token immediately following the opening curly brace.  When used in property element syntax, a markup extension is visually the same as any other element used to provide a property element value: a XAML element declaration that references the markup extension class as an element, enclosed within angle brackets (<>).  [msdn]

For both the general XAML language and WPF specific markup extensions, the behavior of each markup extension is identified to a XAML processor through a class called MarkupExtension (System.Windows.Markup), and provides an implementation of the ProvideValue method. This method on each extension provides the object that is returned when the markup extension is evaluated. The returned object is typically evaluated based on the various string tokens that are passed to the markup extension.

There’s not much ceremony to creating a markup extension.

using System;
using System.Windows;
using System.Windows.Markup;

namespace CustomMarkupForDateTime
{
    public class DateMarkup : MarkupExtension
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return DateTime.Now.ToShortDateString();
         }
    }
}

<Window x:Class="CustomMarkupForDateTime.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:custom="clr-namespace:CustomMarkupForDateTime"
        Title="MainWindow"
        Height="350"
        Width="525">
    <Grid>
        <TextBlock Text="{custom:DateMarkup}" />
    </Grid>
</Window>

Playing with the IserviceProvider

As the name suggest, it provides some services. Depending on the service you will call, you can have information about the types.  Here is the list of services provided.   Below example uses IProvideValueTarget service to get information about the target property name, type and the object owing that property.

using System;
using System.Windows;
using System.Windows.Markup;

namespace CustomMarkupForDateTime
{
    public class DateMarkup : MarkupExtension
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            var service = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
            string text = string.Format("Target Property Name : {1},{0} Type : {2}{0} Target Object Type : {3}",
                                        "\n", (service.TargetProperty as DependencyProperty).Name,
                                        (service.TargetProperty as DependencyProperty).PropertyType.Name,
                                        service.TargetObject.GetType().ToString());
            return text;
         }
    }
}

Markup Extension with Parameter

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Threading.Tasks;
using System.Windows.Markup;
using System.Reflection;
using System.Threading;

namespace CustomMarkupForDateTime
{
    public class DateMarkup : MarkupExtension
    {
        private DependencyProperty _property;
        private DependencyObject _object;

        [ConstructorArgument("DateTimeFormat")]
        public string DateTimeFormat { get; set; }

        public DateMarkup() { }
        public DateMarkup(string dateFormat)
        {
            DateTimeFormat = dateFormat;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            var service = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
            _property = service.TargetProperty as DependencyProperty;
            _object = service.TargetObject as DependencyObject;
            Timer _timer = new Timer(OnTimerCallback, this, 100, 100);            
            return DateTime.Now.ToString(DateTimeFormat);
        }
        private void OnTimerCallback(object state)
        {
            DateMarkup markup = (DateMarkup)state;
            if (!markup._object.Dispatcher.CheckAccess())
            {
                markup._object.Dispatcher.Invoke(() => markup._object.SetValue(_property, DateTime.Now.ToString(DateTimeFormat)));
            }
            else
            {
             markup._object.SetValue(_property, DateTime.Now.ToString(DateTimeFormat));
            }
        }
    }
}
<Window x:Class="CustomMarkupForDateTime.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:custom="clr-namespace:CustomMarkupForDateTime"
        Title="MainWindow"
        Height="350"
        Width="525">
    <Grid>
        <TextBlock Text="{custom:DateMarkup DateTimeFormat=dd/MMM/yyyy-hh:mm:ss}" />
    </Grid>
</Window>
Advertisement

One thought on “Custom Markup Extensions in XAML

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s