Search code examples
c#.netwpfcultureinfo.net-4.6

CultureInfo.ClearCachedData is not working properly in .net 4.6 and older for WPF only


Consider the following simple WPF application:

XAML (MainWindow):

<StackPanel>
    <TextBlock x:Name="Old"/>
    <TextBlock x:Name="New"/>
</StackPanel>

Codebehind:

  public MainWindow()
        {
            InitializeComponent();
            SystemEvents.UserPreferenceChanged += (sender, args) =>
            {
                Old.Text = $"Old culture name {CultureInfo.CurrentCulture.Name}";
                CultureInfo.CurrentCulture.ClearCachedData();
                New.Text = $"New culture name {CultureInfo.CurrentCulture.Name}";
            };
        }

Each time the user changes their regional settings it will print to Old TextBlock the old culture's name and, after calling ClearCachedData, the new culture's name will be set on New TextBlock. This is the window where I change these settings in Clock and Region section within Control Panel:

enter image description here

Now, if you build targeting 4.5.2 or less, it will work as expected and you will receive, after actually changing the setting, an output similar to this:

enter image description here

Now, build targeting 4.6 or later versions, you get something like this:

enter image description here

If you are targeting 4.6 or later, no matter how many times you change the number format region, no matter how many times you call ClearCachedData, CurrentCulture will not change...

But wait! Things get weirder... If you put above code into a Console App (using Console.WriteLine instead of course), it works not depending in which framework you chose (4.5.2, 4.6, anything..), strange isn't it? That is why I wrote "WPF only" in the title.. but I do not know for sure if this happens in any other platform other than WPF

There is still another crazy thing in this tale, since I have been playing around with this issue since yesterday, I found a trikcy way to make WPF or Console apps, targeting 4.5.2 or below, working wrong, just as the one targeting 4.6 and older. Simply setting CultureInfo.DefaultThreadCurrentCulture to anything different from null will break things apart.

public MainWindow()
{
    InitializeComponent();

    CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CurrentCulture;

    SystemEvents.UserPreferenceChanged += (sender, args) =>
    {
        Old.Text = $"Old culture name {CultureInfo.CurrentCulture.Name}";
        CultureInfo.CurrentCulture.ClearCachedData();
        New.Text = $"New culture name {CultureInfo.CurrentCulture.Name}";
    };
}

The above code will behave as if it were targeting 4.6. So, what I thought was that WPF targeting 4.6 was setting CultureInfo.DefaultThreadCurrentCulture in some way, but after testing this theory I am really stuck, because it is null by default, event in 4.6 and older...

So, is this a bug in WPF and/or .NET 4.6? I have not been able to find this breaking change reported.

Please, anything that shed some light on this will be appreciated. Also, if this is a bug and anyone know where could I report it, please tell me.

Note: I tag this question with C# as it is the language I am using, although I consider this issue is language independent.


Solution

  • For those visitors from the future and beyond who may will be having the same issue. I found a sort of workaround:

        public MainWindow()
        {
            InitializeComponent();
    
            CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CurrentCulture;
    
            SystemEvents.UserPreferenceChanged += (sender, args) =>
            {
                Old.Text = $"Old culture name {CultureInfo.CurrentCulture.Name}";
                CultureInfo.CurrentCulture.ClearCachedData();
                CultureInfo.CurrentCulture = new Thread(() => { }).CurrentCulture;
                New.Text = $"New culture name {CultureInfo.CurrentCulture.Name}";
            };
        }
    

    The above code will work in both building options: 4.5.2 and earlier, or 4.6 and later. Notice that the call to ClearCachedData is still a must. We create a new thread instance and we manually update our CurrentCulture to that of the new thread.

    Happy coding!