Using Custom Markup Extensions in Silverlight 5

13 Comments April 15, 2011


The first beta of Silverlight 5 was announced at MIX this week and is available for downloading. As such, I’ll be blogging about the new features in weeks to come. I’ll also be delivering sessions on Silverlight 5 at several upcoming conferences, including Microsoft TechDays in Belgium, Microsoft DevDays in the Netherlands, Devscovery in Redmond, WA, and Microsoft TechEd in Atlanta. If you plan to attend any of those conferences, I’d love to see you in my sessions!

One of the most exciting features that Silverlight 5 introduces – and one that has been a long time in coming to the platform – is custom markup extensions. My fellow coconspirator and Silverlight MVP Jeremy Likness presented a custom markup extension that uses MEF to satisfy imports on object instances declared in XAML. I’d like to present a custom markup extension of my own – one that retrieves RESX localization resources and simplifies the task of adding localization support to Silverlight applications.

In the past, RESX-based localization was usually performed in Silverlight XAML with the help of the built-in {Binding} markup extension. To demonstrate, the following example declares an instance of the ResourceManager wrapper class named Resources (which is generated by Visual Studio from Resources.resx), assigns the Resources instance to the DataContext property of a TextBlock, and uses a data-binding expression to set the TextBlock’s Text property equal to the Greeting property of the Resources instance:

 

<Grid>

  <Grid.Resources>

    <local:Resources x:Key="Localize" />

  </Grid.Resources>

  <TextBlock Text="{Binding Greeting}" DataContext="{StaticResource Localize}" />

</Grid>

 

It works, but it makes you wonder why you have to resort to data binding to make localization work when localization is such a common task in Silverlight applications.

You can make this work a little more cleanly by writing a custom markup extension. Such an extension might be applied this way:

 

<Grid>

  <TextBlock

    Text="{local:Resx ResxKey=Greeting, ResxType=Resources, Default=Welcome}" />

</Grid>

 

In this example, Resx is the custom markup extension, ResxKey identifies the localization resource to be loaded, ResxType identifies the ResourceManager wrapper class that provides access to that resource, and Default is an optional default value that’s used if the specified localization resource doesn’t exist or can’t be retrieved. Better, is it not? And it’s just one of a million different applications for custom markup extensions.

Implementing a custom markup extension is, in most cases, relatively straightforward. You begin by deriving from Silverlight 5’s new System.Windows.Markup.MarkupExtension class. Then you override ProvideValue in the derived class and return the value generated by the markup extension. My ResxExtension class is implemented this way:

 

public class ResxExtension : MarkupExtension

{

    public string ResxType { get; set; }

    public string ResxKey { get; set; }

    public object Default { get; set; }

 

    public override object ProvideValue(IServiceProvider serviceProvider)

    {

        if (!String.IsNullOrEmpty(ResxType) && !String.IsNullOrEmpty(ResxKey))

        {

            try

            {

                // Create a strongly typed resource manager instance

                object resman = Activator.CreateInstance(Type.GetType(ResxType));

 

                // Get the value of the specified property

                PropertyInfo pi = resman.GetType().GetProperty(ResxKey);

                return pi.GetValue(resman, null);

            }

            catch (Exception)

            {

                // Purposely do nothing here to allow the call to fall through

            }

        }

 

        // If we make it to here, return the default value (if specified) or,

        // as a last resort, the key name

        if (Default != null)

            return Default;

        else

            return ResxKey;

    }

}

 

The three public properties – ResxType, ResxKey, and Default – define the named parameters that the markup extension accepts. The XAML parser automatically initializes these properties with the values provided in the markup. My ProvideValue override uses reflection to create an instance of the ResourceManager wrapper class identified by ResxType, and then uses reflection again to retrieve the value of the property whose name is stored in the markup extension’s ResxKey property. If anything goes wrong, ProvideValue returns the default value specified with the Default parameter, or the value of ResxKey if there is no Default parameter.

If you’d like to see ResxExtension in action, you can download a zip file containing the Visual Studio solution. When you run the app for the first time, you’ll see this:

ResxExtension1

But if you open App.xaml.cs and uncomment line of code that sets the culture to French, you’ll see this instead:

ResxExtension2

The welcome text, the URI of the flag image, and the width of the flag image come from RESX files named Resources.resx, Resources.fr.resx, Resources.es.resx, and Resources.de.resx. Open these RESX files in Visual Studio and you’ll see exactly how the individual resources are defined. As for applying the localization resources, that happens in MainPage.xaml:

 

<TextBlock Text="{local:Resx ResxKey=Greeting,

  ResxType=CustomMarkupExtensionDemo.Localization.Resources, Default='Nice Try!'}"

  Foreground="LightYellow" FontSize="72" FontWeight="Bold"

  HorizontalAlignment="Center" VerticalAlignment="Center">

  <TextBlock.Effect>

    <DropShadowEffect BlurRadius="12" ShadowDepth="12" Opacity="0.5" />

  </TextBlock.Effect>

</TextBlock>

<Image Source="{local:Resx ResxKey=FlagUri,

  ResxType=CustomMarkupExtensionDemo.Localization.Resources}"

  Width="{local:Resx ResxKey=FlagWidth,

  ResxType=CustomMarkupExtensionDemo.Localization.Resources}" />

 

Examine the source and see what kinds of cool custom markup extensions you can come up with. And don’t forget that you’ll need to download and install the Silverlight 5 Beta to run the example.


13 Comments

  • Gravatar Image
    Silverlight 5 &ndash; Custom Markup Extensions &laquo; xamlgeek April 20, 2011 8:12 AM

    PingBack from http://xamlgeek.net/2011/04/20/silverlight-5-custom-markup-extensions/

  • Gravatar Image
    Amyo Kabir April 23, 2011 10:35 AM

    Great Silverlight 5 feature.
    Thanks for your post.

  • Gravatar Image
    stimentic April 26, 2011 7:47 AM

    Does not work at design time

  • Gravatar Image
    Braulio May 23, 2011 1:14 AM

    Jeff great sample, just something to enhance, Visual Studio Design Time + Blendability.

    In Visual Studio Design mode nothing is shown (nor text and flag).

    In Blend the flag is missing.

  • Gravatar Image
    ben June 9, 2011 11:03 AM

    Hello

    How do I use your method to do a dynamic translation?

    Thanks

  • Gravatar Image
    Liero July 26, 2011 9:07 AM

    override ToString method for design time support.

  • Gravatar Image
    Community Blogs October 17, 2011 5:26 PM

    This article is sponsored by Telerik RadControls for Silverlight . Related content from the sponsor:

  • Gravatar Image
    Water Damage Restoration October 25, 2011 10:29 PM

    I have used telerik's rad controls for SL4. Their reportviewer works great with VisStudio 2010. 988Any issues with SL5.

  • Gravatar Image
    Zach May 11, 2012 2:44 PM

    A bit old now but, is there anyway to trigger the MarkupExtensions to re-evaluate, it looks like they only evaluate once at initial startup. So in your above example, if dynamically the properties were update to provide new locale settings (like the user picked a new language from a drop down) is it possible to trigger the UI to re-evaluate and thus get the new language values.

  • Gravatar Image
    1trick May 24, 2012 7:01 AM

    There is one thing missing here and that's not at all obvious from the code: the project must be edited to specify the supported cultures so that the satellite assemblies get built. I realize this has nothing to do with custom markup extensions but since the topic is applied localization (and I spent an hour figuring it out), I thought it worth mentioning!

  • Gravatar Image
    Ryan Watson February 25, 2013 9:34 AM

    One of the most helping post for Silverlight develoeprs..

  • Gravatar Image
    mauro August 26, 2013 1:09 PM

    "download a zip file" not work

  • Gravatar Image
    info August 27, 2013 8:33 AM

    download a zip file not work

Have a Comment?

Archives

Tags

Blogs