简体   繁体   中英

How can I set CultureInfo in WPF in startup event?

I have a WPF application for .NET 4.7.2 and want to set the global culture based on command line arguments. According to many sources (eg here or official docs ) the culture in a multithreaded application is set with CultureInfo.DefaultThreadCurrentCulture . This does work in the constructor of the App. However it does not work where I need it, namely in the Application_Startup event, where I can evaluate the command line args. Why is that and how can I fix it?

App.xaml:

<Application x:Class="WpfApp1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp1"
             StartupUri="MainWindow.xaml"
             Startup="Application_Startup">

    <Application.Resources>

    </Application.Resources>
</Application>

App.xaml.cs:

namespace WpfApp1
{
    public partial class App : Application
    {
        public App()
        {
            Demo("en-EN");
        }

        // Stops working if Demo is called in Application_Startup instead of constructor
        /* private void Application_Startup(object sender, StartupEventArgs e)
           {
               // TODO Take culture code from StartupEventArgs
               Demo("en-EN");
           }
        */

        private static void Demo(string cultureCode)
        {
            WriteDate();
            SetCulture(cultureCode);
            Task.Factory.StartNew(WriteDate);
        }

        private static void SetCulture(string code)
        {
            var ci = new CultureInfo(code);
            CultureInfo.DefaultThreadCurrentCulture = ci;
            CultureInfo.DefaultThreadCurrentUICulture = ci;
        }

        static void WriteDate()
        {
            var threadId = Thread.CurrentThread.ManagedThreadId;
            var threadCulture = Thread.CurrentThread.CurrentCulture;
            System.Diagnostics.Debug.WriteLine($"Thread {threadId} with culture {threadCulture} => {DateTime.Now}");
        }
    }
}

My system culture is German. Output when using constructor is as expected:

Thread 1 with culture de-DE => 09.01.2020 16:13:20
Thread 3 with culture en-EN => 1/9/2020 4:13:20 PM

Now when I move the call to Demo to the Application_Startup event the output is:

Thread 1 with culture de-DE => 09.01.2020 16:16:10
Thread 3 with culture de-DE => 09.01.2020 16:16:10

The call to CultureInfo.DefaultThreadCurrentCulture is not respected! Interestingly if I change this call to Thread.CurrentThread.CurrentCulture this fixes the problem. However I want to set the culture for all threads, not just the current one.

You could define a custom entry point and get the arguments from there:

public class Program
{
    static void Main(string[] args)
    {
        App app = new App(args);
        app.InitializeComponent();
        app.Run();
    }
}

public partial class App : Application
{
    public App(string[] args)
    {
        Demo("en-EN");
    }

    private static void Demo(string cultureCode)
    {
        WriteDate();
        SetCulture(cultureCode);
        for (int i = 0; i < 100; i++)
            Task.Run((Action)WriteDate);
    }

    private static void SetCulture(string code)
    {
        var ci = new CultureInfo(code);
        CultureInfo.DefaultThreadCurrentCulture = ci;
    }

    static void WriteDate()
    {
        var threadId = Thread.CurrentThread.ManagedThreadId;
        var threadCulture = Thread.CurrentThread.CurrentCulture;
        System.Diagnostics.Debug.WriteLine($"Thread {threadId} with culture {threadCulture} => {DateTime.Now}");
    }
}

Remember to change the Build Action of App.xaml to Page to prevent the compiler from generating a Main method for you.

It is hard to answer what is the reason but looking at Threads windows I suppose thread pool handling background tasks is created before event Application_Startup is fired. You can see 1 thread in App constructor and 3 threads in startup event. Most likely 1 of those is used to handle Tasks.

I see 2 possible solutions:

1) In App constructor you can also access command line arguments using Environment so just keep logic in constructor:

Environment.GetCommandLineArgs()

2) Set it for all threads - enumerate them in Application_Startup and set culture for all using this stackoverflow answer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM