StringFormat for Windows Phone 7

by Daniel Sklenitzka

WPF 3.5 SP1 introduced a very handy feature: the StringFormat property of the BindingBase class. This property enables us to specify a format string when binding to data which is applied to the value of the bound property:
<TextBlock Text="{Binding Amount, StringFormat=Amount: {0}}"/>

This line produces the text Amount: 5 in a single TextBlock. Prior to WPF 3.5 SP1, it would have been necessary to use multiple TextBlocks or alternatively to add another property to the DataContext and do the formatting there.

Of course we can use the usual .NET format specifiers as well. The following line formats the price as a currency string depending on the user’s current culture, for example $9.90.
<TextBlock Text="{Binding Price, StringFormat=C2}"/>

This also works for MultiBindings, where it is probably even more useful, since we don’t even need an IMultiValueConverter anymore:
<TextBlock>
    <TextBlock.Text>
        <MultiBinding StringFormat="Amount: {0}, Price: {1:C2}">
            <Binding Path="Amount"/>
            <Binding Path="Price"/>
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

The code above creates a single TextBlock containing the text Amount: 5, Price: $9.90. Pretty cool!

Silverlight also supports the StringFormat property since version 4 – but unfortunately Windows Phone 7 doesn’t.

One way to recreate this feature for Windows Phone 7 is to write a converter that can be used instead (by implementing the IValueConverter interface). The format string can then be specified as the ConverterParameter of the binding and will be passed on to our converter.

This is how the Convert method of the interface can be implemented:
public object Convert(object value, Type targetType, 
    object parameter, CultureInfo culture)
{
    String format = parameter as String;
    if (value == null || format == null)
        return value;
           
    return String.Format(culture, format, value);
}

Having implemented the converter, we can add one instance to the application’s resources (don’t forget to add the namespace declaration):
<Application.Resources>
    <local:StringFormatConverter x:Key="StringFormatConverter"/>
</Application.Resources>

Now we can access the converter anywhere in the application by using the StaticResource markup extension:
<TextBlock Text="{Binding Amount, Converter={StaticResource
    StringFormatConverter}, ConverterParameter='Amount: {0}, '}"/>
<TextBlock Text="{Binding Price, Converter={StaticResource
    StringFormatConverter}, ConverterParameter='{}{0:C2}'}"/>

The code produces the output Amount: 5, $9.90. Windows Phone 7 doesn’t support MultiBindings, so we have to use two separate TextBlocks here.
Also notice that the format string needs to start with an extra pair of curly brackets when the first character would be an opening curly bracket. This tells the XAML parser that we’re not trying to use a markup extension like {Binding} or {StaticResource}. In order to make our converter support these format strings as well, we need to add two lines right before the return statement:
if (format.StartsWith("{}"))
    format = format.Substring(2);

The full version of the code provided below (and in the downloadable sample project) also adds a property to the converter so that the user (not the end user, the user of the converter – you ;)) can specify whether he wants empty strings to be formatted or not.
The following example shows the use of this feature. Imagine we have a list of football players and we want to display their last and first name separated by a comma. However, some players use a pseudonym and thus no first name is available. In that case, we don’t want to add the comma. Take a look at this DataTemplate:
<DataTemplate>
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding LastName}"/>
        <TextBlock>
            <TextBlock.Text>
                <Binding Path="FirstName" 
			 ConverterParameter=", {0}">
                    <Binding.Converter>
                        <local:StringFormatConverter 
                            FormatEmptyStrings="False"/>
                    </Binding.Converter>
                </Binding>
            </TextBlock.Text>
        </TextBlock>
    </StackPanel>
</DataTemplate>

The binding of the second TextBlock specifies a new converter whose FormatEmptyStrings property is explicitly set to false. If the first name of a player is an empty string, it won’t apply the format string to it, and thus no comma will be added.

Download sample project


Full version:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace SmileCompany.Samples.Phone.StringFormat
{
    public class StringFormatConverter : DependencyObject,
	                                 IValueConverter
    {
        public bool FormatEmptyStrings { get; set; }

        public StringFormatConverter()
        {
            FormatEmptyStrings = true;
        }

        public object Convert(object value, Type targetType,
            object parameter, CultureInfo culture)
        {
            String format = parameter as String;
            if (value == null || format == null)
                return value;

            if (value == String.Empty && !FormatEmptyStrings)
                return value;

            if (format.StartsWith("{}"))
                format = format.Substring(2);

            return String.Format(culture, format, value);
        }

        public object ConvertBack(object value, Type targetType,
            object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}