Utilizzare “reale” CultureInfo.CurrentCulture in WPF vincolante, non CultureInfo da IetfLanguageTag

StackOverflow https://stackoverflow.com/questions/5831455

Domanda

Nel mio caso:

Ho un TextBlock Associazione a una proprietà di tipo DateTime. Io voglio che sia visualizzato come le impostazioni regionali del utente dice.

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

Io sono la creazione proprietà Language come WPF XAML Associazioni e CurrentCulture display dice:

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

Ma con questa riga di codice solo visualizza il testo come formato di default di CultureInfo con IetfLanguageTag di CurrentCulture dice, non come il valore effettivo selezionato nelle impostazioni sistemi regione dice:

(ad es. Per "de-DE" dd.MM.yyyy viene usato al posto di selezionato aaaa-MM-dd )

C'è un modo vincolante utilizza il formato corretto senza definire ConverterCulture su ogni singolo Binding?

Nel codice

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

utilizza le impostazioni cultura giusta.

modifica

un altro modo che non funziona, se lo desideri (come this.Language = ... fa):

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

e

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

Soluzione

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!

Altri suggerimenti

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}}" />

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)

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}}" />

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>

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

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

How about changing the lanaguge in the code behind?

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

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top