Trying out Binding.StringFormat

 

StringFormat is a new property in .Net 3.5 SP1, which is currently in Beta. See Scott’s blog for more info on the beta.

 

 

When you bind data into a property on an element, it’s automatically type converted for you. For example, this markup:

 

<StackPanel xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <StackPanel.DataContext>

        <sys:Int32>123</sys:Int32>

    </StackPanel.DataContext>

   

    <TextBlock Text="{Binding}" /> <!-- Simply bind to the DataContext -->

   

</StackPanel>

 

… shows a TextBlock with the text “123”.

 

If you want to have more control over the conversion of the value during binding, you can write a value converter for it. For example, with this code:

 

public class TestConverter : IValueConverter

{

    public object Convert(object value, Type targetType, object parameter,

                          System.Globalization.CultureInfo culture)

    {

        return String.Format(culture, "Cost: {0:C}", value);

    }

    public object ConvertBack(object value, Type targetType, object parameter,

                          System.Globalization.CultureInfo culture)

    {

        throw new NotImplementedException();

    }

}

 

… and this markup:

 

<StackPanel xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <StackPanel.Resources>

        <local:TestConverter x:Key="testConverter" />

    </StackPanel.Resources>

    <StackPanel.DataContext>

        <sys:Int32>123</sys:Int32>

    </StackPanel.DataContext>

    <TextBlock Text="{Binding Converter={StaticResource testConverter}}" />

   

</StackPanel>

 

… my TextBlock shows “Cost: $123.00”.

 

 

The New StringFormat Property

That’s great, but it’s too bad that you have to write a value converter to get that. To make it simpler, we’ve added the StringFormat property to Binding, which causes the value to be run through, not surprisingly, the String.Format method.

 

So, the above can now be accomplished with this markup, and no code:

 

<StackPanel xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <StackPanel.DataContext>

        <sys:Int32>123</sys:Int32>

    </StackPanel.DataContext>

    <TextBlock Text="{Binding StringFormat=Cost: {0:C}}" />

   

</StackPanel>

 

 

If you just want to add the format specifier, e.g. just “{0:C}” rather than “Cost: {0:C}”, there’s nothing unusual if you’re in code, but in Xaml the syntax gets confused with the markup extension syntax. That is, a property value that starts with a “{“ (as the first character) is interpreted to be a markup extension, such as {Binding}, {DynamicResource}, {x:Null}, etc. The way to really have a “{“ as the first character of a property value is to escape it with a “{}”, so the above example becomes:

 

<TextBlock Text="{Binding StringFormat={}{0:C}}" />    

 

 

Not Just on Bindings

 

StringFormat shows up on some controls too. For example, just like ContentControl (the base class of Button) lets you template your content with the ContentTemplate property, you can now format your content with the ContentFormatString property, such as:

 

<Button ContentStringFormat="{}{0:C}">

    <sys:Int32>123</sys:Int32>

</Button>

 

The ContentStringFormat property is available on ContentControl, ContentPresenter, and TabControl.

 

There’s an analogous HeaderStringFormat for HeaderedContentControl, GridViewColumn, GroupStyle, and HeaderedItemsControl. Again, these are all cases where there is an analogous HeaderTemplate property.

 

Finally, there’s an ItemStringFormat on ItemsControl (the base class for ListBox) that allows you to format items. For example, this produces a ListBox with two entries, “1,234.00” and “5,6789.00”:

 

<ListBox ItemStringFormat="{}{0:N2}">

    <sys:Int32>1234</sys:Int32>

    <sys:Int32>5678</sys:Int32>

</ListBox>

 

 

 

Multi Bindings

 

All of the examples so far reference the bound value with some form of “{0}”, which is the zero-th index into the array of sources. With a MultiBinding, you can actually get more than one input. For example, the following has a TextBlock that composes the value in two other TextBlocks (in a real-world example, those would probably be TextBox’s for user input):

 

<TextBlock Name="First">Fred</TextBlock>

<TextBlock Name="Last">Flintstone</TextBlock>

<TextBlock >

    <TextBlock.Text>

        <MultiBinding StringFormat="Name: {1}, {0}">

            <Binding ElementName="First" Path="Text"/>

            <Binding ElementName="Last" Path="Text"/>

        </MultiBinding>

    </TextBlock.Text>

</TextBlock>

 

This produces “Name: Flintstone, Fred”.

 

 

Culture

 

All of the examples so far have been in US English (“en-US”). But you can pick the culture for StringFormat, the same way that you pick culture for the Binding.Converter property. That is, you can set it explicitly on the Binding, as in:

 

<TextBlock Text="{Binding ConverterCulture='en-US', StringFormat='{}{0:N2}' }" />

 

… which gives me “123.00” (note the period for the decimal separator), or as in:

 

<TextBlock Text="{Binding ConverterCulture='fr-FR', StringFormat='{}{0:N2}' }" />

 

… which gives me “123,00” (note the comma for the decimal separator).

 

Or you can specify it on the element in the Language property, as in:

 

<StackPanel Language="fr-FR">

    <TextBlock Text="{Binding StringFormat='{}{0:N2}' }" />

</StackPanel>

 

As an aside, note that the xml:lang attribute is mapped to the Language property. So the above is equivalent to:

 

<StackPanel xml:lang="fr-FR">

    <TextBlock Text="{Binding StringFormat='{}{0:N2}' }" />

</StackPanel>

 

 

More Samples

 

Finally, see Lester’s post for more StringFormat examples.