46

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)

Region settings: not the default but yyy-MM-dd is used

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}" />
Community
  • 1
  • 1
Markus k
  • 513
  • 1
  • 4
  • 8

9 Answers9

30

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!

aKzenT
  • 7,775
  • 2
  • 36
  • 65
  • thank you for your answer. this solution souns like the only one possible. – Markus k May 10 '11 at 06:39
  • 4
    "since retroactively forcing Binding to respect the culture could break other code in WPF which depends on this behavior." - I'd argue that in the long-term it's worse to keep on maintaining broken behaviour like this than it is to introduce a short-term break. Or they could fix it but use .NET's assembly binding features to detect when an application expects the older version and maintains the broken behaviour for them. It's insane that this bug has been in WPF for 12 years now and they don't have their own `CultureAwareBinding` built-in. – Dai Nov 03 '17 at 03:55
24

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}}" />
Parth Shah
  • 2,060
  • 1
  • 23
  • 34
  • 2
    I like your detailed explanation on this. Any input on correcting errors for certain binds? That result in something like, "The type "CultureAwareBinding" does not include a constructor that has the specified number of arguments." – Poken1151 Oct 12 '14 at 04:44
  • I haven't seen that error yet. I would need to see some code to determine why that would happen. Perhaps you could post a new question on Stack Overflow and add a link to it over here? – Parth Shah Oct 13 '14 at 04:14
  • 2
    Thanks, I fixed the error. It was bad coding, evidently, {Binding ITEM-NAME .... } is not good, I needed {Binding Path=ITEM-NAME, ... }. That resolved my error (which would still compile in both cases) – Poken1151 Oct 13 '14 at 21:52
  • 5
    @Poken1151 You can also define an additional constructor for CultureAwareBinding that takes a string as sole parameter: `public CultureAwareBinding(string path) : base(path) { ConverterCulture = CultureInfo.CurrentCulture; }`. This way you don't need to specify `Path=…`. – linac May 19 '16 at 12:54
12

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)

Kurt Van den Branden
  • 11,995
  • 10
  • 76
  • 85
Alan Hauge
  • 137
  • 1
  • 2
  • 3
    Works like a charm and is just one line of code without the need to touch any other code. I don't understand, why this isn't the accepted answer and why it has so few upvotes. Are there any pitfalls? – Aaginor Aug 29 '18 at 15:15
  • 3
    As was stated in the question, setting the language only causes WPF to use the language's default date/time formats, not the formats that are selected in the system's region settings. – Collin K Nov 09 '18 at 16:17
  • I've put this code in my App.xaml.cs Application_Startup method and worked well. Now datetime is correctly formatted to my system locale. – Guillermo Espert Aug 26 '20 at 16:10
  • 1
    @CollinK It works perfectly well not only for the date/time formats but also for the other regional settings, e.g. the decimal separator (that is not a dot but a comma in many system's regional settings). –  Jul 18 '21 at 12:00
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}}" />
Cheetah
  • 1,141
  • 11
  • 21
3

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>
Eric Ouellet
  • 10,996
  • 11
  • 84
  • 119
3

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

1

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

How about changing the lanaguge in the code behind?

this.Language = XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentCulture.Name);
Adi Lester
  • 24,731
  • 12
  • 95
  • 110
  • This method ignores the custom settings in "Region and Language" dialog and just uses the name to obtain language. – Markus k Jul 23 '12 at 10:35
  • Its the simplest solution and works fine for me too (Windows Phone 7). @Markus k: Sorry, didn't catch it what you're trying to say/explain? – hfrmobile Sep 07 '12 at 20:17
  • 1
    @hfmobile What he meant was that if you select a language in regional settings (ex German Germany), and then manually modify any of the settings (ex Short Date change to d.M.yyyy), setting the language suggested above will not pick up the new format. – Goran Jul 04 '14 at 21:05
-1

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

Olivier
  • 79
  • 3
  • 3
    @ Olivier: simply saying "other users don't even know about such an option", "from my many, many years of WPF and Silverlight knowledge I know best" or even claim that anyone knows people's "real needs" is arrogant. You know nothing about the preferences of nearly 99% of this planet's people - just as all other developers don't. Accept that there's a need and provide help. Using YYYY-MM-DD is a date format I wished the whole world would rather agree upon than use their regional ones. IMHO, "from big to small" is more useful, especially whenever it comes to sorting, than any other format. – Yoda Oct 26 '16 at 12:26