Problemy z lokalizacją StringFormat w wpf

112

W WPF 3.5SP1 używam ostatniej funkcji StringFormat w DataBindings:

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

Problem, z którym się spotykam, polega na tym, że data jest zawsze sformatowana w języku angielskim ... chociaż mój system jest w języku francuskim? Jak mogę wymusić, aby data była zgodna z datą systemową?

Pavel Anikhouski
źródło
14
3 lata to wysoko oceniane pytanie, ale bez odpowiedzi! Wszędzie smutne twarze.
Gusdor,

Odpowiedzi:

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

Od tworzenia międzynarodowego kreatora w WPF

loraderon
źródło
17
Tak, to jest dość denerwujące. +1
Szymon Rozga
2
Dziękuję za rozwiązanie mojego bólu głowy.
Skurmedel
9
Wspaniały. Ale co zrobić, jeśli kultura zmienia się w trakcie cyklu życia aplikacji (np. Użytkownik może zmienić preferowaną kulturę w oknie dialogowym ustawień). Zgodnie z dokumentacją FrameworkElement.LanguageProperty.OverrideMetadata nie można wywołać więcej niż jeden raz (zgłasza wyjątek)
TJKjaer
1
@pengibot To rozwiązanie działa dla mnie. Używam .net 4 / C # / WPF i umieszczam kod w metodzie OnStartup.
Björn
18
Zwróć uwagę, że element Run nie dziedziczy po FrameworkElement , więc jeśli powiążesz daty itp. Z Run , będziesz potrzebować dodatkowego wywołania typeof (System.Windows.Documents.Run)
Mat Fergusson
90

Zdefiniuj następującą przestrzeń nazw xml:

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

Teraz spójrz na tę fantastyczną poprawkę:

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

Zdaję sobie sprawę, że to nie jest globalna poprawka i będziesz jej potrzebować na każdym z Twoich powiązań, ale na pewno jest to po prostu dobry XAML? O ile mi wiadomo, następnym razem, gdy wiążąca aktualizacja użyje poprawnego CultureInfo.CurrentCulturelub tego, co dostarczyłeś.

To rozwiązanie natychmiast zaktualizuje Twoje Bindings o poprawne wartości, ale wygląda na to, że zawiera dużo kodu dla czegoś tak rzadkiego i nieszkodliwego.

Gusdor
źródło
4
Doskonały! To zadziałało cudownie! Nie mam problemu z dodaniem tego do kilku miejsc, w których jest to potrzebne. Przy okazji, w twoim przykładzie brakuje}
Johncl
3
Wspaniała robota. To tak dziwne, że WPF domyślnie używa języka angielskiego w wersji amerykańskiej, w przeciwieństwie do bieżącej kultury.
Kris Adams
12

Chciałem tylko dodać, że odpowiedź loraderona działa świetnie w większości przypadków. Kiedy umieszczam następujący wiersz kodu w moim App.xaml.cs, daty w moich TextBlocks są formatowane w poprawnej kulturze.

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

Mówię `` w większości przypadków '', na przykład to zadziała po wyjęciu z pudełka:

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

... ale w przypadku używania Run w TextBlock DateTime jest formatowany w domyślnej kulturze.

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

Aby to zadziałało, potrzebowałem odpowiedzi Gusdora , a mianowicie dodania ConverterCulture = {x: Static gl: CultureInfo.CurrentCulture} do 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)

Mam nadzieję, że ta dodatkowa odpowiedź komuś się przyda.

Daniël Teunkens
źródło
Rzeczywiście, Run nie pochodzi od FrameworkElement. Możesz spróbować zmodyfikować odpowiedź loraderona, aby powtórzyć jego kod dla podstawy Run (FrameworkContentElement), jak również dla FrameworkElement.
Nathan Phillips
Dla tych, którzy mogą się zastanawiać: xmlns: gl = "clr-namespace: System.Globalization; assembly = mscorlib"
Igor Meszaros
11

Po prostu wstaw skrót kultury do tagu najwyższego poziomu:

xml:lang="de-DE"

na przykład:

<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
źródło
5
Ale to jest tak samo złe, jak założenie, że en-US to kultura „poprawna”. Powinien raczej pobrać ustawienia z komputera użytkownika.
błędne określenie
Dziękuję bardzo, właśnie tego szukałem! Jeśli WPF uważa, że ​​en-EN jest właściwą kulturą dla każdej sytuacji, mogę to zrobić z własną lokalizacją. Ponieważ pracuję nad aplikacją weryfikującą koncepcję, w której szybkość tworzenia jest na porządku dziennym, nie ma czasu na majstrowanie przy dziesiątkach linii kodu tylko po to, aby jeden DatePickerwykonał swoją pracę, więc ta łatwa poprawka szybko wracam na właściwe tory!
M463,
najlepsza odpowiedź na mój przypadek, w końcu szukałem od lat :) i oczywiście jest poprawna, albo zakładasz, że jest en-US, albo jest de-DE ... ludzie zawsze mają problemy z prostymi rozwiązaniami -.-
MushyPeas
10

Jak już wspomniano, XAML domyślnie przyjmuje niezmienną kulturę (en-US) i można użyć

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

aby ustawić kulturę na kulturę domyślną dla języka bieżącej kultury. Ale komentarz jest błędny; to jest nie używać aktualnej kultury, jak nie będzie widać żadnych dostosowań użytkownik może dokonane, to zawsze będzie domyślnym dla danego języka.

Aby faktycznie używać bieżącej kultury z dostosowaniami, musisz ustawić ConverterCulturerazem z StringFormat, jak w

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

ze glzdefiniowaną jako globalną przestrzenią nazw w elemencie głównym

xmlns:gl="clr-namespace:System.Globalization;assembly=mscorlib"
KZeise
źródło
Jeśli robisz to za pomocą kodu zamiast XAML, wygląda to następująco:binding.ConverterCulture = System.Globalization.CultureInfo.CurrentCulture;
Metalogic
8

Jeśli chcesz zmienić język, gdy program jest uruchomiony, możesz po prostu zmienić właściwość Język w elemencie głównym (nie jestem pewien, czy ma to natychmiastowy efekt, czy też element podrzędny musi zostać odtworzony, w moim przypadku działa to przynajmniej)

element.Language = System.Windows.Markup.XmlLanguage.GetLanguage(culture.IetfLanguageTag);
Piotr
źródło
natychmiast dokonuje ponownej oceny, ale niestety musi być ustawione dla każdego rootelementu (okna) osobno
Firo
6

Pełny kod do zmiany lokalizacji również w elementach takich jak <Run />:

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
źródło
0

Jeśli chcesz zmienić informacje o kulturze w czasie wykonywania, możesz użyć zachowania (patrz poniżej)

  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
źródło
0

Jeśli pracujesz nad kodem, a nie XAML, możesz ustawić ConverterCulture w następujący sposób:

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

Uznanie dla @KZeise za wskazanie subtelnej różnicy między użyciem domyślnej definicji kultury a użyciem niestandardowej definicji kultury użytkownika.

Metalogiczne
źródło
-3

Użyj etykiety (w tym Cultture), a nie texblock

david
źródło