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.
Comments
Anonymous
May 30, 2008
PingBack from http://www.alvinashcraft.com/2008/05/30/dew-drop-may-30-2008/Anonymous
October 07, 2010
Thanks!Anonymous
November 04, 2010
Thanks! I was trying to find some good examples for multi-property string formatting.Anonymous
November 30, 2010
Unfortunately, there is a bug in WPF, considered "by design" by Microsoft. If you don't specify the culture in the binding, it will always use en-US, regardless of the user's setting for CurrentCulture. See connect.microsoft.com/.../wpf-binding-uses-the-wrong-currentculture-by-defaultAnonymous
February 24, 2011
{Binding MyProperty, StringFormat={}{0}:}, to produce "<MyProperty>:" output, does not work, for some reason. Somehow the parser does not like the colon at the end. Another Microsoft bug, I guess. The alternative ContentStringFormat="{}{0}:" works as expected.Anonymous
January 15, 2012
Excellent article. crux points pointed out.Anonymous
August 06, 2013
thank you very much.my problem was solved.