CurrentUICulture忽略区域和语言设置 [英] CurrentUICulture ignores Region and Language settings

查看:105
本文介绍了CurrentUICulture忽略区域和语言设置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Windows 7的区域和语言"对话框中的各种设置为CurrentCulture对象的属性提供值.但是,WPF控件似乎改用CurrentUICulture,导致完全无法尊重用户的首选项.

Assorted settings in the Windows 7 Region and Language dialog supply values to the properties of the CurrentCulture object. However, WPF controls seem to use CurrentUICulture instead, resulting in a total failure to respect the user's preferences.

例如,在我的工作站上,WPF控件似乎使用的是美国英语CurrentUICulture,这使它们以美国格式M/d/yyyy而不是在区域和语言"对话框中指定的澳大利亚格式显示日期.

On my workstation, for example, WPF controls seem to use CurrentUICulture which is en-US, causing them to display dates with the American format M/d/yyyy rather than the Australia format specified in the Region and Language dialog.

在数据绑定中明确指定en-AU的区域性会导致所涉及的控件使用默认的澳大利亚格式,但是它将继续忽略用户指定的格式.这很奇怪;进入应用程序后,我验证了DateTimeFormatInfo.CurrentInfo == Thread.CurrentThread.CurrentCulture.DateTimeFormat(相同的对象)和DateTimeFormatInfo.CurrentInfo.ShortDatePattern =="yyyy-MM-dd"(我设置的值,以便可以确定用户偏好设置或默认值被选中).一切都按预期进行,因此面对现实,最大的问题是如何说服WPF控件和数据绑定使用CurrentCulture而不是CurrentUICulture.

Explicitly specifying a culture of en-AU in a databinding causes the control in question to to use default Australian formats, but it continues to ignore the user specified formats. This is odd; stepping into the app I verified that DateTimeFormatInfo.CurrentInfo == Thread.CurrentThread.CurrentCulture.DateTimeFormat (same object) and DateTimeFormatInfo.CurrentInfo.ShortDatePattern == "yyyy-MM-dd" (a value I set so I could determine whether user preferences or defaults were being picked up). Everything was as expected, so on the face of things the big question is how to persuade WPF controls and databindings to use CurrentCulture rather than CurrentUICulture.

我们应该如何使WPF应用程序尊重区域和语言设置?

How are we supposed to get WPF apps to respect the Region and Language settings?

基于Sphinxx的答案,我覆盖了Binding类的两者构造函数,以提供与标准标记的更完全兼容性.

Building on Sphinxx's answer, I overrode both constructors of the Binding class to provide more complete compatibility with standard markup.

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

namespace ScriptedRoutePlayback
{
  public class Bind : Binding
  {
    public Bind()
    {
      ConverterCulture = CultureInfo.CurrentCulture;
    }
    public Bind(string path) : base(path)
    {
      ConverterCulture = CultureInfo.CurrentCulture;
    }
  }
}


进一步的实验表明,您可以使用x:Static在标记中引用System.Globalization.CultureInfo.CurrentCulture.在运行时这是完全成功的,但在设计时却是灾难,因为绑定编辑器不断删除它.更好的解决方案是一个帮助程序类,以遍历窗口的DOM并修复它找到的每个Binding的ConverterCulture.


Further experimentation reveals that you can use x:Static to reference System.Globalization.CultureInfo.CurrentCulture in markup. This is a complete success at run-time but a disaster at design-time because the binding editor keeps removing it. A better solution is a helper class to traverse the DOM of a window and fix up the ConverterCulture of every Binding it finds.

using System;
using System.Windows;
using System.Windows.Data;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;

namespace ScriptedRoutePlayback
{
  public static class DependencyHelper
  {
    static Attribute[] __attrsForDP = new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues | PropertyFilterOptions.UnsetValues | PropertyFilterOptions.Valid) };

    public static IList<DependencyProperty> GetProperties(Object element, bool isAttached = false)
    {
      if (element == null) throw new ArgumentNullException("element");

      List<DependencyProperty> properties = new List<DependencyProperty>();

      foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(element, __attrsForDP))
      {
        DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(pd);
        if (dpd != null && dpd.IsAttached == isAttached)
        {
          properties.Add(dpd.DependencyProperty);
        }
      }

      return properties;
    }

    public static IEnumerable<Binding> EnumerateBindings(DependencyObject dependencyObject)
    {
      if (dependencyObject == null) throw new ArgumentNullException("dependencyObject");

      LocalValueEnumerator lve = dependencyObject.GetLocalValueEnumerator();

      while (lve.MoveNext())
      {
        LocalValueEntry entry = lve.Current;

        if (BindingOperations.IsDataBound(dependencyObject, entry.Property))
        {
          Binding binding = (entry.Value as BindingExpression).ParentBinding;
          yield return binding;
        }
      }
    }

    /// <summary>
    /// Use in the constructor of each Window, after initialisation.
    /// Pass "this" as the dependency object and omit other parameters to have 
    /// all the bindings in the window updated to respect user customisations 
    /// of regional settings. If you want a specific culture then you can pass 
    /// values to recurse and cultureInfo. Setting recurse to false allows you 
    /// to update the bindings on a single dependency object.
    /// </summary>
    /// <param name="dependencyObject">Root dependency object for binding change treewalk</param>
    /// <param name="recurse">A value of true causes processing of child dependency objects</param>
    /// <param name="cultureInfo">Defaults to user customisations of regional settings</param>
    public static void FixBindingCultures(DependencyObject dependencyObject, bool recurse = true, CultureInfo cultureInfo = null)
    {
      if (dependencyObject == null) throw new ArgumentNullException("dependencyObject");
      try
      {
        foreach (object child in LogicalTreeHelper.GetChildren(dependencyObject))
        {
          if (child is DependencyObject)
          {
            //may have bound properties
            DependencyObject childDependencyObject = child as DependencyObject;
            var dProps = DependencyHelper.GetProperties(childDependencyObject);
            foreach (DependencyProperty dependencyProperty in dProps)
              RegenerateBinding(childDependencyObject, dependencyProperty, cultureInfo);
            //may have children
            if (recurse)
              FixBindingCultures(childDependencyObject, recurse, cultureInfo);
          }
        }
      }
      catch (Exception ex)
      {
        Trace.TraceError(ex.Message);
      }
    }

    public static void RegenerateBinding(DependencyObject dependencyObject, DependencyProperty dependencyProperty, CultureInfo cultureInfo = null)
    {
      Binding oldBinding = BindingOperations.GetBinding(dependencyObject, dependencyProperty);
      if (oldBinding != null)
        try
        {
          //Bindings cannot be changed after they are used.
          //But they can be regenerated with changes.
          Binding newBinding = new Binding()
          {
            Converter = oldBinding.Converter,
            ConverterCulture = cultureInfo ?? CultureInfo.CurrentCulture,
            ConverterParameter = oldBinding.ConverterParameter,
            FallbackValue = oldBinding.FallbackValue,
            IsAsync = oldBinding.IsAsync,
            Mode = oldBinding.Mode,
            NotifyOnSourceUpdated = oldBinding.NotifyOnSourceUpdated,
            NotifyOnTargetUpdated = oldBinding.NotifyOnValidationError,
            Path = oldBinding.Path,
            StringFormat = oldBinding.StringFormat,
            TargetNullValue = oldBinding.TargetNullValue,
            UpdateSourceExceptionFilter = oldBinding.UpdateSourceExceptionFilter,
            UpdateSourceTrigger = oldBinding.UpdateSourceTrigger,
            ValidatesOnDataErrors = oldBinding.ValidatesOnDataErrors,
            ValidatesOnExceptions = oldBinding.ValidatesOnExceptions,
            XPath = oldBinding.XPath
          };
          //set only one of ElementName, RelativeSource, Source
          if (oldBinding.ElementName != null)
            newBinding.ElementName = oldBinding.ElementName;
          else if (oldBinding.RelativeSource != null)
            newBinding.Source = oldBinding.Source;
          else
            newBinding.RelativeSource = oldBinding.RelativeSource;
          BindingOperations.ClearBinding(dependencyObject, dependencyProperty);
          BindingOperations.SetBinding(dependencyObject, dependencyProperty, newBinding);
        }
        catch (Exception ex)
        {
          Trace.TraceError(ex.Message);
        }
    }

  }
}

推荐答案

此SO帖子(WPF/Silverlight)带有指向

This SO post (WPF/Silverlight) has a link to this article (WPF only), explaining how to use CurrentCulture as the default for your application. Does that solve your problem?

进行绑定使用区域和语言"中的自定义设置代替当前语言的默认设置需要更多技巧. 这篇文章得出的结论是,每个绑定的还必须将ConverterCulture显式设置为CultureInfo.CurrentCulture.以下是一些DateTime测试:

Making bindings use the custom settings from "Region and Language" instead of the current language's default settings requires some more trickery. This post concludes that every binding's ConverterCulture also has to be explicitly set to CultureInfo.CurrentCulture. Here are some DateTime tests:

<!-- Culture-aware(?) bindings -->
<StackPanel DataContext="{Binding Source={x:Static sys:DateTime.Now}}" >

    <!-- WPF's default en-US formatting (regardless of any culture/language settings) -->
    <TextBlock Text="{Binding Path=.}" />

    <!-- *Default* norwegian settings (dd.MM.YYY) -->
    <TextBlock Text="{Binding Path=., ConverterCulture=nb-NO}" />

    <!-- Norwegian settings from the "Region and Languague" dialog (d.M.YY) -->
    <TextBlock Text="{Binding Path=., ConverterCulture={x:Static sysglb:CultureInfo.CurrentCulture}}" />

    <!-- Hiding ConverterCulture initialization in our own custom Binding class as suggested here:
         https://stackoverflow.com/questions/5831455/use-real-cultureinfo-currentculture-in-wpf-binding-not-cultureinfo-from-ietfl#5937477 -->
    <TextBlock Text="{local:CultureAwareBinding Path=.}" />

</StackPanel>

自定义绑定类:

public class CultureAwareBinding : Binding
{
    public CultureAwareBinding()
    {
        this.ConverterCulture = System.Globalization.CultureInfo.CurrentCulture;
    }
}

在挪威的机器上,一切最终看起来像这样:

It all ends up looking like this on a norwegian machine:

这篇关于CurrentUICulture忽略区域和语言设置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆