Getting a WPF application to pick up the correct regional settings

Before we begin, the way a program gets its settings is both complicated and outside the scope of this blog post, when you test functionality related to settings change it’s important you start the program directly from a folder window, if you run it from within Visual Studio or any other program you may get incorrect results.

First let’s see the problem - open Visual Studio and create a new “Wpf application” project

Change Window1 code to:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        DataContext = this;
    }

    public DateTime Date { get { return new DateTime(2009, 1, 2); } }
}

And add this in the accosiated XAML file:

<TextBlock Name=”DateField” Text="{Binding Date}"/>

Now we have a program that should display a date, January 2nd 2009, if you compile and run the program everything will look fine – if you are in the US, that is, if you try to change your regional settings between “English (United states)” and “English (united kingdom)” and restart your program you will see the display stays in US mode.

Just in case you don’t know, the date should be displayed 1/2/2009 in the US but 02/01/2009 in the UK.

Now that we’ve seen the problem, let’s add the magic line that will solve it (at least partially), to your App.xaml.cs add:

    protected override void OnStartup(StartupEventArgs e)
    {
        FrameworkElement.LanguageProperty.OverrideMetadata(
            typeof(FrameworkElement),
            new FrameworkPropertyMetadata(
                XmlLanguage.GetLanguage(
                CultureInfo.CurrentCulture.IetfLanguageTag)));
        base.OnStartup(e);
     }

Compile and run again (reminder, run directly from a folder window, not from Visual Studio) change you regional settings and restart the program and everything works.

We can stop here, or we can get more ambitions, let’s have our program change the formatting on the fly, without restarting.

There are 4 steps we need to perform:

  • Find out that the settings have changed
  • Update .net CultureInfo
  • Update Wpf
  • Refresh the display

Detecting that the settings has changed is easy, when the regional settings change Windows sends a WM_SETTINGCHNAGE message to all top level windows with the string “intl” in the lParam parameter.

Updating CultureInfo in also easy and documented, you just have to call CultureInfo.ClearCachedData.

Updating Wpf is more difficult, we can’t use OverrideMetadata again like we did before, it’s too late, we have to visit all the windows in the application and change their language property, in our sample application we have just one window so it’s not so bad.

Refreshing the display turns out to be the most difficult of them all, Wpf data binding just refused to update the control text, no matter how I tried to force it to update, the only way I got it to update is by removing the binding and re-adding it, in the example code I’ll do it explicitly for the only binding in the program, doing this automatically for the entire program is left as an exercise for the reader.

The actual code is actually shorter much then the explenation above, just add this to the Window1 class:

    protected override void OnSourceInitialized(EventArgs e)
    {
        ((HwndSource)HwndSource.FromVisual(this)).AddHook(IntlChangeHook);
        base.OnSourceInitialized(e);
    }

    private const int WM_SETTINGSCHANGE = 0x1a;

    private IntPtr IntlChangeHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        // when regional settings changed
        if (msg == WM_SETTINGSCHANGE &&
            Marshal.PtrToStringUni(lParam) == "intl")
        {
            // update CultureInfo
            CultureInfo.CurrentCulture.ClearCachedData();

            // update our window's language
            this.Language = XmlLanguage.GetLanguage( CultureInfo.CurrentCulture.IetfLanguageTag);

            // and reset the binding
            Binding b = DateField.GetBindingExpression(TextBlock.TextProperty).ParentBinding;
            BindingOperations.ClearBinding(DateField, TextBlock.TextProperty);
            DateField.SetBinding(TextBlock.TextProperty,b);
        }
        return IntPtr.Zero;
    }

Before ending this post just one word of warning, let’s say you have a window with a TextBox displaying a date and an ok button, the user opens the window with the US settings, the box now shows 1/2/2009 for January 2nd, 2009, the user changes the regional settings to UK, you follow the instructions in this post to switch the application’s settings but don’t change the TextBox text it’s still 1/2/2009, the user clicks ok and the program parses the text as February 1st 2009, oops – so if you go to the trouble of picking up regional settings changes on the fly you have to make sure all the data bound and non-data bound controls are also updated.

posted @ Wednesday, March 18, 2009 4:45 PM

Comments on this entry:

# re: Getting a WPF application to pick up the correct regional settings

Left by Nils at 11/24/2009 1:49 PM

Thanks for the fix, I was looking for one!

Now, why doesn't WPF already set the initial language metadata from the user's regional settings? It's a bug, isn't it?

# re: Getting a WPF application to pick up the correct regional settings

Left by Charles at 2/4/2010 5:55 PM

I have spent over 4 hours looking for this solution, thanks for sharing your knowledge.

# re: Getting a WPF application to pick up the correct regional settings

Left by benny at 7/7/2011 4:49 PM

Thanks!!

The solution i've searched for!!

# re: Getting a WPF application to pick up the correct regional settings

Left by marimuthu at 10/21/2011 1:56 PM

I want to change the currrency symbol
when i use the above lines in my application.vb nothing changes im using VB not c#.

# re: Getting a WPF application to pick up the correct regional settings

Left by John at 11/9/2013 6:45 PM

I'm a bit disappointed that this issue still exists four years on.

Your comment:



 (will not be displayed)


 
 
Please add 1 and 4 and type the answer here: