#766 – WPF Data Binding Ignores Custom Formatting

You can get the WPF data binding engine to respect the current locale, as specified in the Region applet, by setting the Language property of a FrameworkElement to the language tag that is part of the CurrentCulture property.

Doing this allows you to set up the proper locale in a single line of code and data binding will use the correct rules for formatting dates and floating point numbers for the appropriate language.

This mechanism won’t, however, honor any custom formatting that you specify in the Region applet.  A custom format is anything that you change from the defaults in the Region applet.

766-001

If you want the data binding engine to completely respect all of the settings chosen in the Region applet, you can set the ConverterCulture property to the CurrentCulture, for each binding.

        <TextBlock Text="{Binding BoundDate, ConverterCulture={x:Static glob:CultureInfo.CurrentCulture}}"/>

The glob namespace points to System.Globalization.

766-002

Advertisement

#765 – WPF Data Binding Ignores CurrentCulture

Using the ToString method on numeric and date-based types from within your code, the output will reflect the current culture, as stored in the CurrentCulture property of the current thread.

When the data binding mechanism in WPF converts numeric or date-based data to a string, however, it doesn’t automatically use the current culture.  Instead, you must set the Language property of the FrameworkElement where the binding is occurring.  You typically set this property based on the CurrentCulture property of the current thread.  You can set the Language property of the topmost user-interface element, so that all of the other elements inherit the value.

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

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

We can now do binding as follows (where BoundDate property is of type DateTime):

        <TextBlock Text="{Binding BoundDate, StringFormat=d}"/>

Now if we run with the region set to French (France), we see:
765-001

#764 – Current Culture Is Used When Converting Data to A String

The CurrentCulture property of a Thread object indicates a user’s current locale, as set in the Region applet.  This value can normally be thought of as indicating the user’s location.

If you convert either numeric or date data to a string, the formatting used will depend on the current locale, as set in the Region applet and reported by the CurrentCulture property.

For example:

            double d1 = 123.456;
            DateTime date1 = DateTime.Now;

            string output = string.Format("123.456 => {0}\nNow => {1}",
                d1.ToString(),
                date1.ToString());

            MessageBox.Show(output);

If we run this code when our region is set to English (United States), we get:

764-001

But now if we switch our region to French (France) and re-run the application, we get:

764-002

Notice that the decimal point is now displayed as a comma (,) and the date format is dd/mm, rather than mm/dd.

#763 – The Difference Between CurrentCulture and CurrentUICulture

In a WPF application, you have access to two properties that give you information about the user’s culture and language.  They are both properties of a Thread object, which you can access via Thread.CurrentThread.

  • CurrentCulture – Tells you the user’s current locale, as set in the Region applet.  I.e.: Where is the user located?
  • CurrentUICulture – Tells you the native language of the version of Windows that is installed.  I.e.: What language does the user speak?

The user can change CurrentCulture using the Region applet.  It’s used to determine formatting for numeric and date/time strings.

763-001

The user normally can’t change CurrentUICulture without re-installing Windows.  It’s used to know what language to use when displaying text in your application.  (I.e. Which resource files to load).

            CultureInfo ci = Thread.CurrentThread.CurrentCulture;
            lblCurrentCulture.Content = string.Format("CurrentCulture = {0} - {1}", ci.Name, ci.DisplayName);

            ci = Thread.CurrentThread.CurrentUICulture;
            lblCurrentUICulture.Content = string.Format("CurrentUICulture = {0} - {1}", ci.Name, ci.DisplayName);

For example, someone in England might see:
763-002

#762 – Set FlowDirection at Runtime Based on CurrentUICulture

The FlowDirection property of a FrameworkElement (including a Window) indicates how child elements of an element should be layed out–left to right (what we are used to in the US), or right to left.

FlowDirection should typically be set to match the text direction of the language being used to render text in your user interface.  If text in your application is in Arabic, the letters will run right to left, so your GUI should also be layed out right to left.

You’ll normally load text resources based on the CurrentUICulture property, which is meant to indicate the target language for your application.  So you should also set the flow direction based on information pointed to by CurrentUICulture.

For example:

        public MainWindow()
        {
            InitializeComponent();

            this.FlowDirection =
                CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft ?
                    FlowDirection.RightToLeft :
                    FlowDirection.LeftToRight;
        }

On an English version of Windows, we get:
762-001
And on an Arabic version of Windows, we now get:
762-002

#761 – How FlowDirection Affects HorizontalContentAlignment

The FlowDirection property, which can be LeftToRight or RightToLeft, indicates how elements within a control should be layed out.

FlowDirection of RightToLeft reverse the meaning of all HorizontalContentAlignment values.  The example below shows the effect of different combinations of HorizontalContentAlignment and FlowDirection for some Label controls.

    <Grid ShowGridLines="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Label Content="HorizontalContentAlignment / FlowDirection Values" Grid.Row="0" Grid.ColumnSpan="2"
               HorizontalAlignment="Center"/>

        <Label Content="Left / LeftToRight" Grid.Row="1" Grid.Column="0"
               HorizontalAlignment="Stretch" HorizontalContentAlignment="Left"
               Background="Bisque"/>
        <Label Content="Left / RightToLeft" Grid.Row="1" Grid.Column="1"
               HorizontalAlignment="Stretch" HorizontalContentAlignment="Left"
               FlowDirection="RightToLeft"
               Background="Bisque"/>

        <Label Content="Right / LeftToRight" Grid.Row="2" Grid.Column="0"
               HorizontalAlignment="Stretch" HorizontalContentAlignment="Right"
               Background="Bisque"/>
        <Label Content="Right / RightToLeft" Grid.Row="2" Grid.Column="1"
               HorizontalAlignment="Stretch" HorizontalContentAlignment="Right"
               FlowDirection="RightToLeft"
               Background="Bisque"/>
    </Grid>

761-001

#760 – Horizontal and Vertical Alignment Basics

The general idea of the HorizontalAlignment and VerticalAlignment properties, for objects that inherit from FrameworkElement, is to indicate how the element is positioned within the space that the parent panel provides, after accounting for any margins.

Different values for HorizontalAlignment and VerticalAlignment only lead to different layout if the parent panel provides more room than the child element requires, in the corresponding dimension.

In the grid shown below:

  • Labels in the 1st row have HorizontalAlignment set to Left
  • Labels in 2nd row have HorizontalAlignment set to Center
  • Labels in 3rd row have HorizontalAlignment set to Stretch
  • 1st column width is set to Auto–make wide enough for largest item
  • 2nd column width takes up remaining space

760-001

Things to note:

  • Labels in 2nd column have more room, so horizontal alignment results in different behavior
  • 1st column sizes to fit 2nd row, so horizontal alignment doesn’t always apply

#759 – Device-Independent Units (DIPs)

(Also known as WPF Units)

In WPF, when you’re specifying size or position for user interface elements, the values are always by default in Device-Indepdent Units (DIPs).

1 DIP = 1/96 inch.  This means at 96 dpi, 1 DIP = 1 pixel.

At 120 dpi, 1 DIP = 1.25 pixels.  (120/96)

The purpose of using DIPs when indicating desired size or position is that controls will still have the same physical size on your screen, regardless of the DPI setting.  A button that is specified as being 96 DIPs wide will be 1″ wide, whether your display is set to 96 DPI, 120 DPI, or 150 DPI.

To convert from DIPs to pixels, you need to know your screen DPI setting (e.g. 96, 120):

# pixels = (DIPs) * (DPI / 96)

So 96 DIPs = 96 pixels at 96 DPI, and 96 DIPs = 120 pixels at 120 DPI.

#758 – Access Actual Height and Width from within LayoutUpdated Event Handler

The ActualHeight and ActualWidth properties of a FrameworkElement can be read to get the values for the final height and width that the layout system assigned to the element.

The LayoutUpdated event of a UIElement (parent of FrameworkElement) will fire whenever the layout changes.  You can handle this event and read the values of ActualHeight and ActualWidth from within the handler.

You should typically only read these properties from within the LayoutUpdated handler.  If you try reading them at some other point, there’s no guarantee that they have arrived at their final values as a result of the layout changing.

#757 – RenderSize, ActualHeight and ActualWidth

The ActualHeight and ActualWidth properties of an element that inherits from FrameworkElement indicate the final height and width of the element, after layout has taken place.

This same information is also available via the UIElement.RenderSize property (UIElement is the parent class of FrameworkElement).  RenderSize is of type Size, which is a struct containing the width and the height.