如何设置和改变WPF中的文化

我有一个.NET 4.0 WPF应用程序,用户可以在其中更改语言(文化)我只需让用户select一种语言,创build一个对应的CultureInfo并设置:

Thread.CurrentThread.CurrentCulture = cultureInfo; Thread.CurrentThread.CurrentUICulture = cultureInfo; 

在C#代码这工作正常。 然而在WPF控制下,文化仍然是en-US。 这意味着,例如,date将以美国格式显示,而不是对当前文化而言是正确的。

显然,这不是一个错误。 根据MSDN和一些关于StackOverflow的博文和文章,WPF语言不会自动地遵循当前的文化。 这是en-US,直到你这样做:

 FrameworkElement.LanguageProperty.OverrideMetadata( typeof(FrameworkElement), new FrameworkPropertyMetadata( XmlLanguage.GetLanguage(CultureInfo.CurrentUICulture.IetfLanguageTag))); 

请参阅wpf中的StringFormat本地化问题 。

我不完全明白这里发生了什么。 看起来,所有frameworkelements上的Language属性都设置为当前文化。 无论如何,它的工作。 我在应用程序启动时执行此操作,现在所有控件都按预期工作,例如date根据当前文化进行格式化。

但现在的问题是:根据MSDN FrameworkElement.LanguageProperty.OverrideMetadata只能调用一次。 事实上,如果我再次调用它(当用户改变语言),它会抛出一个exception。 所以我还没有真正解决我的问题。

问题:我如何可靠地在WPF中不止一次地在应用程序生命周期中更新文化?

(我研究时发现这个: http : //www.nbdtech.com/Blog/archive/2009/03/18/getting-a-wpf-application-to-pick-up-the-correct-regional.aspx和它看起来他在那里工作,但我无法想象如何在我的应用程序中这样做,似乎我将不得不在所有打开的窗口和控件中更新语言,并刷新所有现有的绑定等)

我要在这里钟声

我使用OP提到的OverrideMetadata()方法成功地做到了这一点:

 var lang = System.Windows.Markup.XmlLanguage.GetLanguage(MyCultureInfo.IetfLanguageTag); FrameworkElement.LanguageProperty.OverrideMetadata( typeof(FrameworkElement), new FrameworkPropertyMetadata(lang) ); 

但是,我仍然在我的WPF中发现了系统文化被应用于date和数字值的例子。 原来,这些是<Run>元素中的值。 这是因为System.Windows.Documents.Run类没有从System.Windows.FrameworkElementinheritance,所以在FrameworkElement上重写元数据显然没有效果。

System.Windows.FrameworkContentElement代替从System.Windows.FrameworkContentElementinheritance它的Language属性。

所以显而易见的解决scheme是以相同的方式覆盖FrameworkContentElement上的元数据。 唉,这样做会引发一个exception( PropertyMetadata已经注册了System.Windows.FrameworkContentElementtypes ),所以我不得不在Run的下一个后代祖先上代替System.Windows.Documents.TextElement

 FrameworkContentElement.LanguageProperty.OverrideMetadata( typeof(System.Windows.Documents.TextElement), new FrameworkPropertyMetadata(lang) ); 

这整理了我所有的问题。

还有一些FrameworkContentElement子类(列在这里 ),为了完整性,它们的元数据也应该被覆盖。

我不知道如何解决“不能调用OverrideMetadata多次”的例外。

解决方法是,当用户更改应用程序中的UI文化时,可以使用该文化重新启动您的应用程序,将新文化作为命令行parameter passing。 除非你的用户经常改变文化,这听起来像是一个合理的解决scheme。

我从来没有find一个办法来完成我在问题中所要求的。 在我的情况下,我最终解决了所有我的用户控件inheritance自一个超类包含这个:

 /// <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); } } 

当用户更改语言时,我将创build当前正在运行的任何用户控件的新实例。

这解决了我的问题。 但是,我仍然想要一种“自动”的方式(即不必跟踪任何实例化的对象)。

只是我的两分钱:在尝试使用我的德语程序集来实现ComponentOne WPF控件(DataGrid和C1DatePicker)之后,我几乎要疯了,于是我偶然发现了这个页面。

这似乎是正确的方向:我刚刚进入我的App.xaml.cs / Application_startup例程上面的代码,现在德国date/时间格式为C1DatePicker终于工作。

必须在此之后testingDataGrid。

  private void Application_Startup(object sender, StartupEventArgs e) { FrameworkElement.LanguageProperty.OverrideMetadata( typeof(FrameworkElement), new FrameworkPropertyMetadata( System.Windows.Markup.XmlLanguage.GetLanguage(CultureInfo.CurrentUICulture.IetfLanguageTag))); } 

谢谢!

更新:testingC1DataGrid的WPF – 工程! 这解决了我在我的应用程序中使用国际date/时间设置所遇到的所有问题。 大!

我几乎有同样的问题。

我发现这个: http : //www.codeproject.com/Articles/35159/WPF-Localization-Using-RESX-Files (可能不是原始来源)。

它讨论了名为“UICultureExtension”的标记扩展,它附加到需要本地化(在XAML中)的所有框架元素的Language属性。

如果您提出UI语言更改的事件,后台的静态扩展pipe理器将更新所有已注册的框架元素。

自适应覆盖元数据

某种forms的重新加载是不可避免的,因为更改控件的Language属性并不会使其更新文本。

但是,有一种重写元数据的方法,可以将其设置为一次,并使控件自动使用当前文化:

 FrameworkElement.LanguageProperty.OverrideMetadata( typeof(FrameworkElement), new FrameworkPropertyMetadata( System.Windows.Markup.XmlLanguage.Empty, default(PropertyChangedCallback), _CoerceCurrentXmlLang)); 

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); } 

本身并不足够,因为新创build的控件在被强制的时候会得到默认值System.Windows.Markup.XmlLanguage.Empty 。 但是,如果您在窗口的XAML中设置了xml:lang="" ,那么将被强制执行,然后每个新的控件都会看到它从父项inheritance了一个值并强制它。 结果是添加到该窗口的新控件将使用当前的语言。

PS和WPF中的很多东西一样,如果他们不那么热衷于保持internal东西,那将会更加简单。 DefaultValueFactory将是一个更加优雅的方式。

重装

最重要的,但也是可靠的重新装载的方式只是创build一个新的主窗口,丢弃旧窗口。

几乎同样极端但并不完全是为了只在主窗口的一个非常简单的窗格中进行语言设置的改变,而且很less加载一个完全的数据绑定到支持强制属性改变通知的视图模型一切。

现有的这个问题的答案还有其他的build议。

这不完全是你的答案,但我用它来重新加载资源。 但你仍然需要重新加载窗口…

  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); }