Use “real” CultureInfo.CurrentCulture in WPF Binding, not CultureInfo from IetfLanguageTag

余生颓废 提交于 2019-12-17 06:39:20

问题


In my case:

I have a TextBlock Binding to a property of type DateTime. I want it to be displayed as the Regional settings of the User says.

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

I am setting Language property as WPF XAML Bindings and CurrentCulture Display says:

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

But with this line of code it just displays the text as the default format of CultureInfo with IetfLanguageTag of CurrentCulture says, not as the effective value selected in systems region settings says:

(e.g. for "de-DE" dd.MM.yyyy is used instead of selected yyyy-MM-dd)

Is there a way Binding uses the correct format without defining ConverterCulture on every single Binding?

In code

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

uses the right Culture settings.

edit:

another way which doesn't work as desired (like this.Language = ... does):

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

and

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

回答1:


You can create a subclass of binding (e.g. CultureAwareBinding) which sets the ConverterCulture automatically to the current culture when created.

It's not a perfect solution, but it's probably the only one, since retroactively forcing Binding to respect the culture could break other code in WPF which depends on this behavior.

Let me know if you need more help!




回答2:


This is an extension of answer from aKzenT. They proposed that we should create a subclass of Binding class and set the ConverterCulture to CurrentCulture. Even though the answer is very straight forward, I feel some people may not be very comfortable implementing it, so I am sharing the code version of aKzenT's answer with an example of how to use it in XAML.

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

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

Example of how to use this in XAML

1) You need to import your namespace into your XAML file:

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

2) Real world usage of the CultureAwareBinding

<Textblock Text="{myWpfLib:CultureAwareBinding Path=Salary, Source=Contact, StringFormat={}{0:C}}" />



回答3:


Put the following line of code, before any UI is initialized. This worked for me.

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

(And remove all explicit culture parameters)




回答4:


Your second attempt was close, and led me to a solution that does work for me.

The problem with setting the ConverterCulture is that it is only used when you have a Converter. So simply create a simple StringFormatConverter that takes the format as its parameter:

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

Then you can adjust your binding (assuming you've imported the converter's namespace as "my")

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



回答5:


I use that code with proper results to my needs. Hope it could fills your :-) ! Perhaps you are better throwing an exception if cannot "TryParse". Up to you.

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

Usage:

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



回答6:


I came up with a hack/workaround that avoids updating all your bindings. Add this code to the constructor of your main window.

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

Since it's using reflection there is no guarantee that it will work in the future, but for now it does (.NET 4.6).




回答7:


We can create a DateTime Converter using the 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();
        }


    }

Apply this in the XAML as shown below

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

Also change the current culture to get the desired format and the same needs to be applied on the application startup

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



回答8:


How about changing the lanaguge in the code behind?

this.Language = XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentCulture.Name);



回答9:


The problem that is avoiding using "this.Language = XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentCulture.Name);" is not really a common one. I don't know any user here in france that will change the date format to US or Japan one, just because at least no user is knowing that such a change is possible (and dont know how to do it)... So of course the "language=" is not perfect, but in many many years of WPF and Silverlight practice I never see a problem of this kind with any user... So I still use the "Langage=" trick, it is simple and cover 100% of real needs. Of course others solutions seem to be better, but there is no need for (and I saw a few implementations that are far from perfect compare to "language=" solution).



来源:https://stackoverflow.com/questions/5831455/use-real-cultureinfo-currentculture-in-wpf-binding-not-cultureinfo-from-ietfl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!