Click here to Skip to main content
12,399,799 members (48,255 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

7.5K views
4 downloads
2 bookmarked
Posted

Adventures while building a Silverlight Enterprise application part #4

, 18 Aug 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
Another week has gone. Here we are with part 4 of our adventure. Last time we dough into the ins and outs of using multiple XAP files.

Another week has gone. Here we are with part 4 of our adventure. Last time we dough into the ins and outs of using multiple XAP files. Thanks again to dsoltesz for some constructive comments on that article.

This time we want to look into something I've been struggling with for about a day, last week. It's localizing our application. As you may remember from the first post, one of the requirements is that the application has to be available in both English and Dutch. "Well, that is easy. Just go to silverlight.net and you'll find a very nice instructional video on this", I hear you say. That was our initial thought as well and it proved to be true...

...until, that is, we tried to apply this to a datagrid. I'll describe what happened by using the car application from some of my other articles.

The default implementation

As documented by many, we did the following to localize our application:

  • Add a folder named "Resources" to our projects
  • Add a file named "Strings.resx" and add any strings for the neutral culture (in our case nl-NL for dutch in the Netherlands)
  • Add a file for each culture you want to support (in our case only "Strings.en.resx") with all the same strings
  • Add a local resource to each UserControl for the generated class:
<UserControl.Resources>
<cardemo:Engines x:Key="Engines" />
<local:Strings x:Key="LocalStrings" />
</UserControl.Resources>
  • Bind each string to display to an entry in the resource:
<data:DataGridTextColumn Binding="{Binding Brand}"
    Header="{Binding brandLabel, Source={StaticResource LocalStrings}}">
</data:DataGridTextColumn>

And Bob's your uncle. For any control, this works great, but not for the datagrid. Run the code and you'll get the following exception:

System.Windows.Markup.XamlParseException occurred Message=
    "AG_E_PARSER_BAD_TYPE [Line: 11 Position: 46]" LineNumber=11 
    LinePosition=46 StackTrace: at System.Windows.Application.LoadComponent(
    Object component, Uri resourceLocator) at ComboLookup.Page.InitializeComponent() at ComboLookup.Page..ctor()

First thing I did was Google around to find out if someone already solved this problem. I found out this problem was introduced in the RTW release and I found some forum treads on silverlight.net, discussing this problem. I even found some solutions, but I was not very happy with those. One solution ended up using some reflection code inside a LINQ statement to add all the string entries to the local Resources instance (which could end up being a performance issue in applications with larger resource files). Another solution involved building a custom mechanism for filling up the headers of the datagrid, which seems like a maintenance nightmare to me.

So I figured I would have to come up with some better solution. First I started digging to find out what was causing the problem. I used Reflector to dig into the code that is actually executed and found out that the problem is actually deep down in the core of the Silverlight .NET Framework, where the XAML is actually parsed, and you can't get to that code. This struck me as odd, as you would expect the XAML parser to be completely generic, but still there is this specific case for a specific control that goes wrong.

Based on this fact, I concluded that a fix would not be lying in the controls code, and in fact it would not be possible for me to fix the underlying problem. This meant I had to find a decent workaround for this problem, that would fit our application.

So it was back to Google again and time to study the solutions of others, to get some inspiration for a good fix. In the end, I wouldn't mind to add the strings to the local Resources instance, as this would result in nice XAML and would work the most intuitive. The problem was that I didn't like the way people were getting to the strings in the resource file. There had to be a better way, then to actually use reflection to get each property we need.

I played around with the ResourceManager class and read some documentation, with the following code as a result:

private void LoadLocalStrings()
{
ResourceManager manager = new ResourceManager("ComboLookup.Resources.Strings",
    Assembly.GetExecutingAssembly());
ResourceSet resourceSet = manager.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
IDictionaryEnumerator resourceEnum = resourceSet.GetEnumerator();

while (resourceEnum.MoveNext())
{
Resources.Add(resourceEnum.Key.ToString(), resourceEnum.Value);
}
}

This method is called in the constructor BEFORE InitializeComponents().

What happens is that a resource manager with the specified base name ("ComboLookup.Resources.Strings") is looked for in the assembly you specify. The base name has to be the fully qualified name for the instance of the resource class.

It should be noted that if you have resources defined in your XAML, they will override the earlier loaded resources. If you need to load other resources besides the localized strings, you should do so from code, by adding them to the Resources instance.

The XAML for using localized strings looks like this:

<data:DataGridTextColumn Binding="{Binding Brand}" Header="{StaticResource brandLabel}">
</data:DataGridTextColumn>

Note that you can now directly access a resource with the required string. To prevent us from implementing this in every single module we have to build, we defined a base class that solves this for us. All that has changed in this method is that we specify a different assembly, trough a property that is overridden in the derived class. Same goes for the name of the resource manager.

I hope this saves you all a lot of troubles. If you have any questions or comments, please leave them below. I always enjoy reading and answering them.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

mrjvdveen
Software Developer (Senior) CTB Personele Systemen
Netherlands Netherlands
Since early 2001 I've been working full time as a software developer and since 2004 I've been working mostly with Microsoft technology.
I started out as a product developer, but after a few years I switched to a project company where my roles ranged from developer up to consultant and from team lead and coach to manager.
Eventually I switched jobs and focused on the consultant part but now I'm back where I started out and I'm building a product once again.

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160721.1 | Last Updated 18 Aug 2009
Article Copyright 2009 by mrjvdveen
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid