TOP

Simplifying Text Resources in Silverlight

Here is how you can simplify the way you work with resources in Silverlight 4. The normal procedure to bind against resources is writing an binding statement in the .Content or .Text property of your elements. I will explain how you can use a dependency property to extend your Silverlight controls with an Resource.Key (ResourceKey) property and one for Resource.Tooltip.

The sample also includes a way to enable live language switching in your application, enabling the user to see language-change effects in real-time without restarting the application.

Markup differences

The binding syntax in XAML is somewhat awkward and it uses a lot of bracket ({  }). I wanted to avoid this and make it cleaner and more understandable. Have a look at this example on how you normally bind the content of a button to a static resource, which should be your generated resources class that the .resx file generates.

Content="{Binding About, Source={StaticResource Resources}}"

What this does is bind against the resource property About. You register the Resources property in your App.xaml file like this.

<resources:ApplicationStrings x:Key="Resources" />

What we want to do instead, is a syntax like the following example.

cirium:Resource.Key="About"

This is obviously much cleaner and communicates better than the first example. Resource.Key is a dependency property defines in the Cirium Application Framework. You don’t need to use Cirium to achieve this behavior, I have included the source-code below.

SimplifyResources_source

What’s the catch?

Obviously there are some small issues. The current implementation does not support resource text with format parameters (eg. “Welcome {0}”), I’m considering adding this in a future update.

Important: For this behavior and sample to work, you are required to modify the generated code-behind for your .resx file. You need to modify the constructor of your class and make it public, by default it becomes internal. You also need to modify the class to be partial, for the auto-magic update of all bindings to work properly. If you don’t do this, you can’t register your resource directly in the App.xaml.

You also need to manually add support for any controls I have not added in the dependency property class, currently it supports Button, TextBlock, TextBox and CheckBox. If you use any Silverlight Toolkit/SDK controls, you need to modify to specify which dependency property to bind against.

How does it work?

There is a simple class named Resource that handles the binding of controls with your resources. It’s important to notice that we use binding of the property instead of just setting the content directly from the resource. You might want to reconsider changing this if your application is not going to support multiple languages, but mine required to support multiple languages and be able to dynamically and quickly change between them without restarting the application or reloading the views.

There is one single requirement and that is that you register all your resources in the applications static resources collection under the name “Resources”. You can obviously change this in the class file as you see fit for your own project. Here is a screenshot that shows the sample application running, with localized content and tooltip.

SimplifyResources_screenshot

Enabling your project for localization

When I first starting localizing my Silverlight application I was surprised how hard it was to figure it all out. It’s not enough to simply add the different .resx files to your project, you need to manually (even in Silverlight 4 with Visual Studio 2010) edit your project file with the proper languages you want to support. Unload your project, edit the content of it, locate the SupportedCulture XML element and add the languages you want to support. This example is for English and Norwegian.

<SupportedCultures>en-US;nb-NO</SupportedCultures>

To add multiple languages to your application, start by adding one .resx file to your project if you don’t have one. Fill it out with some values. Copy the .resx file to the same location in your project, and rename by adding the language (culture) code before the .resx file-ending. E.g. ApplicationStrings.nb-NO.resx or ApplicationStrings.en-US.resx.

Tip: You can localize the Out-of-browser settings by making multiple OUtOfBrowserSettings.xml files, just name them in the same way as your .resx files.

Tip: You can disable the code generation for your extra languages, Visual Studio won’t generate any content in your code-behind when you have multiple languages, only for the main language, so disabling the code generation in the extra .resx files will avoid generating empty files in your project.

Source Code

Below is the source code, it’s a simple working application in Silverlight 4, configured to run out-of-browser. It’s important to notice that you need to manually configure SupportedCultures if you need more languages than what’s in the sample.

Download the SimplifyResources sample project.

Here is the full source-code of the Resource.cs:

namespace SimplifyResources

{

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Data;

    using System;

    public static class Resource

    {

        /// <summary>

        /// A dependency property for attaching a resource key to the element.

        /// </summary>

        public static readonly DependencyProperty KeyProperty =

            DependencyProperty.RegisterAttached(

                "Key",

                typeof(string),

                typeof(string),

                new PropertyMetadata(OnKeyChanged)

                );

        public static string GetKey(DependencyObject obj)

        {

            return (string)obj.GetValue(KeyProperty);

        }

        public static void SetKey(DependencyObject obj, string value)

        {

            obj.SetValue(KeyProperty, value);

        }

        /// <summary>

        /// A dependency property for attaching a resource key to the element used in the tooltip.

        /// </summary>

        public static readonly DependencyProperty TooltipProperty =

            DependencyProperty.RegisterAttached(

                "Tooltip",

                typeof(string),

                typeof(string),

                new PropertyMetadata(OnTooltipChanged)

                );

        public static string GetTooltip(DependencyObject obj)

        {

            return (string)obj.GetValue(TooltipProperty);

        }

        public static void SetTooltip(DependencyObject obj, string value)

        {

            obj.SetValue(TooltipProperty, value);

        }

        private static void OnKeyChanged(DependencyObject targetLocation, DependencyPropertyChangedEventArgs args)

        {

            SetupBinding(targetLocation, args, false);

        }

        private static void OnTooltipChanged(DependencyObject targetLocation, DependencyPropertyChangedEventArgs args)

        {

            SetupBinding(targetLocation, args, true);

        }

        private static void SetupBinding(DependencyObject targetLocation, DependencyPropertyChangedEventArgs args, bool isTooltip)

        {

            if (args.OldValue == args.NewValue) return;

            FrameworkElement element = targetLocation as FrameworkElement;

            if (element == null)

            {

                throw new Exception("Resource.Key can only be set on framework elements.");

            }

            // Load the resources source from the application’s StaticResources collection.

            // This will only work if you have added it to your App.xaml.

            var dataSource = Application.Current.Resources["Resources"];

            Binding binding = new Binding((string)args.NewValue);

            binding.Source = dataSource;

            if (isTooltip)

            {

                element.SetBinding(ToolTipService.ToolTipProperty, binding);

            }

            else

            {

                element.SetBinding(FindDependencyProperty(element), binding);

            }

        }

        private static DependencyProperty FindDependencyProperty(DependencyObject control)

        {

            DependencyProperty property = null;

            if (control is Button)

            {

                property = Button.ContentProperty;

            }

            else if (control is TextBlock)

            {

                property = TextBlock.TextProperty;

            }

            else if (control is TextBox)

            {

                property = TextBox.TextProperty;

            }

            else if (control is CheckBox)

            {

                property = CheckBox.ContentProperty;

            }

            return property;

        }

    }

}

Notes

If you get exception similar to this one, you need to modify the generated resources class.

No matching constructor found on type ‘SimplifyResources.Assets.Resources.ApplicationStrings’. [Line: 9 Position: 46]

3 comments. Leave a Reply

  1. Ganesh

    Hi,
    Thanks for a wonderful code. I am trying the code in vb it works fine on load but when clicked switch language it does not work, what could be the reason for it, but your code works. any idea ?

    Thanks & Regards
    Ganesh

  2. Ganesh

    Hi,
    Thanks for your code, i had ignored this line “When I first starting localizing my Silverlight application I was surprised how hard it was to figure it all out. It’s not enough to simply add the different .resx files to your project, you need to manually (even in Silverlight 4 with Visual Studio 2010) edit your project file with the proper languages you want to support. Unload your project, edit the content of it, locate the SupportedCulture XML element and add the languages you want to support. This example is for English and Norwegian.

    en-US;nb-NO”

    It really works.
    Thanks & Regards
    Ganesh

  3. Elton Saulsberry

    I found this very useful, except for the partial class / public constructor. The problem of course is every time your resource file changes it regenerates the code and overwrites your changes. Instead, I’ve marked the resource file for no code generation, then generated the static resource wrapper using a T4 template adapted from http://blog.baltrinic.com/software-development/dotnet/t4-template-replace-resxfilecodegenerator

    Thanks again. It’s made assigning resources much less tedious.

Leave a Reply

Your email is never published nor shared.

You may use these HTML tags and attributes:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>