Используйте настоящую CultureInfo.CurrentCulture в привязке WPF, а не CultureInfo из IetfLanguageTag

В моем случае:

У меня есть привязка TextBlock к свойству типа DateTime. Я хочу, чтобы он отображался в соответствии с региональными настройками пользователя.

<TextBlock Text="{Binding Date, StringFormat={}{0:d}}" />

Я устанавливаю свойство Language как WPF XAML Bindings и CurrentCulture Display говорит:

this.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag);

Но с этой строкой кода он просто отображает текст в качестве формата по умолчанию для CultureInfo с IetfLanguageTag из CurrentCulture, а не в соответствии с эффективным значением, выбранным в настройках региона системы:

(например, для "de-DE" вместо выбранного yyyy-MM-dd используется dd.MM.yyyy)

Настройки региона: не по умолчанию, но используется yyy-MM-dd

Есть ли способ, которым Binding использует правильный формат без определения ConverterCulture для каждой привязки?

В коде

string.Format("{0:d}",Date);

использует правильные настройки культуры.

изменить:

другой способ, который не работает должным образом (например, this.Language = ... работает):

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

и

<Binding Source="{x:Static glob:CultureInfo.CurrentCulture}" 
 Path="IetfLanguageTag" 
 ConverterCulture="{x:Static glob:CultureInfo.InvariantCulture}" />

person Markus k    schedule 29.04.2011    source источник
comment
Обратите внимание на следующее: stackoverflow.com/questions / 2764615 /   -  person Pavlo Glazkov    schedule 29.04.2011
comment
@Pavlo: ответ на этот вопрос также использует IetfLanguageTag для получения CultureInfo.   -  person Markus k    schedule 29.04.2011


Ответы (9)


Вы можете создать подкласс привязки (например, CultureAwareBinding), который автоматически устанавливает ConverterCulture на текущую культуру при создании.

Это не идеальное решение, но, вероятно, единственное, поскольку задним числом принуждение привязки к культуре может нарушить другой код в WPF, который зависит от этого поведения.

Дай мне знать, если тебе еще понадобится помощь!

person aKzenT    schedule 09.05.2011
comment
Спасибо за ваш ответ. это решение похоже на единственно возможное. - person Markus k; 10.05.2011
comment
поскольку ретроактивное принуждение привязки к соблюдению культуры может нарушить другой код в WPF, который зависит от этого поведения. - Я бы сказал, что в долгосрочной перспективе хуже поддерживать такое нарушенное поведение, чем вводить краткосрочный перерыв. Или они могли бы исправить это, но использовать функции привязки сборки .NET, чтобы определить, когда приложение ожидает более старую версию и поддерживает для них нарушенное поведение. Безумие, что эта ошибка существует в WPF уже 12 лет, и у них нет собственной встроенной CultureAwareBinding. - person Dai; 03.11.2017

Это продолжение ответа от aKzenT. Они предложили создать подкласс класса Binding и установить для ConverterCulture значение CurrentCulture. Несмотря на то, что ответ очень прост, я чувствую, что некоторым людям может быть не очень удобно его реализовывать, поэтому я делюсь версией кода ответа aKzenT с примером того, как использовать его в XAML.

using System;
using System.Globalization;
using System.Windows.Data;

namespace MyWpfLibrary
{
    public class CultureAwareBinding : Binding
    {
        public CultureAwareBinding()
        {
            ConverterCulture = CultureInfo.CurrentCulture;
        }
    }
}

Пример того, как использовать это в XAML

1) Вам необходимо импортировать пространство имен в файл XAML:

<Page
    ...
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:myWpfLib="clr-namespace:MyWpfLibrary;assembly=<assembly_name>"
    ...
>

2) Использование CultureAwareBinding в реальном мире

<Textblock Text="{myWpfLib:CultureAwareBinding Path=Salary, Source=Contact, StringFormat={}{0:C}}" />
person Parth Shah    schedule 05.11.2013
comment
Мне нравится ваше подробное объяснение по этому поводу. Есть ли какие-либо предложения по исправлению ошибок для определенных привязок? В результате получается что-то вроде того, что тип CultureAwareBinding не включает конструктор с указанным количеством аргументов. - person Poken1151; 12.10.2014
comment
Я еще не видел этой ошибки. Мне нужно было бы увидеть код, чтобы определить, почему это могло произойти. Возможно, вы могли бы опубликовать новый вопрос о переполнении стека и добавить ссылку на него здесь? - person Parth Shah; 13.10.2014
comment
Спасибо, ошибку исправил. Кодирование было плохим, очевидно, {Binding ITEM-NAME ....} нехорошо, мне нужен {Binding Path = ITEM-NAME, ...}. Это разрешило мою ошибку (которая все равно компилируется в обоих случаях) - person Poken1151; 14.10.2014
comment
@ Poken1151 Вы также можете определить дополнительный конструктор для CultureAwareBinding, который принимает строку в качестве единственного параметра: public CultureAwareBinding(string path) : base(path) { ConverterCulture = CultureInfo.CurrentCulture; }. Таким образом, вам не нужно указывать Path=…. - person linac; 19.05.2016

Поместите следующую строку кода перед инициализацией любого пользовательского интерфейса. Это сработало для меня.

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

(И удалите все явные параметры культуры)

person Alan Hauge    schedule 25.07.2016
comment
Работает как шарм и представляет собой всего одну строчку кода без необходимости касаться какого-либо другого кода. Я не понимаю, почему это не принятый ответ и почему у него так мало голосов. Есть ли подводные камни? - person Aaginor; 29.08.2018
comment
Как было указано в вопросе, установка языка заставляет WPF использовать только языковые форматы даты и времени по умолчанию, а не форматы, выбранные в настройках региона системы. - person Collin K; 09.11.2018
comment
Я поместил этот код в свой метод App.xaml.cs Application_Startup и работал хорошо. Теперь datetime правильно отформатирован в соответствии с моей системной локалью. - person Guillermo Espert; 26.08.2020
comment
@CollinK Он отлично работает не только для форматов даты / времени, но и для других региональных настроек, например десятичный разделитель (это не точка, а запятая во многих региональных настройках системы). - person Giulio; 18.07.2021

Ваша вторая попытка была близка и привела меня к решению, которое действительно работает для меня.

Проблема с настройкой ConverterCulture заключается в том, что она используется только при наличии Converter. Так что просто создайте простой StringFormatConverter, который принимает формат в качестве параметра:

public sealed class StringFormatConverter : IValueConverter
{
    private static readonly StringFormatConverter instance = new StringFormatConverter();
    public static StringFormatConverter Instance
    {
        get
        {
            return instance;
        }
    }

    private StringFormatConverter()
    {
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return string.Format(culture, (string)parameter, value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Затем вы можете настроить привязку (при условии, что вы импортировали пространство имен конвертера как «мое»).

<TextBlock Text="{Binding Date, Converter={x:Static my:StringFormatConverter.Instance}, ConverterCulture={x:Static glob:CultureInfo.CurrentCulture}, ConverterParameter={}{0:d}}" />
person Cheetah    schedule 25.08.2011

Я использую этот код для получения нужных результатов в соответствии с моими потребностями. Надеюсь, он заполнит ваш :-)! Возможно, вам лучше выбросить исключение, если не удается выполнить "TryParse". Вам решать.

public sealed class CurrentCultureDoubleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((double)value).ToString((string)parameter ?? "0.######", CultureInfo.CurrentCulture);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        double result;
        if (Double.TryParse(value as string, NumberStyles.Number, CultureInfo.CurrentCulture, out result))
        {
            return result;
        }

        throw new FormatException("Unable to convert value:" + value);
        // return value;
    }
}

Использование:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:simulatorUi="clr-namespace:SimulatorUi"
        xmlns:Converter="clr-namespace:HQ.Wpf.Util.Converter;assembly=WpfUtil" x:Class="SimulatorUi.DlgTest"
        Title="DlgTest" Height="300" Width="300">
    <Window.DataContext>
        <simulatorUi:DlgTestModel/>
    </Window.DataContext>

    <Window.Resources>
        <Converter:CurrentCultureDoubleConverter x:Key="CurrentCultureDoubleConverter"/>
    </Window.Resources>

    <Grid>
        <TextBox Text="{Binding DoubleVal, Converter={StaticResource CurrentCultureDoubleConverter}}"/>
    </Grid>
</Window>
person Eric Ouellet    schedule 21.04.2015

Я придумал обходной путь, позволяющий избежать обновления всех ваших привязок. Добавьте этот код в конструктор вашего главного окна.

XmlLanguage language = XmlLanguage.GetLanguage("My-Language");
typeof(XmlLanguage).GetField("_compatibleCulture", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(language, CultureInfo.CurrentCulture);
this.Language = language;

Поскольку он использует отражение, нет никаких гарантий, что он будет работать в будущем, но пока он работает (.NET 4.6).

person Joost van den Boom    schedule 26.11.2015

Мы можем создать DateTime Converter с помощью IValueConverter

[ValueConversion(typeof(DateTime), typeof(String))]
    class DateTimeToLocalConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!(value is DateTime)) return "Invalid DateTime";
            DateTime DateTime = (DateTime)value;
            return DateTime.ToLocalTime().ToShortDateString();

        }


        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }


    }

Примените это в XAML, как показано ниже.

Binding="{Binding Path=createdDateTime,Converter={StaticResource DateTimeConverter}}"

Также измените текущую культуру, чтобы получить желаемый формат, и то же самое необходимо применить при запуске приложения.

/// <summary>
        /// Set Culture
        /// </summary>
        private void SetCulture() {
            var newCulture = new CultureInfo("en-IN");
            newCulture.DateTimeFormat.ShortDatePattern = "dd-MMM-yyyy";
            newCulture.DateTimeFormat.LongDatePattern = "dd-MMM-yyyy";
            newCulture.DateTimeFormat.FullDateTimePattern = "dd-MMM-yyyy";
            CultureInfo.DefaultThreadCurrentCulture = newCulture;
            CultureInfo.DefaultThreadCurrentUICulture = newCulture;
            Thread.CurrentThread.CurrentCulture = newCulture;
            Thread.CurrentThread.CurrentUICulture = newCulture;
            FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(
                System.Windows.Markup.XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
        }
person ASHOK MANGHAT    schedule 10.01.2019

Как насчет изменения языка в коде позади?

this.Language = XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentCulture.Name);
person Jostein Lie Sand    schedule 19.07.2012
comment
Этот метод игнорирует пользовательские настройки в диалоговом окне «Регион и язык» и просто использует имя для определения языка. - person Markus k; 23.07.2012
comment
Это самое простое решение, которое отлично работает и для меня (Windows Phone 7). @Markus k: Извините, я не понял, что вы пытаетесь сказать / объяснить? - person hfrmobile; 08.09.2012
comment
@hfmobile Он имел в виду, что если вы выберете язык в региональных настройках (например, немецкий, Германия), а затем вручную измените любую из настроек (например, изменение короткой даты на dMyyyy), установка языка, предложенного выше, не подберет новый формат. - person Goran; 05.07.2014

Проблема, которую можно избежать с помощью "this.Language = XmlLanguage.GetLanguage (Thread.CurrentThread.CurrentCulture.Name);" не очень распространенный. Я не знаю ни одного пользователя здесь, во Франции, который изменит формат даты на формат даты в США или Японии, просто потому, что, по крайней мере, ни один пользователь не знает, что такое изменение возможно (и не знает, как это сделать) ... Итак, Конечно, «language =» не идеален, но за многие годы практики WPF и Silverlight я никогда не видел проблем подобного рода ни с одним пользователем ... Так что я все еще использую уловку «Langage =», она проста и покрывает 100% реальных потребностей. Конечно, другие решения кажутся лучше, но в этом нет необходимости (и я видел несколько реализаций, которые далеки от совершенства по сравнению с решением "language =").

person Olivier    schedule 23.08.2014
comment
@ Olivier: просто сказать, что другие пользователи даже не знают о такой опции, исходя из моих многолетних знаний WPF и Silverlight, которые я знаю лучше всего, или даже утверждаю, что кто-то знает настоящие потребности людей, - это высокомерие. Вы ничего не знаете о предпочтениях почти 99% жителей этой планеты, как и все остальные разработчики. Примите то, что в этом есть необходимость, и помогите. Использование ГГГГ-ММ-ДД - это формат даты, с которым я хотел бы, чтобы весь мир скорее согласился, чем использовал свои региональные. ИМХО, от большого к маленькому полезнее, особенно когда дело доходит до сортировки, чем любой другой формат. - person Yoda; 26.10.2016