Using Custom Markup Extensions in Silverlight 5

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:

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

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.

Jeff Prosise

View Comments

  • 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.

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

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

  • 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.

  • 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!

Recent Posts

8-Step AWS to Microsoft Azure Migration Strategy

Microsoft Azure and Amazon Web Services (AWS) are two of the most popular cloud platforms.…

6 days ago

How to Navigate Azure Governance

 Cloud management is difficult to do manually, especially if you work with multiple cloud…

2 weeks ago

Why Azure’s Scalability is Your Key to Business Growth & Efficiency

Azure’s scalable infrastructure is often cited as one of the primary reasons why it's the…

4 weeks ago

Unlocking the Power of AI in your Software Development Life Cycle (SDLC)

https://www.youtube.com/watch?v=wDzCN0d8SeA Watch our "Unlocking the Power of AI in your Software Development Life Cycle (SDLC)"…

1 month ago

The Role of FinOps in Accelerating Business Innovation

FinOps is a strategic approach to managing cloud costs. It combines financial management best practices…

2 months ago

Azure Kubernetes Security Best Practices

Using Kubernetes with Azure combines the power of Kubernetes container orchestration and the cloud capabilities…

2 months ago