Creating your own markup extensions in WPF XAML

If you do not know what markup extensions are in WPF and XAML then you have to first go through this MSDN link to understand what they are, here in this post i will be explaining how to create and implement your own Markup extensions.

To define a markup extension you have to first derive your class from the System.Windows.Markup.MarkupExtension abstract base class. This abstract class has a abstract method defined inside it abstract object ProvideValue(IServiceProvider serviceProvider) implement this this abstract class in your derived class as shown below.


C#

public class CultureKeyResolver :System.Windows.Markup.MarkupExtension 
{
     public override object ProvideValue(IServiceProvider serviceProvider)
    {
       throw new NotImplementedException();
    }
}


Now if you see that we get a method with the name  public override object ProvideValue(IServiceProvider serviceProvider) , Now this is the method that is always called when the markup extensions are called for in XAML. This method should always return a VALUE which is depending on the context.

Now let me tell you the context of this markup extension, here we will return a string VALUE based on a KEY passed , the resolution will happen using a method within this class.

Lets add a dictionary to this class that will hold the KEY VALUE pairs.

C#

public class CultureKeyResolver :System.Windows.Markup.MarkupExtension
{
    //The dictionary holds the key value pair of culture keys
    static Dictionary<stringstring> _lookTable = new Dictionary<stringstring>();


    //This method will be called when the extension is called.
          public override object ProvideValue(IServiceProvider serviceProvider)
    {
       throw new NotImplementedException();
    }
}


This dictionary will hold the KEY VALUE pair, and the the key will be matched with the KEY that is passed to this markup extension and the  VALUE if exists will be returned or a default VALUE will be returned.

Lets add two constructors to this class , one will be a default constructor and the other will be take a string parameter which is the KEY , Also lets add a static constructor that will add entries to the dictionary.

C#

public class CultureKeyResolver :System.Windows.Markup.MarkupExtension
{
    //The dictionary holds the key value pair of culture keys
    static Dictionary<stringstring> _lookTable = new Dictionary<stringstring>();

    //Static constructor initializes the dictionary
    static CultureKeyResolver()
    {
       _lookTable.Add("en-us""Click Me");
       _lookTable.Add("Albanian""Kliko Mua");
       _lookTable.Add("Dutch""Klik op mij");
    }

    //Local field and property
    private string cultureKey = string.Empty;
    public string CultureKey
    {
       get { return cultureKey; }
       set { cultureKey = value; }
    }
    
    //Default constructor
    public CultureKeyResolver() 
    {
    }

    //This is a constructor with a parameter as the input key
    public CultureKeyResolver(string key)
    {
       cultureKey = key;
    }

          //This method will be called when the extension is called.
         public override object ProvideValue(IServiceProvider serviceProvider)
    {
       return this.GetValue(cultureKey);
    }
}



Now we will add a method to this class that will resolve this KEY for a VALUE, if that KEYexists in the dictionary the corresponding VALUE will be returned else a default VALUE will be returned
the method will look something like this.

C#

public string GetValue(string culture)
{
    //Make a check for the key
    if (_lookTable.ContainsKey(culture))
    {
       //If exists then return the value
       return _lookTable[culture];
    }
    //Return default value
    return _lookTable["en-us"];
}



Go Ahead and add this method into the class, Your class will look something like this now

C#


public class CultureKeyResolver :System.Windows.Markup.MarkupExtension
{
    //The dictionary holds the key value pair of culture keys
    static Dictionary<string, string> _lookTable = new Dictionary<string, string>();

    //Static constructor initializes the dictionary
    static CultureKeyResolver()
    {
       _lookTable.Add("en-us", "Click Me");
       _lookTable.Add("Albanian", "Kliko Mua");
       _lookTable.Add("Dutch", "Klik op mij");
    }

    //Local field and property
    private string cultureKey = string.Empty;
    public string CultureKey
    {
       get { return cultureKey; }
       set { cultureKey = value; }
    }
    
    //Default constructor
    public CultureKeyResolver() 
    {
    }

    //This is a constructor with a parameter as the input key
    public CultureKeyResolver(string key)
    {
       cultureKey = key;
    }

    public string GetValue(string culture)
    {
       //Make a check for the key
       if (_lookTable.ContainsKey(culture))
       {
          //If exists then return the value
          return _lookTable[culture];
       }
       //Return default value
       return _lookTable["en-us"];
    }

          //This method will be called when the extension is called.
          public override object ProvideValue(IServiceProvider serviceProvider)
    {
       return this.GetValue(cultureKey);
    }
}


Your Markup extension class is ready now, lets call this in our XAML code.

XAML


<Window x:Class="WpfApplication1.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:MyApp="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    
    <StackPanel>

        <Button Content="{MyApp:CultureKeyResolver Dutch}" Height="30" Width="100"></Button>
       
    </StackPanel>
</Window


Now in the above XAML code we see that we have made a reference to our Application namespace and given the alias as MyApp this is also used to call our markup extension as
Content="{MyApp:CultureKeyResolver Dutch}" Here if we look the KEY is passed as a value to the constructor , anything that follows the class name separated by space will be passed as a input to the constructor. For example if the constructor takes two or more parameters then your syntax should look something like this Content="{MyApp:CultureKeyResolver Dutch en-us}" here Dutch and en-us will be the two parameter values any value separated by space will be considered.

If you are wondering how it works? Internally this is how it is going to call your markup extension. 

if(CultureKeyResolver is MarkupExtension)
{
   MarkupExtension _extension=new CultureKeyResolver();
   object value= _extension.ProvideValue(argument);
   currentControl.Property=value
}   

The KEY can also be passed as a Property value to the Markup Extension. You can pass it the following way

XAML

<Window x:Class="WpfApplication1.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:MyApp="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">   

<StackPanel>

<Button Content="{MyApp:CultureKeyResolver Dutch,  CultureKey=en-us}" Height="30" Width="100"></Button>

</StackPanel>

</Window


With that , you can see that now we have a property name coming up in yor XAML IDE , this is assigned a value.

This is how you implement your own markup extension in WPF XAML.


1 comment:

  1. This blog post hits the nail on the head when it comes to the importance of a well-designed website for non-profits. A compelling online presence can make a huge difference in fundraising and awareness. I've seen firsthand how an effective website can help our organization reach a broader audience. Great insights! Please visit our website:- non profit website design

    ReplyDelete