Cómo establecer y cambiar la cultura en WPF

Tengo una aplicación .NET 4.0 WPF donde el usuario puede cambiar el idioma (cultura) Simplemente dejo que el usuario seleccione un idioma, crea una CultureInfo correspondiente y establece:

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

En el código C # esto funciona bien. Sin embargo, en los controles de WPF, la cultura todavía está en-US. Esto significa, por ejemplo, que las fechas se mostrarán en el formato de EE. UU. En lugar de lo que sea correcto para la cultura actual.

Aparentemente, esto no es un error. Según MSDN y varias publicaciones de blog y artículos sobre StackOverflow, el lenguaje WPF no sigue automáticamente la cultura actual. Es en-US hasta que hagas esto:

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

Ver por ejemplo problemas de localización StringFormat en wpf .

No entiendo completamente lo que está pasando aquí. Parece que la propiedad Idioma en todos los elementos de marco se establece en la cultura actual. De todos modos, funciona. Lo hago cuando la aplicación se inicia y ahora todos los controles funcionan como se esperaba, y por ejemplo, las fechas se formatean de acuerdo con la cultura actual.

Pero ahora el problema: de acuerdo con MSDN FrameworkElement.LanguageProperty.OverrideMetadata solo se puede llamar una vez. Y de hecho, si lo vuelvo a llamar (cuando el usuario cambia el idioma) emitirá una excepción. Entonces realmente no he resuelto mi problema.

La pregunta: ¿cómo puedo actualizar de manera confiable la cultura en WPF más de una vez y en cualquier momento en el ciclo de vida de mis aplicaciones?

(Encontré esto al investigar: http://www.nbdtech.com/Blog/archive/2009/03/18/getting-a-wpf-application-to-pick-up-the-correct-regional.aspx y parece que tiene algo trabajando allí. Sin embargo, no me puedo imaginar cómo hacerlo en mi aplicación. Parece que tendría que actualizar el idioma en todas las ventanas y controles abiertos y actualizar todas las encuadernaciones existentes, etc.)

Voy a entrar aquí.

Lo logré utilizando el método OverrideMetadata() que mencionó el OP:

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

Pero, todavía encontré instancias en mi WPF en las que se aplicaba la cultura del sistema para fechas y valores numéricos. Resultó que estos eran valores en elementos . Sucedía porque la clase System.Windows.Documents.Run no hereda de System.Windows.FrameworkElement , por lo que la anulación de los metadatos en FrameworkElement obviamente no tuvo ningún efecto.

System.Windows.Documents.Run hereda su propiedad Language de System.Windows.FrameworkContentElement lugar.

Y entonces la solución obvia fue anular los metadatos en FrameworkContentElement de la misma manera. Por desgracia, hacer lanzar una excepción ( PropertyMetadata ya está registrado para el tipo System.Windows.FrameworkContentElement ), así que tuve que hacerlo en el próximo ancestro descendiente de Run lugar, System.Windows.Documents.TextElement :

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

Y eso resolvió todos mis problemas.

Hay unas pocas subclases más de FrameworkContentElement (enumeradas aquí ) que, para completar, también deberían tener sus metadatos anulados.

No estoy seguro de cómo evitar la excepción “no puedo llamar a OverrideMetadata varias veces”.

Como solución alternativa, cuando el usuario cambia las culturas de la interfaz de usuario en su aplicación, puede reiniciar su aplicación con esa cultura, pasando la nueva cultura como un argumento de línea de comando. A menos que sus usuarios cambien culturas a menudo, esto suena como una solución razonable.

Nunca encontré la manera de hacer exactamente lo que pedí en la pregunta. En mi caso, terminé resolviéndolo haciendo que todos mis controles de usuario heredaran de una superclase que contenía esto:

 ///  /// Contains shared logic for all XAML-based Views in the application. /// Views that extend this type will have localization built-in. ///  public abstract class ViewUserControl : UserControl { ///  /// Initializes a new instance of the ViewUserControl class. ///  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); } } 

Cuando el usuario cambia el idioma, creo nuevas instancias de los controles de usuario que se están ejecutando actualmente.

Esto resolvió mi problema. Sin embargo, todavía me gustaría una manera de hacer esto “automáticamente” (es decir, sin tener que hacer un seguimiento de los objetos instanciados).

Solo mis dos centavos: después de casi volverme loco al tratar de implementar los controles WPF de ComponentOne (DataGrid y C1DatePicker) con mi ensamblador de idioma alemán, me topé con esta página.

Esto parece estar dirigiendo de la manera correcta: acabo de ingresar el código anterior en mi rutina App.xaml.cs / Application_startup y ahora el formato alemán de fecha / hora para C1DatePicker finalmente funciona.

Tengo que probar DataGrid justo después de eso.

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

¡Gracias!

Actualización: Probado C1DataGrid para WPF – ¡funciona! Esto resolvió todos los problemas que tuve con la configuración internacional de Fecha / Hora en mis aplicaciones. ¡Estupendo!

Casi tengo el mismo problema.

Encontré esto: http://www.codeproject.com/Articles/35159/WPF-Localization-Using-RESX-Files (podría no ser la fuente original).

Discute una extensión de marcado llamada “UICultureExtension” que se adjunta a la propiedad Language de todos los elementos de framework que necesitan localización (en XAML).

Si plantea un evento de cambio de idioma de IU, los administradores de extensiones estáticas en segundo plano actualizarán todos los elementos de marco registrados.

Adaptive OverrideMetadata

Alguna forma de recarga es inevitable, ya que cambiar la propiedad de Language del control no hace que actualice su texto.

Sin embargo, hay una forma de anular los metadatos que le permite configurarlo una vez y que los controles nuevos usen automáticamente la cultura actual:

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

donde CoerceValueCallback es

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

Por sí solo, esto no es suficiente, porque los controles recién creados obtendrán el valor predeterminado System.Windows.Markup.XmlLanguage.Empty sin que se forzara. Sin embargo, si configura xml:lang="" en su XAML de Windows, se forzará y luego cada control nuevo verá que hereda un valor de su elemento primario y lo forzará. El resultado es que los controles nuevos agregados a esa ventana usarán el idioma actual.

PD Como con muchas cosas en WPF, sería bastante más simple si no hubieran estado tan interesados ​​en mantener las cosas internal . DefaultValueFactory sería una forma mucho más elegante de hacer esto.

Recargando

La manera más extrema, pero por lo tanto confiable, de recargar es simplemente crear una nueva ventana principal y descartar la anterior.

Casi tan extremo como no lo suficiente es arreglar que la configuración de idioma se modifique solo en un panel muy simple de la ventana principal con muy poca carga, y que muy poco se vincule por completo a un modelo de vista que permita forzar una notificación de cambio de propiedad para todo.

Las respuestas existentes a esta pregunta tienen otras sugerencias.

No es completamente tu respuesta, pero utilicé esto para volver a cargar los recursos. Pero aún necesita volver a cargar las ventanas …

  List dictionaryList = new List(); 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); }