WPF中的字符串格式本地化问题

125

在 WPF 3.5SP1 中,我使用了数据绑定中的最新功能 StringFormat:

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

我面临的问题是,日期始终以英文格式显示...尽管我的系统是法语?如何强制日期遵循系统日期?

9个回答

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

来自在WPF中创建国际化向导


18
是的,这真的很烦人。+1 - Szymon Rozga
2
谢谢您解决我的烦恼。 - Skurmedel
9
好的。但是如果应用程序的生命周期中文化发生变化怎么办(例如,用户可以在设置对话框中更改他的首选文化)。根据文档,FrameworkElement.LanguageProperty.OverrideMetadata 不能被多次调用(会抛出异常)。 - Torben Junker Kjær
1
@pengibot 这个解决方案对我有效。我正在使用 .net 4/C#/WPF,并将代码放在 OnStartup 方法中。 - Björn
21
请注意,Run 元素不继承自 FrameworkElement,因此,如果您将日期等内容绑定到 Run,则需要使用额外的调用来引用 **typeof(System.Windows.Documents.Run)**。 - Mat Fergusson
显示剩余2条评论

98

定义以下XML命名空间:

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

现在看看这个奇妙的修复:

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

我很清楚这不是全局性的解决方案,你需要在每个绑定上使用它,但这难道不是良好的XAML实践吗?据我所知,下一次绑定更新时将会使用正确的 CultureInfo.CurrentCulture 或您提供的任何其他值。

这个解决方案可以立即更新您的绑定并提供正确的值,但对于如此罕见且无害的事情来说,代码量似乎有点多。


5
太棒了!这个方法非常有效!我添加到需要的几个地方都没有问题。顺便提一下,你的示例代码缺少一个}。 - Johncl
4
干得好。很奇怪WPF默认使用美式英语,而不是当前文化。 - Kris Adams

14

我只是想要补充一点,loraderon的答案在大多数情况下都很有效。当我将以下代码行放入我的App.xaml.cs中时,我的TextBlocks中的日期会以正确的文化格式进行格式化。

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

我说“大多数情况”。例如,这将立即生效:

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

…但当在TextBlock中使用Run时,DateTime会按默认区域设置格式化。

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

为使其工作,我需要Gusdor的答案,即在绑定中添加ConverterCulture={x:Static gl:CultureInfo.CurrentCulture}。

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

我希望这个补充回答能对某个人有所帮助。


确实,Run并不是从FrameworkElement派生的。您可以尝试修改loraderon的答案,重复他的代码用于Run的基类(FrameworkContentElement)以及FrameworkElement。 - Nathan Phillips
对于那些可能会感到疑惑的人:xmlns:gl="clr-namespace:System.Globalization;assembly=mscorlib" - Igor Meszaros

12

如前所述,XAML默认使用不变文化(en-US),您可以使用

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

将文化设置为当前文化语言的默认文化。但是注释是错误的;因为它不使用当前文化,所以您将看不到用户可能进行的任何自定义,它始终是该语言的默认值。

要实际使用具有自定义的当前文化,您需要将ConverterCultureStringFormat一起设置,例如

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

在您的根元素中将gl定义为全局命名空间

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

如果您是通过代码而不是XAML进行此操作,则如下所示: binding.ConverterCulture = System.Globalization.CultureInfo.CurrentCulture; - Metalogic

12

只需将 culture 快捷方式插入到顶级标记即可:

xml:lang="de-DE"
e.g.:
<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>

5
假设 en-US 是“正确”的文化,这与假设任何其他文化是“正确”的一样糟糕。最好从用户的机器上获取设置。 - misnomer
非常感谢,这正是我所需要的!如果WPF认为en-EN是任何情况下的正确文化,那么我也可以使用自己的本地化。由于我正在开发一个概念验证应用程序,开发速度是当天的命令,没有时间在数十行代码中瞎折腾,只需进行简单修复即可让DatePicker快速恢复正常! - M463
1
为我个案找到最佳答案,终于寻找了很久 :) 当然是正确的,不管你认为它是en-US还是de-DE...人们总是在简单的解决方案上遇到问题 -.- - MushyPeas
我想覆盖系统文化,这很完美;而且我可以补充说它适用于任何标记,并传播到所有子元素,不必是顶级元素(Window、UserControl等)。 - Soleil

8

如果您需要在程序运行时更改语言,只需更改根元素上的Language属性(我不确定这是否会立即生效或者子元素是否需要重新创建,在我的情况下至少可以使用此方法)

element.Language = System.Windows.Markup.XmlLanguage.GetLanguage(culture.IetfLanguageTag);

它可以立即重新评估,但遗憾的是必须为每个根元素(窗口)单独设置。 - Firo

7
完整的代码可以切换本地化,包括像<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

0

如果你正在编写代码而不是 XAML,你可以按如下方式设置 ConverterCulture:

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

感谢@KZeise指出使用默认文化定义和使用用户自定义文化定义之间微妙差别的区别。

0
如果您想在运行时更改文化信息,可以使用行为(请参见下文)。
  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();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接