.net 如何在 WPF 中设置和更改文化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4041197/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to set and change the culture in WPF
提问by T.J.Kjaer
I have a .NET 4.0 WPF application where the user can change the language (culture) I simply let the user select a language, create a corresponding CultureInfo and set:
我有一个 .NET 4.0 WPF 应用程序,用户可以在其中更改语言(文化)我只是让用户选择一种语言,创建相应的 CultureInfo 并设置:
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
In the C# code this works fine. However in the WPF controls the culture is still en-US. This means for example that dates will be shown in the US format instead of whatever is correct for the current culture.
在 C# 代码中,这工作正常。然而,在 WPF 控件中,文化仍然是美国。例如,这意味着日期将以美国格式显示,而不是任何适合当前文化的格式。
Apparently, this is not a bug. According to MSDN and several blog posts and articles on StackOverflow the WPF language does not automatically follow the current culture. It is en-US until you do this:
显然,这不是一个错误。根据 MSDN 和 StackOverflow 上的几篇博客文章和文章,WPF 语言不会自动遵循当前的文化。在您执行此操作之前,它是 en-US:
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(CultureInfo.CurrentUICulture.IetfLanguageTag)));
See for example StringFormat Localization issues in wpf.
例如,参见wpf 中的 StringFormat 本地化问题。
I do not completely understand what is going on here. It seems the Language property on all frameworkelements is set to the current culture. Anyway, it works. I do this when the application starts up and now all controls works as expected, and e.g. dates is formatted according to the current culture.
我不完全明白这里发生了什么。似乎所有框架元素的 Language 属性都设置为当前文化。无论如何,它有效。我在应用程序启动时执行此操作,现在所有控件都按预期工作,例如根据当前文化格式化日期。
But now the problem: According to MSDN FrameworkElement.LanguageProperty.OverrideMetadatacan only be called once. And indeed, if I call it again (when the user changes the language) it will throw an exception. So I haven't really solved my problem.
但是现在的问题是:根据MSDNFrameworkElement.LanguageProperty.OverrideMetadata只能调用一次。事实上,如果我再次调用它(当用户改变语言时)它会抛出一个异常。所以我还没有真正解决我的问题。
The question: How can I reliably update the culture in WPF more than once and at any time in my applications life cycle?
问题:如何在应用程序生命周期中的任何时间多次可靠地更新 WPF 中的文化?
(I found this when researching: http://www.nbdtech.com/Blog/archive/2009/03/18/getting-a-wpf-application-to-pick-up-the-correct-regional.aspxand it seems he has something working there. However, I can't imagine how to do this in my application. It seems I would have to update the language in all open windows and controls and refresh all existing bindings etc.)
(我在研究时发现了这个:http: //www.nbdtech.com/Blog/archive/2009/03/18/getting-a-wpf-application-to-pick-up-the-correct-regional.aspx和它似乎他在那里工作。但是,我无法想象如何在我的应用程序中执行此操作。似乎我必须更新所有打开的窗口和控件中的语言并刷新所有现有绑定等)
采纳答案by T.J.Kjaer
I never found a way to do exactly what I asked for in the question. In my case I ended up solving it by having all my usercontrols inherit from a superclass that contained this:
我从来没有找到一种方法来完全按照我在问题中的要求去做。在我的情况下,我最终通过让我的所有用户控件从包含以下内容的超类继承来解决它:
/// <summary>
/// Contains shared logic for all XAML-based Views in the application.
/// Views that extend this type will have localization built-in.
/// </summary>
public abstract class ViewUserControl : UserControl
{
/// <summary>
/// Initializes a new instance of the ViewUserControl class.
/// </summary>
protected ViewUserControl()
{
// This is very important! We make sure that all views that inherit
// from this type will have localization built-in.
// Notice that the following line must run before InitializeComponent() on
// the view. Since the supertype's constructor is executed before the type's
// own constructor (which call InitializeComponent()) this is as it
// should be for classes extending this
this.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag);
}
}
When the user changes the language I then create new instances of any usercontrols that are currently running.
当用户更改语言时,我会创建当前正在运行的任何用户控件的新实例。
This solved my problem. However, I would still like a way to do this "automatically" (i.e. without having to keep track of any instantiated objects).
这解决了我的问题。但是,我仍然想要一种“自动”执行此操作的方法(即无需跟踪任何实例化对象)。
回答by Ross
I'm going to chime in here.
我要在这里插话。
I successfully did this using the OverrideMetadata()method that the OP mentioned:
我使用OverrideMetadata()OP 提到的方法成功地做到了这一点:
var lang = System.Windows.Markup.XmlLanguage.GetLanguage(MyCultureInfo.IetfLanguageTag);
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(lang)
);
But, I still found instances in my WPF in which the system culture was being applied for dates and number values. It turned out these were values in <Run>elements. It was happening because the System.Windows.Documents.Runclass does not inherit from System.Windows.FrameworkElement, and so the overriding of metadata on FrameworkElementobviously had no effect.
但是,我仍然在我的 WPF 中发现了一些实例,其中系统区域性被应用于日期和数字值。原来这些是<Run>元素中的值。之所以发生这种情况,是因为System.Windows.Documents.Run该类没有继承自System.Windows.FrameworkElement,因此覆盖 上的元数据FrameworkElement显然没有效果。
System.Windows.Documents.Runinherits its Languageproperty from System.Windows.FrameworkContentElementinstead.
System.Windows.Documents.Run继承了它的Language属性System.Windows.FrameworkContentElement。
And so the obvious solution was to override the metadata on FrameworkContentElementin the same way. Alas, doing do threw an exception (PropertyMetadata is already registered for type System.Windows.FrameworkContentElement), and so I had to do it on the next descendant ancestor of Runinstead, System.Windows.Documents.TextElement:
因此显而易见的解决方案是以FrameworkContentElement相同的方式覆盖元数据。唉,做 do 抛出异常(PropertyMetadata 已经注册为 System.Windows.FrameworkContentElement 类型),所以我不得不在下一个后代祖先上做它,Run而不是System.Windows.Documents.TextElement:
FrameworkContentElement.LanguageProperty.OverrideMetadata(
typeof(System.Windows.Documents.TextElement),
new FrameworkPropertyMetadata(lang)
);
And that sorted out all my issues.
这解决了我所有的问题。
There are a few more sub-classes of FrameworkContentElement(listed here) which for completeness should have their metadata overridden as well.
还有一些FrameworkContentElement(在此处列出)的子类,为了完整起见,它们的元数据也应该被覆盖。
回答by Judah Gabriel Himango
I'm not sure how to get around the "can't call OverrideMetadata multiple times" exception.
我不确定如何解决“无法多次调用 OverrideMetadata”异常。
As a workaround, when the user changes UI cultures in your app, you could restart your app with that culture, passing in the new culture as a command line argument. Unless your users will be changing cultures often, this sounds like a reasonable solution.
作为一种解决方法,当用户在您的应用程序中更改 UI 文化时,您可以使用该文化重新启动您的应用程序,将新文化作为命令行参数传递。除非您的用户经常改变文化,否则这听起来是一个合理的解决方案。
回答by RJ Moeller
Just my two cents: After almost going crazy when trying to implement ComponentOne WPF controls (DataGrid and C1DatePicker) with my German language assembly I stumbled upon this page.
只是我的两分钱:在尝试使用我的德语程序集实现 ComponentOne WPF 控件(DataGrid 和 C1DatePicker)时几乎发疯后,我偶然发现了这个页面。
This seems to be directing in the right way: I just entered the above code into my App.xaml.cs / Application_startup routine and now German date/time formatting for C1DatePicker finally works.
这似乎是以正确的方式指导的:我刚刚将上述代码输入到我的 App.xaml.cs / Application_startup 例程中,现在 C1DatePicker 的德语日期/时间格式终于起作用了。
Got to test DataGrid right after that.
在那之后必须立即测试 DataGrid。
private void Application_Startup(object sender, StartupEventArgs e)
{
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
System.Windows.Markup.XmlLanguage.GetLanguage(CultureInfo.CurrentUICulture.IetfLanguageTag)));
}
Thanks!
谢谢!
Update:Tested C1DataGrid for WPF - works! This solved all the problems I had with international Date / Time settings in my Applications. Great!
更新:为 WPF 测试了 C1DataGrid - 有效!这解决了我在应用程序中使用国际日期/时间设置时遇到的所有问题。伟大的!
回答by rbee
I pretty much had the same issue.
我几乎有同样的问题。
I found this: http://www.codeproject.com/Articles/35159/WPF-Localization-Using-RESX-Files(might not be the original source).
我发现了这个:http: //www.codeproject.com/Articles/35159/WPF-Localization-Using-RESX-Files(可能不是原始来源)。
It discusses a markup extension named "UICultureExtension" which is attached to the Language property of all framework elements that need localization (in XAML).
它讨论了一个名为“UICultureExtension”的标记扩展,它附加到所有需要本地化(在 XAML 中)的框架元素的 Language 属性。
If you raise a UI language changed event, static extension managers in the background will update all registered framework elements.
如果您引发 UI 语言更改事件,后台的静态扩展管理器将更新所有已注册的框架元素。
回答by Peter Taylor
Adaptive OverrideMetadata
自适应覆盖元数据
Some form of reloading is inevitable, because changing a control's Languageproperty doesn't make it update its text.
某种形式的重新加载是不可避免的,因为更改控件的Language属性不会使其更新其文本。
However, there's a way of overriding the metadata which allows you to set it once and have newcontrols automatically use the current culture:
但是,有一种覆盖元数据的方法,它允许您设置一次并让新控件自动使用当前区域性:
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
System.Windows.Markup.XmlLanguage.Empty,
default(PropertyChangedCallback),
_CoerceCurrentXmlLang));
where the CoerceValueCallbackis
其中,CoerceValueCallback为
private static object _CoerceCurrentXmlLang(DependencyObject d, object baseValue)
{
var lang = baseValue as System.Windows.Markup.XmlLanguage;
var culture = System.Globalization.CultureInfo.CurrentUICulture;
return lang != null && lang.IetfLanguageTag.Equals(culture.Name, StringComparison.InvariantCultureIgnoreCase)
? lang
: System.Windows.Markup.XmlLanguage.GetLanguage(culture.Name);
}
By itself this isn't quite enough, because newly created controls will get the default value System.Windows.Markup.XmlLanguage.Emptywithout it being coerced. However, if you then set xml:lang=""in your windows' XAML, that will be coerced, and then each new control will see that it inherits a value from its parent and will coerce it. The result is that new controls added to that window will use the current language.
这本身还不够,因为新创建的控件将获得默认值System.Windows.Markup.XmlLanguage.Empty而不会被强制。但是,如果您随后xml:lang=""在 Windows 的 XAML 中进行了设置,那么它将被强制转换,然后每个新控件都会看到它从其父级继承了一个值并对其进行了强制转换。结果是添加到该窗口的新控件将使用当前语言。
PS As with many things in WPF, it would be quite a bit simpler if they hadn't been so keen to keep things internal. DefaultValueFactorywould be a far more elegant way of doing this.
PS 与 WPF 中的许多东西一样,如果他们不那么热衷于保留东西,那会简单得多internal。DefaultValueFactory将是一种更优雅的方式来做到这一点。
Reloading
重装
The most extreme, but therefore reliable, way of reloading is just to create a new main window and discard the old one.
最极端但因此可靠的重新加载方式就是创建一个新的主窗口并丢弃旧的主窗口。
Almost as extreme but not quite is to arrange for the language setting to be changed only in a very simple pane of the main window with very little loaded, and that very little to be entirely databound to a viewmodel which supports forcing a property changed notification for everything.
几乎同样极端但不完全是安排只在主窗口的一个非常简单的窗格中更改语言设置,加载很少,并且很少完全数据绑定到支持强制属性更改通知的视图模型一切。
Existing answers to this question have other suggestions.
这个问题的现有答案还有其他建议。
回答by Emmanuel
It's not completely your answer, but I used this to reload the resources. But you still need to reload the windows...
这不完全是你的答案,但我用它来重新加载资源。但是你仍然需要重新加载窗口......
List<Uri> dictionaryList = new List<Uri>();
foreach (ResourceDictionary dictionary in Application.Current.Resources.MergedDictionaries)
{
dictionaryList.Add(dictionary.Source);
}
Application.Current.Resources.MergedDictionaries.Clear();
foreach (Uri uri in dictionaryList)
{
ResourceDictionary resourceDictionary1 = new ResourceDictionary();
resourceDictionary1.Source = uri;
Application.Current.Resources.MergedDictionaries.Add(resourceDictionary1);
}

