WPF 在运行时改变整个应用程序文化?
WPF Change whole application culture at runtime?
我有一个大型 WPF 应用程序,需要有一个菜单在 运行 时切换 language/culture。它用于文本本地化,也用于单位(例如:KMH/MPH 使用 Culture 的 RegionInfo
IsMetric
属性)甚至生成一些 URL,例如打开用户手册 pdf使用正确的语言。
在 .NET 4.7.2 中,我有这个完美运行的解决方案:
var language = XmlLanguage.GetLanguage(culture.Name);
foreach (Window childWindow in Application.Current.Windows)
{
childWindow.Dispatcher.Thread.CurrentCulture = culture;
childWindow.Dispatcher.Thread.CurrentUICulture = culture;
childWindow.Language = language;
}
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
LocalizeDictionary.Instance.Culture = culture;
LocalizeDictionary
是 XAMLMarkupExtensions nuget 的一部分,允许立即刷新所有本地化绑定。
不幸的是,自从迁移到 NET6 并在其中添加了一些异步代码后,它就无法正常工作了。
它确实改变了当前的线程文化,所有新的线程文化,甚至刷新了当前打开的 windows 个文化。
问题是,似乎 运行ning 线程没有得到更新,应用程序现在尝试比以前更多地重用它们,而不是创建新线程。因此,如果我放入断点,让我们说一个按钮命令并检查文化,它仍然显示旧的。为新 window 创建新视图模型也是如此,它似乎是使用旧文化创建的。
我看到很多答案都提到了这一行:
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(language));
不幸的是,这需要在静态上下文中为整个应用程序设置一次,无法在 运行 时间 (PropertyMetadata is already registered for type 'FrameworkElement'.
) 更改。而且我不认为这真的是问题所在,因为在当前 windows 上循环并更改 Language
属性 似乎也是如此。我的问题似乎在于现有的后台线程,而不是 WPF 元素本身。
我试图找到一种方法来查找所有现有线程,但只设法获得 OS 个具有 Process.GetCurrentProcess().Threads
的线程,而不是托管线程,因此无法通过该方法改变它们的文化。
有什么方法可以列出所有现有话题并改变他们的文化?或者强制 WPF 在新线程上执行 运行 命令而不是重新使用它们?
出于某种原因,上面的代码在 threads/default/dispatchers/window xml 上更改区域性是有效的,但是当 运行 来自普通事件处理程序或命令时。
在我的情况下,它是 运行 在异步命令中(在 ReactiveUI 库 ReactiveCommand.CreateFromTask() 的帮助下)并且在该上下文中执行它不会保留更改。即使我从该任务内部调用 WPF 调度程序也不行。所有跟随 event/commands 甚至绑定 属性 getter 的人都会被称为旧文化。
所以我无法解释细节,但如果您的文化更改不适用,请检查您正在执行操作的上下文。
此外,在每个 windows 上循环是没有用的。您可以将其设置为 Application.Current.MainWindow
我有一个大型 WPF 应用程序,需要有一个菜单在 运行 时切换 language/culture。它用于文本本地化,也用于单位(例如:KMH/MPH 使用 Culture 的 RegionInfo
IsMetric
属性)甚至生成一些 URL,例如打开用户手册 pdf使用正确的语言。
在 .NET 4.7.2 中,我有这个完美运行的解决方案:
var language = XmlLanguage.GetLanguage(culture.Name);
foreach (Window childWindow in Application.Current.Windows)
{
childWindow.Dispatcher.Thread.CurrentCulture = culture;
childWindow.Dispatcher.Thread.CurrentUICulture = culture;
childWindow.Language = language;
}
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
LocalizeDictionary.Instance.Culture = culture;
LocalizeDictionary
是 XAMLMarkupExtensions nuget 的一部分,允许立即刷新所有本地化绑定。
不幸的是,自从迁移到 NET6 并在其中添加了一些异步代码后,它就无法正常工作了。
它确实改变了当前的线程文化,所有新的线程文化,甚至刷新了当前打开的 windows 个文化。
问题是,似乎 运行ning 线程没有得到更新,应用程序现在尝试比以前更多地重用它们,而不是创建新线程。因此,如果我放入断点,让我们说一个按钮命令并检查文化,它仍然显示旧的。为新 window 创建新视图模型也是如此,它似乎是使用旧文化创建的。
我看到很多答案都提到了这一行:
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(language));
不幸的是,这需要在静态上下文中为整个应用程序设置一次,无法在 运行 时间 (PropertyMetadata is already registered for type 'FrameworkElement'.
) 更改。而且我不认为这真的是问题所在,因为在当前 windows 上循环并更改 Language
属性 似乎也是如此。我的问题似乎在于现有的后台线程,而不是 WPF 元素本身。
我试图找到一种方法来查找所有现有线程,但只设法获得 OS 个具有 Process.GetCurrentProcess().Threads
的线程,而不是托管线程,因此无法通过该方法改变它们的文化。
有什么方法可以列出所有现有话题并改变他们的文化?或者强制 WPF 在新线程上执行 运行 命令而不是重新使用它们?
出于某种原因,上面的代码在 threads/default/dispatchers/window xml 上更改区域性是有效的,但是当 运行 来自普通事件处理程序或命令时。
在我的情况下,它是 运行 在异步命令中(在 ReactiveUI 库 ReactiveCommand.CreateFromTask() 的帮助下)并且在该上下文中执行它不会保留更改。即使我从该任务内部调用 WPF 调度程序也不行。所有跟随 event/commands 甚至绑定 属性 getter 的人都会被称为旧文化。
所以我无法解释细节,但如果您的文化更改不适用,请检查您正在执行操作的上下文。
此外,在每个 windows 上循环是没有用的。您可以将其设置为 Application.Current.MainWindow