(Lviv community of .NET developers)

Markup Extensions

October 20, 2007 02:36 by rat

First of all, what is this misterious thing, MarkupExtension? As it follows from it's name, this is some kind of the extension for the XAML markup syntax, and in our case that is an the extension that allows user to shorten or simplify some operations in XAML, or at least provide some non-string data in case TypeConverter can not be used. One of the most common examples of the MarkupExtension is a shortened declaration of a data binding, like {Binding Path}. {StaticResource key} is also widely used, and that is also a MarkupExtension. Both of them can be declared in element syntax, but that is not widely used because the amount of XAML needed in such case is a bit larger, so I will use attribute syntax in my samples.


 In general, MarkupExtension is an object that is created by the XAML reader when it finds some construction like {ExtensionName Param1, param2,... } in object's attributes in XAML (or if we use the element syntax for the property values, than xaml reader just searches for the class with the specified name, but with an Extension word at the end). For example, such property value like Property="{Binding Value}" in XAML will be treated like this:

BindingExtension extension = new BindingExtension( Value );
someobj.Property = extension.ProvideValue( ... some service provider here... );

Here is a short sample of a simple markup extension:

namespace sample  
{  
    public class TestExtension : MarkupExtension  
    {  
        private string m_text;  
 
        public TestExtension( string text )  
        {  
            m_text = text;  
        }  
 
        public override object ProvideValue( IServiceProvider serviceProvider )  
        {  
            return m_text;  
        }  
    }  
An extension from this sample can be used in this way:
<Window xmlns:local="clr-namespace:sample">  
    <TextBlock Text="{local:Test text_to_print}"/>  
Window> 

As you could notice, parameters, that are passed in XAML after the name of the markup extension, are passed as a parameters to the most suitable constructor of the extension class. Also you can assign extension's properties in a way like this: {Extension Property1=data data data, Property2=data1 data1 data1}. All constructor parameters and property setters are separated with commas, so the values for them can contain spaces.

There is one case when the usage of the markup extension with element syntax can be extremely usefull. That is a case when extension should accept unknown count of the data of unknown type, like x:Array extension does.
Here is how it can be used:

<x:Array Type="typeName">  
  <arrayObject1/> 
  <arrayObject2/> 
  ...  
x:Array> 

You can add support for such functionality into your own markup extension by supporting IAddChild interface - XAML reader understands that as an ability to contain sub-elements inside the extension object.

So now you know what is a MarkupExtension (you can also look for information about it in MSDN article). But there are two more things, related to markup extensions, that you do not know about yet: MarkupExtensionReturnType attribute and the list of the services that are provided by the service provider that is passed in a ProvideValue method.

MarkupExtensionReturnType

MarkupExtensionReturnType attribute is used to determine the type of the value that is supposed to be returned by the extension. This is needed just for compilation-time checks and does not have any influence on the runtime.

Here is an example of the usage of the attribute:

namespace sample  
{  
    [MarkupExtensionReturnType(typeof(int))]  
    public class TestExtension : MarkupExtension  
    {  
        private string m_text;  
 
        public TestExtension( string text )  
        {  
            m_text = text;  
        }  
 
        public override object ProvideValue( IServiceProvider serviceProvider )  
        {  
            return int.Parse(m_text);  
        }  
    }  
Such extension can be used only the property accepts integers as a value.

Services

By default, the following services are supported: IProvideValueTarget, IXamlTypeResolver, IUriContext and IFreezeFreezables.
All of them can be accessed via serviceProvider in a following way: IProvideValueTarget target = (IProvideValueTarget)serviceProvider.GetService( typeof(IProvideValueTarget) );

Here are some details on every of the supported services:

  • IProvideValueTarget
    Used to provide the information on the target object and target property for the markup extension.
    Example:
    IProvideValueTarget target = (IProvideValueTarget)serviceProvider.GetService( typeof(IProvideValueTarget) );  
    // Outputs an object, that own the property, the result of ProvideValue method is about to be assigned to.  
    Debug.WriteLine( target.TargetObject, "Target object" );   
    // Outputs the description of the target property. PropertyInfo class is used to describe the property.  
    Debug.WriteLine( target.TargetProperty, "Target property" ); 
  • IXamlTypeResolver
    Such type resolver is used to find the desired type by it's name. It is used in x:Type extension to resolve types by their names, like Brush or local:MyType.
    Example:
    IXamlTypeResolver service = serviceProvider.GetService( typeof( IXamlTypeResolver ) ) as IXamlTypeResolver;  
    Type type = service.Resolve( "Brush" ); // In this case Brush type will be returned. 
  • IUriContext
    Used to determine the URI of the object, in context of which an extension is used. In most cases that is a path to the XAML file, where the particular instance of the extension is used. For examle it can be pack://application:,,,/App1;component/window1.xaml
    Example:
    IUriContext target = (IUriContext)serviceProvider.GetService( typeof( IUriContext ) );  
    Debug.WriteLine( target.BaseUri ); 

In the next article I will provide some usefull markup extensions that can make the life of the WPF developer a bit easier.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: ,
Categories: WinFx
Actions: Permalink | Comments (0) | RSSRSS comment feed

Add comment


(Will show your Gravatar icon)