Problemas de localização de StringFormat no wpf

112

No WPF 3.5SP1 eu uso o último recurso StringFormat em DataBindings:

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

O problema que enfrento é que a data é sempre formatada em inglês ... embora meu sistema seja em francês? Como posso forçar a data a seguir a data do sistema?

Pavel Anikhouski
fonte
14
3 anos uma pergunta bem avaliada, mas nenhuma resposta marcada! Rostos tristes por toda parte.
Gusdor

Respostas:

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

Da criação de um assistente internacionalizado no WPF

Loraderon
fonte
17
Sim, isso é muito chato. +1
Szymon Rozga
2
Obrigado por resolver minha dor de cabeça.
Skurmedel
9
Ótimo. Mas o que fazer se a cultura mudar durante o ciclo de vida do aplicativo (por exemplo, o usuário pode mudar sua cultura preferida em uma caixa de diálogo de configurações). De acordo com a documentação FrameworkElement.LanguageProperty.OverrideMetadata não pode ser chamado mais de uma vez (isso gera uma exceção)
TJKjaer
1
@pengibot Esta solução funciona para mim. Estou usando .net 4 / C # / WPF e coloquei o código no método OnStartup.
Björn
18
Observe que o elemento Run não herda de FrameworkElement , portanto, se você vincular datas etc. a um Run , será necessária uma chamada extra para typeof (System.Windows.Documents.Run)
Mat Fergusson
90

Defina o seguinte namespace xml:

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

Agora, observe esta solução fantástica:

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

Estou bem ciente de que essa não é uma correção global e você precisará dela em cada um de seus Bindings, mas certamente isso é apenas um bom XAML. Pelo que eu sei, da próxima vez que a ligação for atualizada, ela usará o correto CultureInfo.CurrentCultureou o que você forneceu.

Esta solução atualizará imediatamente seus Bindings com os valores corretos, mas parece muito código para algo tão raro e inócuo.

Gusdor
fonte
4
Excelente! Isso funcionou maravilhosamente bem! Não tenho nenhum problema em adicionar isso aos poucos lugares onde é necessário. A
propósito
3
Excelente trabalho. É tão estranho que o WPF use o inglês americano por padrão, ao contrário da cultura atual.
Kris Adams
12

Eu só queria acrescentar que a resposta de Loraderon funciona muito bem na maioria dos casos. Quando coloco a seguinte linha de código em meu App.xaml.cs, as datas em meus TextBlocks são formatadas na cultura correta.

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

Eu digo 'a maioria dos casos'. Por exemplo, isso funcionará fora da caixa:

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

... mas ao usar Run's em um TextBlock, o DateTime é formatado na cultura padrão.

<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 isso funcionasse, eu precisava da resposta de Gusdor , a saber, adicionar ConverterCulture = {x: Static gl: CultureInfo.CurrentCulture} ao Binding.

<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 resposta adicional seja útil para alguém.

Daniël Teunkens
fonte
Na verdade, Run não deriva de FrameworkElement. Você poderia tentar modificar a resposta de loraderon para repetir seu código para a base de Run (FrameworkContentElement), bem como para FrameworkElement.
Nathan Phillips
Para aqueles que podem se perguntar: xmlns: gl = "clr-namespace: System.Globalization; assembly = mscorlib"
Igor Meszaros
11

Basta inserir o atalho de cultura para a tag de nível superior:

xml:lang="de-DE"

por exemplo:

<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
fonte
5
Mas isso é tão ruim quanto assumir que en-US é a cultura 'correta'. Em vez disso, ele deve pegar as configurações da máquina do usuário.
nome impróprio
Muito obrigado, era exatamente isso que eu procurava! Se WPF pensa que en-EN é a cultura correta para qualquer situação, eu também posso com minha própria localização. Como estou trabalhando em um aplicativo de prova de conceito em que a velocidade de desenvolvimento está na ordem do dia, não há tempo para mexer em dezenas de linhas de código apenas para obter uma única DatePickerpara fazer o seu trabalho, então esta solução fácil foi me rapidamente de volta aos trilhos!
M463 de
melhor resposta para o meu caso, finalmente estou procurando há muito tempo :) e claro que está correto, ou você assume que é en-US ou é de-DE ... as pessoas sempre têm problemas com soluções simples -.-
MushyPeas
10

Como já foi dito, o padrão de XAML é a cultura invariável (en-US) e você pode usar

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

para definir a cultura para a cultura padrão para o idioma da cultura atual. Mas o comentário está errado; isso não usa a cultura atual, pois você não verá nenhuma personalização que o usuário possa ter feito, sempre será o padrão para o idioma.

Para realmente usar a cultura atual com personalizações, você precisará definir o ConverterCulturejunto com o StringFormat, como em

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

com o gldefinido como um namespace global em seu elemento raiz

xmlns:gl="clr-namespace:System.Globalization;assembly=mscorlib"
KZeise
fonte
Se você estiver fazendo isso por meio de código em vez de XAML, é o seguinte:binding.ConverterCulture = System.Globalization.CultureInfo.CurrentCulture;
Metalogic
8

Se você precisar alterar o idioma enquanto o programa está sendo executado, você pode apenas alterar a propriedade Language em seu elemento raiz (não tenho certeza se isso tem um efeito instantâneo ou se o subelemento precisa ser recriado, no meu caso isso funciona pelo menos)

element.Language = System.Windows.Markup.XmlLanguage.GetLanguage(culture.IetfLanguageTag);
Peter
fonte
ele reavalia imediatamente, mas infelizmente precisa ser definido para cada rootelement (janela) separadamente
Firo
6

O código completo para mudar a localização também em 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
habakuk
fonte
0

Se você quiser mudar as informações de cultura em tempo de execução, você pode usar um comportamento (veja abaixo)

  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
fonte
0

Se você estiver trabalhando com código em vez de XAML, pode definir o ConverterCulture da seguinte maneira:

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

Parabéns a @KZeise por apontar a diferença sutil entre usar a definição de cultura padrão e usar a definição de cultura personalizada do usuário.

Metalogic
fonte
-3

Use Label (incluindo Cultture) e não texblock

david
fonte