Как установить и изменить культуру в WPF - PullRequest
53 голосов
/ 28 октября 2010

У меня есть приложение .NET 4.0 WPF, в котором пользователь может изменить язык (культуру). Я просто позволяю пользователю выбрать язык, создать соответствующий CultureInfo и установить:

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

В коде C # это работает нормально. Однако в контроле WPF культура все еще в США. Например, это означает, что даты будут отображаться в формате США, а не в соответствии с текущей культурой.

Видимо, это не ошибка. Согласно MSDN и нескольким постам в блоге и статьям о StackOverflow, язык WPF автоматически не соответствует текущей культуре. Это en-US, пока вы не сделаете это:

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

См., Например, Проблемы локализации StringFormat в wpf .

Я не совсем понимаю, что здесь происходит. Похоже, что свойство Language на всех frameworkelements установлено в соответствии с текущей культурой. Во всяком случае, это работает. Я делаю это, когда приложение запускается, и теперь все элементы управления работают, как ожидалось, и, например, даты форматируются в соответствии с текущей культурой.

Но теперь проблема: Согласно MSDN FrameworkElement.LanguageProperty.OverrideMetadata можно вызвать только один раз. И действительно, если я позвоню еще раз (когда пользователь сменит язык), он выдаст исключение. Так что я действительно не решил свою проблему.

Вопрос: как можно надежно обновлять культуру в WPF более одного раза и в любое время в жизненном цикле моих приложений?

(Я нашел это при исследовании: http://www.nbdtech.com/Blog/archive/2009/03/18/getting-a-wpf-application-to-pick-up-the-correct-regional.aspx и похоже у него там что-то работает. Тем не менее, я не могу себе представить, как это сделать в моем приложении. Кажется, мне нужно обновить язык во всех открытых окнах и элементах управления и обновить все существующие привязки и т. Д.)

Ответы [ 7 ]

8 голосов
/ 06 февраля 2014

Я собираюсь здесь перезвонить.

Я успешно сделал это, используя метод OverrideMetadata(), о котором упоминал ОП:

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

Но в моем WPF все еще встречались случаи, когда системная культура применялась для дат и числовых значений. Оказалось, это были значения в <Run> элементах. Это происходило потому, что класс System.Windows.Documents.Run не наследуется от System.Windows.FrameworkElement, и поэтому переопределение метаданных для FrameworkElement, очевидно, не имело никакого эффекта.

System.Windows.Documents.Run наследует свое свойство Language от System.Windows.FrameworkContentElement.

И поэтому очевидным решением было переопределить метаданные на FrameworkContentElement таким же образом. Увы, выполнение do сгенерировало исключение ( PropertyMetadata уже зарегистрировано для типа System.Windows.FrameworkContentElement ), поэтому мне пришлось сделать это для следующего предка-потомка Run вместо System.Windows.Documents.TextElement:

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

И это решило все мои проблемы.

Есть еще несколько подклассов FrameworkContentElement (перечислены здесь ), для полноты которых также должны быть переопределены их метаданные.

5 голосов
/ 16 ноября 2010

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

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

Когда пользователь меняет язык, я создаю новые экземпляры любых пользовательских элементов управления, которые в данный момент выполняются.

Это решило мою проблему. Тем не менее, я все еще хотел бы сделать это «автоматически» (т.е. без необходимости отслеживать какие-либо экземпляры объектов).

5 голосов
/ 08 ноября 2010

Я не уверен, как обойти исключение «не могу вызвать OverrideMetadata несколько раз».

В качестве обходного пути, когда пользователь изменяет культуры пользовательского интерфейса в вашем приложении, вы можете перезапустить свое приложение с этой культурой, передавая новую культуру в качестве аргумента командной строки. Если ваши пользователи не будут часто менять культуру, это звучит как разумное решение.

4 голосов
/ 11 февраля 2013

Только мои два цента: После того, как я почти сошел с ума при попытке реализовать элементы управления WPF ComponentOne (DataGrid и C1DatePicker) с моей немецкой языковой сборкой, я наткнулся на эту страницу.

Кажется, это правильно: я только что ввел вышеуказанный код в свою подпрограмму App.xaml.cs / Application_startup, и теперь немецкое форматирование даты / времени для C1DatePicker наконец работает.

Должен сразу после этого протестировать DataGrid.

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

Спасибо!

Обновление: Проверено C1DataGrid для WPF - работает!Это решило все проблемы, которые у меня были с международными настройками даты / времени в моих приложениях.Отлично!

3 голосов
/ 28 января 2016

Adaptive OverrideMetadata

Некоторая форма перезагрузки неизбежна, поскольку изменение свойства Language элемента управления не приводит к обновлению его текста.

Однако есть способ переопределения метаданных, который позволяет вам установить их один раз и иметь новые элементы управления, автоматически использующие текущую культуру:

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

, где CoerceValueCallback равно

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

Само по себе этого недостаточно, поскольку вновь созданные элементы управления получат значение по умолчанию System.Windows.Markup.XmlLanguage.Empty без принудительного применения. Однако, если вы затем установите xml:lang="" в XAML вашего окна, это будет принудительно выполнено, и тогда каждый новый элемент управления увидит, что он наследует значение от своего родителя, и будет принудительно его приводить. В результате новые элементы управления, добавленные в это окно, будут использовать текущий язык.

PS Как и во многих вещах в WPF, было бы немного проще, если бы они не были так заинтересованы в сохранении вещей internal. DefaultValueFactory было бы гораздо более элегантным способом сделать это.

Перезарядка

Самый экстремальный, но надежный способ перезагрузки - просто создать новое главное окно и удалить старое.

Почти столь же экстремальным, но не совсем, является организация изменения языковых настроек только в очень простой панели главного окна с очень небольшим количеством загруженных, и это очень мало, чтобы быть полностью привязанным к модели представления, которая поддерживает форсирование свойства изменил уведомление для всего.

Существующие ответы на этот вопрос имеют другие предложения.

3 голосов
/ 24 июля 2013

У меня почти такая же проблема.

Я нашел это: http://www.codeproject.com/Articles/35159/WPF-Localization-Using-RESX-Files (может не быть оригинальным источником).

В нем обсуждается расширение разметки с именем «UICultureExtension», которое присоединяется к свойству Language всех элементов инфраструктуры, которые нуждаются в локализации (в XAML).

Если вы вызываете событие изменения языка пользовательского интерфейса, менеджеры статических расширений в фоновом режиме обновят все зарегистрированные элементы каркаса.

1 голос
/ 09 ноября 2010

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

 List<Uri> dictionaryList = new List<Uri>();
        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);
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...