Skip to main content

Testing when the culture changes in WPF

Recently I've do some work on a WPF app which required to support multiple languages in the UI - a spike to see how easy supporting multiple languages is in WPF, the test UI is shown below in English & French:
The app is easy enough to understand - change the UI language and all the text based values are updated with the locale specific instances. I wanted to follow the standard pattern of using language specific resource files to store the translated text, these were placed in the standard location inside the project structure:
One of the big advantages of using this mechanism is the resources are compiled into a class which can be used in your ViewModels and Views and this is why see above the 'Resource.resx' file - this contains the 'untranslated' resource strings etc:
I've been aware for a long time there are 2 culturing settings per thread in .Net - Thread.CurrentCulture & Thread.CurrentUICulture, the second is the one of interest here, it is used by the ResourceManager to load the correct resources at run-time. What's interesting  is in .Net 4.5 they introduced a couple of static methods to set these properties globally - CultureInfo.DefaultThreadCurrentCulture & CultureInfo.DefaultThreadCurrentUICulture, once set these are used as the default setting for all threads created after this point. This brings up an interesting scenario - if you create a thread before initializing these properties it will default to the behavior seen in .Net 4.0, it will take the current culture from the system locale (windows system culture), but once set then any existing threads will be updated (in the same app-domain). The default value for both properties is NULL, I was expecting it to be set to the system locale.

To keep my ViewModels simple, I decided to push the complexity of using these new properties into a service which is able to notify any interested ViewModels when the selected culture has changed:
Loading ....

As you can see I'm using the CultureChanged property to notify anyone interested when the thread UI culture has changed. To make sure the same instance is shared between all ViewModel instances, the interface & class are registered as a singleton with the IoC container (Autofac):
This then allows a ViewModel to update any text which needs to be translated when the current culture changes, the following snippet shows the CultureChanged property being subscribed too and when the stream pumps it raises IPNC for all the required fields on the ViewModel:
So the point of the post was testing when the culture changes and this is done by putting the implementation of the ICultureService interface under test, this was easy as the service has a high level of SRP, infact I only need 3 tests to cover off the behaviour. The first two are straight forward they test when the culture changes we get notified via the CultureChanged property:
As you can see from the green icons these tests are passing as expected - the icons are a visualisation provided by nCrunch.

The third test produced the interesting results, this test was not specifically testing the CultureService, it was more around testing the vertical slice of changing culture - when the culture changes then the ResourceManager should provide the correctly translated text when requested.

To test this requires translated resource strings in culture specific *.resx files, the following screenshot shows resources for English & French and the test:
Does the test pass or fail?

It depends...

When run as a single test from either nCrunch, Resharper or any other test runner it works as expected:
But when run as part of a group of tests - running all tests in a class or assembly then it will fail:
How can this be explained?

I thought it must be because the ResourceManager only gets updated on the dispatcher thread - I was thinking single tests are run on the UI (dispatcher) thread, where as if your running multiple tests they are run on a background thread. A quick look at the threads running threads showed this wasn't the case, single test:
Multiple tests running (also on a work thread):
At this point I gave up looking for a reason for the failure and started looking for a workaround solution to getting it working when running multiple tests, the answer turned out to be simple - explicitly set the compiled Resource class Culture property, this is done in a CultureChanged property subscription:
The updated test now looks like this:

Comments

Popular posts from this blog

Showing a message box from a ViewModel in MVVM

I was doing a code review with a client last week for a WPF app using MVVM and they asked ' How can I show a message from the ViewModel? '. What follows is how I would (and have) solved the problem in the past. When I hear the words ' show a message... ' I instantly think you mean show a transient modal message box that requires the user input before continuing ' with something else ' - once the user has interacted with the message box it will disappear. The following solution only applies to this scenario. The first solution is the easiest but is very wrong from a separation perspective. It violates the ideas behind the Model-View-Controller pattern because it places View concerns inside the ViewModel - the ViewModel now knows about the type of the View and specifically it knows how to show a message box window: The second approach addresses this concern by introducing the idea of messaging\events between the ViewModel and the View. In the example below

Implementing a busy indicator using a visual overlay in MVVM

This is a technique we use at work to lock the UI whilst some long running process is happening - preventing the user clicking on stuff whilst it's retrieving or rendering data. Now we could have done this by launching a child dialog window but that feels rather out of date and clumsy, we wanted a more modern pattern similar to the way <div> overlays are done on the web. Imagine we have the following simple WPF app and when 'Click' is pressed a busy waiting overlay is shown for the duration entered into the text box. What I'm interested in here is not the actual UI element of the busy indicator but how I go about getting this to show & hide from when using MVVM. The actual UI elements are the standard Busy Indicator coming from the WPF Toolkit : The XAML behind this window is very simple, the important part is the ViewHost. As you can see the ViewHost uses a ContentPresenter element which is bound to the view model, IMainViewModel, it contains 3 child v

Custom AuthorizationHandler for SignalR Hubs

How to implement IAuthorizationRequirement for SignalR in Asp.Net Core v5.0 Been battling this for a couple of days, and eventually ended up raising an issue on Asp.Net Core gitHub  to find the answer. Wanting to do some custom authorization on a SignalR Hub when the client makes a connection (Hub is created) and when an endpoint (Hub method) is called:  I was assuming I could use the same Policy for both class & method attributes, but it ain't so - not because you can't, because you need the signatures to be different. Method implementation has a resource type of HubInnovationContext: I assumed class implementation would have a resource type of HubConnectionContext - client connects etc... This isn't the case, it's infact of type DefaultHttpContext . For me I don't even need that, it can be removed completely  from the inheritence signature and override implementation. Only other thing to note, and this could be a biggy, is the ordering of the statements in th