Skip to content


Localising WPF Applications Using RESX Files and Standard Data Binding (Without a MarkupExtension)

Introduction

Localisation is something everyone should really care about. Creating your applications, or websites, in a manner that can be easily localised for different languages and cultures is not only “good practice”, but it may also provide additional opportunities.

Luckily the .NET framework, and WPF, has rich support for localisation, with WPF even having “baked in” support for switching flow direction for right to left languages. On top of that Rick Strahl and Michele Leroux Bustamante compiled an excellent guidance document detailing several technique for localising WPF applications. The documentation, along with sample code, can be found in the WPF Localisation Guidance project on CodePlex.

The document discusses several techniques for localising applications using RESX (resource) files, which is my preferred approach; but each of the techniques has its drawbacks:

  • Static Binding. Simple and easy, but lacks support for dynamically switching culture.
  • Attached Properties. Look powerful, but lacks support for value convertors and it’s a bit inefficient.
  • Markup Extension. A new instance of the helper (complete with event wireup for locale switching) is created for every control, which doesn’t sound ideal and may lead to memory leaks.

All of the techniques are perfectly workable solutions, but with Binding and INotifyPropertyChanged WPF already contains a powerful mechanism for mapping and automatically updating UI elements with data; surely there’s some way we can leverage those? My goal was to attempt to find a way to localise an application with the following criteria:

  • Use the standard Binding syntax so we can support value convertors.
  • No complicated markup extensions.
  • Provide a mechanism for code to “register” resources that can be consumed anywhere in the application, even from other PRISM modules.
  • Be able to cleanly switch locales, without restarting the application or closing/reopening screens.
  • Support the design experience with a minimum of fall back values when in design mode.

The Result – Localisation Using Binding

If you just want to see the code, there’s a basic implementation, and a PRISM demonstration, at the end of the article. Both implementations use the same basic “moving parts”:

LocalisationHelper

In essence this is our ViewModel. There are two key pieces of code here, first being:

public string this[string Key]
{
    get
    {
        if (!validKey(Key))
            throw new ArgumentException(@"Key is not in the valid [ManagerName].[ResourceKey] format");

        if (DesignHelpers.IsInDesignModeStatic)
            throw new Exception("Design mode is not supported");

        return _resourceManager.GetResourceString(GetManagerKey(Key), GetResourceKey(Key));
    }
}

This allows us to bind elements to the LocalisationHelper and provide a “key” which will be used to lookup the correct resource manager and resource string. Our binding uses a little known syntax that looks like this (take note of the initial “.”):

{Binding Path=.[MyResourceManager.MyResourceString]}

To provide design support we throw an exception if we detect design mode (using code from Laurent Bugnion’s MVVM Light Toolkit) so the FallbackValue can be used instead. I’m not too keep on throwing an exception, but I couldn’t see a cleaner way to “fail” the binding.

The other important code hooks an event that fires when the locale changes, and fires a NotifyPropertyChangedEvent with an empty property string. This triggers a refresh for all controls that have bindings to the LocalisationHelper.

ResourceManagerService

The ResourceManagerService provides several functions:

  • The ability to register ResourceManagers – these are automatically generated by Visual Studio when you create RESX files and are used to load the locale specific strings.
  • Retrieve a resource string from a given ResourceManager.
  • Get and set the current locale. A locale consists of an IETF language tag (such as en-GB) and a boolean to indicate whether the locale uses a right to left flow direction.
  • A event that is fired when the locale changes. This is hooked by the LocalisationHelper, for firing PropertyChanged events, and also by the main Window which uses the right to left flag to set the flow direction.

Give Me The Code Already!

There are two samples attached. The first is a simple application that uses a static ResourceManagerService that shows the basic implementation. The second is a PRISM based application that uses the container/service locator, EventAggregator, weak references, and several different modules to give a more “advanced” example.

ResourceTest.zip

PRISMResourceTest.zip

Conclusion

I haven’t yet used this solution in anger, but it certainly seems to “tick all the boxes” from my initial requirements. Comments, suggestions, criticisms and flames are welcomed 🙂

Posted in WPF.

Tagged with , , , , .


System.OutOfMemoryException Gotcha Using Clipboard.GetData in WPF

Introduction

Consider the following simple class for managing storage and retrieval of custom classes in the Windows Clipboard using WPF:

using System;
using System.Windows;

namespace ClipboardTest.Services
{
    public class ClipboardService : IClipboardService
    {
        public bool ContainsData<T>() where T:class
        {
            return Clipboard.ContainsData(typeof(T).ToString());
        }

        public void SetData<T>(T data) where T:class
        {
            Clipboard.SetData(typeof(T).ToString(), data);
        }

        public T GetData<T>() where T : class
        {
            return Clipboard.GetData(typeof(T).ToString()) as T;
        }
    }
}

Simple stuff, but it lets me abstract the clipboard away and remove the usual “magic string” approach for clipboard types.

Now consider the following basic tests:

using ClipboardTest.Services;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ClipboardTest.Test.Services
{
    public class TestPayload
    {
        public string Data { get; set; }
    }

    [TestClass]
    public class ClipboardServiceTests
    {
        [TestMethod]
        public void SetData_CustomClass_ClipboardContainsInstanceOfClass()
        {
            string data = @"More Cowbell";
            var clipboardService = new ClipboardService();

            clipboardService.SetData<TestPayload>(new TestPayload() { Data = data });

            Assert.IsTrue(clipboardService.ContainsData<TestPayload>());
        }

        [TestMethod]
        public void SetDataGetData_CustomClass_ReturnsEquivilantClass()
        {
            string data = @"More Cowbell";
            var clipboardService = new ClipboardService();

            clipboardService.SetData<TestPayload>(new TestPayload() { Data = data });
            var output = clipboardService.GetData<TestPayload>();

            Assert.IsNotNull(output);
            Assert.AreEqual(data, output.Data);
        }
    }
}

Again, all very simple. The first test checks to make sure that the class gets stored onto the clipboard, and the second checks to see if the class we get back off the clipboard is the same as the one we put in.

The more astute among you have probably spotted the problem in the test code already, but the resultant symptoms are a little confusing.

System.OutOfMemoryException?

When we run the tests the first test runs absolutely fine, but the second test crashes out with a System.OutOfMemoryException. A bit more digging showed that storing and retrieving standard types such as strings, ints etc all worked fine, but my own class was throwing an exception.

Checking good old MSDN threw up the following statement:

An object must be serializable for it to be put on the Clipboard.

Seeing as I had stupidly forgotten to add the SerializableAttribute to the TestPayload class that definitely explained why things weren’t working as I expected; but it didn’t really explain the actual behaviour I was seeing. Another quote from MSDN, but from the WinForms documentation, stated:

If you pass a non-serializable object to a Clipboard method, the method will fail without throwing an exception.

So from that I would expect SetData to silently fail, and for nothing to be on the clipboard. However the first test above clearly shows that the clipboard at least thinks that something of our specified type has been added to the clipboard – it just throws an exception when we try and retrieve it again. Marking the payload class as serializable did fix the issue, but the behaviour I was seeing certainly didn’t make that obvious!

Just out of interest I tested the same code using the WinForms Clipboard class, which looks to all intents and purposes exactly the same as the WPF one, and that did fail silently and returned null from GetData.

Conclusion

So, after all that rambling, the conclusion is that if you are working with the Clipboard in WPF and you are getting System.OutOfMemoryExceptions that don’t seem to make any sense, then you’ve probably forgotten to add the SerializableAttribute to whatever class you placed on the Clipboard.

Posted in Rambling, WPF.

Tagged with , , , .


Bindings Not Updating in WPF / Silverlight? Common Mistakes

Introduction

Yesterday I was working on a small prototype, which I will be blogging about shortly, and ran across the common problem of my bindings not updating. A very common problem, and one that’s usually a very simple fix once you’ve tracked it down.

Yes, I’m an Idiot!

Normally my ViewModels inherit from my ViewModelBase base class, which provides a RaisePropertyChanged method and, when in debug mode, uses reflection to check if the property name is valid. Now I’d recently refactored the code so this particular ViewModel wasn’t using the base class, so my first instinct was that I’d simply mistyped the property name magic string in the event – but that was all fine.

I threw in a few breakpoints and I could see my ViewModel was changing, I could see the OnPropertyChanged method being hit, but there didn’t seem to be any listeners and as a result my UI was just ignoring the changes.

After a few minutes of head scratching I noticed that although my class was firing the PropertyChanged event correctly, I hadn’t added INotifyPropertyChanged to my class declaration when I removed the base class! So although I was firing an event that looked like INotifyPropertyChanged.PropertyChanged, I was actually just firing my own event with the same name 🙂

Conclusion

So there you have it, I’m an idiot 🙂 The moral of the story is, when your bindings aren’t working check the obvious:

  1. The property name is correct. A base class that checks this in debug mode can be useful, or failing that, watch the Output window.
  2. Check you are actually implementing INotifyPropertyChanged and not just firing events that happen to have the same name 🙂

Posted in Silverlight, WPF.

Tagged with , , , .


Stack Overflow – Share Your Flair – Now in PNG!

Introduction

This morning I was taking a look at the new (ish) meta stackoverflow site, and noticed a thread asking “Can we get flare as an image?”. It seemed like a good idea, especially for forum signatures and the like, so I thought that seeing as I’d recently updated the WordPress widget, that I’d try and put something together.

Pretty as a Picture (or not)

As C# is generally my weapon of choice these days, I was originally going to knock something together in ASP.Net. I was thinking something along the lines of rending a WPF control to a bitmap, and emitting that as an image. I decided against that approach because:

  1. ASP.Net hosting is generally more expensive – most people I know seem to stick to *nix hosting.
  2. Rendering WPF to a bitmap doesn’t work in partial trust scenarios; so it might not even work on ASP.Net hosting anyway.

So, feeling in a masochistic mood, I thought I’d knock something together in lovely old PHP; the results of which look something like this:

Steve Robbins' StackOverflow Profile

Or, by passing in a userid on the querystring:

Marc Gravell's StackOverflow Profile

You can test  it with your own details by changing the userid on the following url:

https://www.grumpydev.com/imageFlair/imageFlair.php?userid=1

Images are cached (for 30minutes by default) and all of the StackOverflow data is taken from the JSON feed, so the strain on SO should be fairly minimal.

The Code

You can use the script hosted on here if you want (although I might have to change my mind on that if the load gets too high), or you can download the code and run it on your own box:

https://www.grumpydev.com/imageFlair.zip

Although it will run just fine “out of the box”, the code is pretty flexible; with plenty of configurable options for fonts, colours, positioning, background images, caching time etc. Take a look in config.php and you’ll hopefully find you can change everything you want to without having to delve into the main code.

Bear in mind that I am far from a PHP expert, and this was put together in a few hours, so please don’t laugh too much if you do take a peek at the source 🙂

Font Support / Licensing

The text is rendered to the bitmap using a TrueType font of your choosing, which gives us some nice flexibility. Unfortunately though, none of the fonts that we generally take for granted, such as Arial, Verdana etc. come with any distribution rights; so I can’t include any in the source archive. This might not be a problem if you already have your favourite TrueType fonts installed; but if you don’t you can get a “free” version of several core fonts from http://sourceforge.net/projects/corefonts. You just need to extract the archives using WinRAR, or 7zip, or something of that ilk; then just drop the TTF file into the source code directory.

That’s it – hope someone finds it useful 🙂

Update – Bug Fixes and Pretty URLs

After some feedback from the meta question I’ve fixed a stupid bug in the gravatar code, so now it should work for people who have a 1 in their gravatar hash (don’t ask! :-)).

I’ve also enabled “pretty urls” on the hosted version, so now you can go to a url like:

https://www.grumpydev.com/imageFlair/658.png

And you’ll get the PNG for the corresponding userid. I do intend to expand it to include Flair for the other sites in the “Stack Overflow” family, but I haven’t had chance yet.

For those that are interested, this is the .htaccess file that enables the pretty urls, and also reduces the cache time on pngs to 1 second:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)\.png$ imageFlair.php?userid=$1 [L]
</IfModule>

<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/png "access plus 1 seconds"
</IfModule>

Update 2 – Supporting the Whole Family

I’ve added an optional parameter to specify which site’s information to display. The “pretty” url format now looks like this:

http://[ur]/[mode]/[id].png

Where [mode] is one of:

So for Jeff (id number 1 everywhere is easy :-)) we end up with:

Stack Overflow Meta Server Fault

The htaccess file now looks like this:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)/(.*)\.png imageFlair.php?mode=$1&userid=$2 [L]
RewriteRule ^(.*)\.png$ imageFlair.php?userid=$1 [L]
</IfModule>

<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/png "access plus 1 seconds"
</IfModule>

Source ZIP is updated with all the latest changes. Hopefully that will be it for the time being! 🙂

Posted in Misc, Software.

Tagged with , , .


Updated: Stack Overflow WordPress Widget

Introduction

Just a quick post to say I’ve updated my Stack Overflow WordPress Widget to implement some basic caching. I’m still in the process of converting it to the new JSON service that SO have provided; but in the meantime this should cut down on the amount of traffic it uses.

You should be able to upgrade straight from the admin section of your blog; but you can also grab it from the WordPress Plugin Directory.

The cache time is configurable in the widget settings, and defaults to 30minutes. It attempts to locate your temp directory, then creates a unique filename cache in there; so there’s no need to chmod a cache file to get it to work.

I’ll hopefully get a JSON version working in the next few days, but if anyone has any problems feel free to give me a shout from the contact page.

Update: It seems the JSON version was easier than I thought 🙂 I’ve uploaded v2.0.0 that still includes the caching changes, but also uses the “official” JSON feed. If you have already used the cached version you might might need to delete the cache file to make it re-download the raw data.

The only “downside” to the JSON version is it requires json_decode, so you must have PHP 5.2.0 or above. If you can’t satisfy this requirement then you’ll be better off with the previous version.

Posted in Misc.

Tagged with , , .