Reputation: 584
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 (e.g. 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.
Upvotes: 1
Views: 613
Reputation: 169200
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.
Upvotes: 1
Reputation: 748
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
Upvotes: 0