Problemas de localización de StringFormat en wpf

112

En WPF 3.5SP1 utilizo la última característica StringFormat en DataBindings:

     <TextBlock Text="{Binding Path=Model.SelectedNoteBook.OriginalDate, StringFormat='f'}"
                FontSize="20" TextTrimming="CharacterEllipsis" />

El problema al que me enfrento es que la fecha siempre está formateada en inglés ... ¿aunque mi sistema está en francés? ¿Cómo puedo forzar que la fecha siga a la fecha del sistema?

Pavel Anikhouski
fuente
14
¡3 años una pregunta altamente calificada pero ninguna respuesta marcada! Rostros tristes por todos lados.
Gusdor

Respuestas:

212
// Ensure the current culture passed into bindings is the OS culture.
// By default, WPF uses en-US as the culture, regardless of the system settings.
FrameworkElement.LanguageProperty.OverrideMetadata(
      typeof(FrameworkElement),
      new FrameworkPropertyMetadata(
          XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));

De la creación de un asistente internacionalizado en WPF

loraderon
fuente
17
Sí, esto es bastante molesto. +1
Szymon Rozga
2
Gracias por resolver mi dolor de cabeza.
Skurmedel
9
Excelente. Pero qué hacer si la cultura cambia durante el ciclo de vida de la aplicación (por ejemplo, el usuario puede cambiar su cultura preferida en un diálogo de configuración). De acuerdo con la documentación FrameworkElement.LanguageProperty.OverrideMetadata no se puede llamar más de una vez (arroja una excepción)
TJKjaer
1
@pengibot Esta solución me funciona. Estoy usando .net 4 / C # / WPF y puse el código en el método OnStartup.
Björn
18
Tenga en cuenta que el elemento Run no se hereda de FrameworkElement , por lo que si vincula fechas, etc. a una ejecución , necesitará una llamada adicional para typeof (System.Windows.Documents.Run)
Mat Fergusson
90

Defina el siguiente espacio de nombres xml:

xmlns:gl="clr-namespace:System.Globalization;assembly=mscorlib"

Ahora he aquí esta fantástica solución:

<TextBlock Text="{Binding Path=Model.SelectedNoteBook.OriginalDate, StringFormat='f', ConverterCulture={x:Static gl:CultureInfo.CurrentCulture}" FontSize="20"TextTrimming="CharacterEllipsis" />

Soy muy consciente de que esta no es una solución global y la necesitará en cada uno de sus enlaces, pero seguramente eso es un buen XAML. Hasta donde yo sé, la próxima vez que se actualice el enlace, se utilizará el correcto CultureInfo.CurrentCultureo lo que haya proporcionado.

Esta solución actualizará inmediatamente sus enlaces con los valores correctos, pero parece una gran cantidad de código para algo tan raro e inofensivo.

Gusdor
fuente
4
¡Excelente! ¡Esto funcionó maravillosamente! No tengo ningún problema en agregar esto a los pocos lugares donde se necesita. Por cierto, a tu ejemplo le falta un}
Johncl
3
Excelente trabajo. Es tan extraño que WPF use inglés de EE. UU. De forma predeterminada, a diferencia de la cultura actual.
Kris Adams
12

Solo quería agregar que la respuesta de loraderon funciona muy bien en la mayoría de los casos. Cuando pongo la siguiente línea de código en mi App.xaml.cs, las fechas en mis TextBlocks están formateadas en la cultura correcta.

FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(System.Windows.Markup.XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));

Digo 'la mayoría de los casos' Por ejemplo, esto funcionará de inmediato:

<TextBlock Text="{Binding Path=Date, StringFormat={}{0:d MMMM yyyy}}" />
--> "16 mei 2013" (this is in Dutch)

... pero al usar Run's en un TextBlock, DateTime se formatea en la cultura predeterminada.

<TextBlock>
  <Run Text="Datum: " />
  <Run Text="{Binding Path=Date, StringFormat={}{0:d MMMM yyyy}, Mode=OneWay}" />
</TextBlock>
--> "Datum: 16 may 2013" (this is in English, notice the
    name of the month "may" vs. "mei")

Para que esto funcione, necesitaba la respuesta de Gusdor , es decir, agregar ConverterCulture = {x: Static gl: CultureInfo.CurrentCulture} al enlace.

<TextBlock>
  <Run Text="Datum: " />
  <Run Text="{Binding Path=Date, StringFormat={}{0:d MMMM yyyy}, ConverterCulture={x:Static gl:CultureInfo.CurrentCulture}, Mode=OneWay}" />
</TextBlock>
--> "Datum: 16 mei 2013" (=Dutch)

Espero que esta respuesta adicional sea de utilidad para alguien.

Daniël Teunkens
fuente
De hecho, Run no se deriva de FrameworkElement. Puede intentar modificar la respuesta de loraderon para repetir su código para la base de Run (FrameworkContentElement) así como para FrameworkElement.
Nathan Phillips
Para aquellos que puedan preguntarse: xmlns: gl = "clr-namespace: System.Globalization; assembly = mscorlib"
Igor Meszaros
11

Simplemente inserte el acceso directo de la cultura a la etiqueta de nivel superior:

xml:lang="de-DE"

p.ej:

<Window x:Class="MyApp"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xml:lang="de-DE"
    Title="MyApp" Height="309" Width="497" Loaded="Window_Loaded">....</Window>
Gerrit Horeis
fuente
5
Pero esto es tan malo como asumir que en-US es la cultura 'Correcta'. Más bien debería tomar la configuración de la máquina del usuario.
nombre inapropiado
Muchas gracias, ¡esto era exactamente lo que estaba buscando! Si WPF cree que en-EN es la cultura correcta para cualquier situación, yo también puedo hacerlo con mi propia localización. Como estoy trabajando en una aplicación de prueba de concepto donde la velocidad de desarrollo está a la orden del día, no hay tiempo para perder el tiempo con docenas de líneas de código solo para conseguir que una sola DatePickerhaga su trabajo, así que esta fácil solución se ¡Rápidamente de vuelta a la pista!
M463
la mejor respuesta para mi caso, finalmente he estado buscando durante años :) y, por supuesto, es correcto, o asume que es en-US o es de-DE ... la gente siempre tiene problemas con soluciones simples -.-
MushyPeas
10

Como ya se indicó, XAML tiene como valor predeterminado la referencia cultural invariante (en-US), y puede usar

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

para establecer la referencia cultural a la referencia cultural predeterminada para el idioma de la referencia cultural actual. Pero el comentario es incorrecto; esto no usa la cultura actual, ya que no verá ninguna personalización que el usuario pueda haber hecho, siempre será la predeterminada para el idioma.

Para utilizar realmente la cultura actual con personalizaciones, deberá configurar el ConverterCulturejunto con StringFormat, como en

Text="{Binding Day, StringFormat='d', ConverterCulture={x:Static gl:CultureInfo.CurrentCulture}}"

con el gldefinido como un espacio de nombres global en su elemento raíz

xmlns:gl="clr-namespace:System.Globalization;assembly=mscorlib"
KZeise
fuente
Si está haciendo esto a través del código en lugar de XAML, es el siguiente:binding.ConverterCulture = System.Globalization.CultureInfo.CurrentCulture;
Metalogic
8

Si necesita cambiar el idioma mientras el programa se está ejecutando, puede simplemente cambiar la propiedad Language en su elemento raíz (no estoy seguro de si esto tiene un efecto instantáneo o si el subelemento tiene que ser recreado, en mi caso esto funciona al menos)

element.Language = System.Windows.Markup.XmlLanguage.GetLanguage(culture.IetfLanguageTag);
Pedro
fuente
se reevalúa de inmediato, pero lamentablemente debe configurarse para cada elemento raíz (ventana) por separado
Firo
6

El código completo para cambiar la localización también en elementos como <Run />este:

Private Shared Sub SetXamlBindingLanguage()

    '' For correct regional settings in WPF (e.g. system decimal / dot or comma) 
    Dim lang = System.Windows.Markup.XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)
    FrameworkContentElement.LanguageProperty.OverrideMetadata(GetType(TextElement), New FrameworkPropertyMetadata(lang))
    FrameworkContentElement.LanguageProperty.OverrideMetadata(GetType(DefinitionBase), New FrameworkPropertyMetadata(lang))
    FrameworkContentElement.LanguageProperty.OverrideMetadata(GetType(FixedDocument), New FrameworkPropertyMetadata(lang))
    FrameworkContentElement.LanguageProperty.OverrideMetadata(GetType(FixedDocumentSequence), New FrameworkPropertyMetadata(lang))
    FrameworkContentElement.LanguageProperty.OverrideMetadata(GetType(FlowDocument), New FrameworkPropertyMetadata(lang))
    FrameworkContentElement.LanguageProperty.OverrideMetadata(GetType(TableColumn), New FrameworkPropertyMetadata(lang))
    FrameworkElement.LanguageProperty.OverrideMetadata(GetType(FrameworkElement), New FrameworkPropertyMetadata(lang))

End Sub
habacuc
fuente
0

Si desea cambiar la información cultural en tiempo de ejecución, puede usar un comportamiento (ver más abajo)

  public class CultureBehavior<TControl> : Behavior<TControl>
    where TControl : FrameworkElement
{
    private readonly IEventAggregator _eventAggregator;
    private readonly Action<CultureInfo> _handler;

    public CultureBehavior()
    {
        _handler = (ci) => this.AssociatedObject.Language = XmlLanguage.GetLanguage(ci.IetfLanguageTag);
        _eventAggregator = IoC.Container.Resolve<IEventAggregator>();
    }

    protected override void OnAttached()
    {
        base.OnAttached();

        _eventAggregator
            .GetEvent<LanguageChangedEvent>()
            .Subscribe(_handler);

        _handler.Invoke(CultureInfo.CurrentCulture);
    }

    protected override void OnDetaching()
    {
        _eventAggregator
            .GetEvent<LanguageChangedEvent>()
            .Unsubscribe(_handler);

        base.OnDetaching();
    }
}
user3173620
fuente
0

Si está trabajando en código en lugar de XAML, puede configurar ConverterCulture de la siguiente manera:

binding.ConverterCulture = System.Globalization.CultureInfo.CurrentCulture;

Felicitaciones a @KZeise por señalar la sutil diferencia entre usar la definición de cultura predeterminada y usar la definición de cultura personalizada del usuario.

Metalogic
fuente
-3

Use Label (incluida Cultture) y no texblock

david
fuente