Formatting Data in WPF Controls

Formatting Data in WPF Controls

  • Comments 20

One of the basic things that we need to do as business application developers is to make sure that data being displayed to a user makes sense to them and that they know what type of data something is when they are making edits. In every business application I've written the data entry forms needed to format data in different ways. Let's take a look at how we do this in Windows Presentation Foundation.

IValueConverter

In WPF there is an interface called IValueConverter that you can implement on a class to specify how data should be converted for display in the control as well as a way to convert it back to the data source. Then you reference this class in the XAML on the data binding for each control. For instance, let's say we have a very simple form displaying an order where we want to format the Order Date and Order Total.

wpfformat1

We don't want to display the time portion of our date and we'd also like to display the decimal Total in currency format. We also want to make sure users can't type the wrong data types in those fields, i.e. don't allow character strings. The first thing to do is write a simple converter class that will format our controls for display as well as verify the correct data types are entered by the user.

Public Class MyFormatter
    Implements IValueConverter

    Public Function Convert(ByVal value As Object, _
                            ByVal targetType As System.Type, _
                            ByVal parameter As Object, _
                            ByVal culture As System.Globalization.CultureInfo) As Object _
                            Implements System.Windows.Data.IValueConverter.Convert

        If parameter IsNot Nothing Then
            Return Format(value, parameter.ToString())
        End If

        Return value
    End Function

    Public Function ConvertBack(ByVal value As Object, _
                                ByVal targetType As System.Type, _
                                ByVal parameter As Object, _
                                ByVal culture As System.Globalization.CultureInfo) As Object _
                                Implements System.Windows.Data.IValueConverter.ConvertBack

        If targetType Is GetType(Date) OrElse targetType Is GetType(Nullable(Of Date)) Then
            If IsDate(value) Then
                Return CDate(value)
            ElseIf value.ToString() = "" Then
                Return Nothing
            Else
                Return Now() 'invalid type was entered so just give a default.
            End If
        ElseIf targetType Is GetType(Decimal) Then
            If IsNumeric(value) Then
                Return CDec(value)
            Else
                Return 0
            End If
        End If

        Return value
    End Function
End Class

The Convert method is called when data is coming out of the data source and into the control for display. Here we're taking advantage of the parameter called parameter (very creative name <g>). I'm just using the Visual Basic Format function here for simplicity but you can write any type of formatting code here. The VB Format function takes in simple string format styles to format values and there are a bunch of predefined ones for dates and numbers. You can also specify your own. It's very similar to the String.Format .NET framework method but it is simpler to specify the styles. So to format a date value to only show the date portion we could pass a 'd' as the parameter. Using the predefined masks the Format function is internationally aware. However, also note that you can use the culture parameter to determine the CultureInfo as well.

The ConvertBack method is called when data is going back into the data source and it gives us a chance to take the string values the user enters and convert them to their correct data types. Here I'm checking the targetType in order to determine what the type of the value should be and then attempting to convert it back to the real data type. If the value cannot be converted, say the user typed the character "B" for the date field, then I'm just returning a default value. In the case of my date it allows null values so if the user blanks out the date then the value becomes the empty string and we return Nothing (null).

Specifying the Converter on the Bindings

Next we need to augment the XAML so that we can refer to our converter class in the bindings. What we need to do is add this class as a static resource of the window by first adding an XML namespace to identify our local project and then adding the reference into the Window.Resources:

<Window x:Class="Formatting"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyProject"    
    Title="Formatting" Height="207" Width="300">
    <Window.Resources>
        <local:MyFormatter x:Key="myformatter" />
    </Window.Resources>

Now we can augment the data bindings of the fields we want formatting on by specifying the Converter and ConverterParameter properties on the Binding:

<TextBox Height="28" Width="Auto" Margin="3" IsReadOnly="True" 
  Name="txtID" 
  Text="{Binding Path=OrderId}"/>
<TextBox Height="28" Width="Auto" Margin="3" 
  Name="txtProductName" 
  Text="{Binding Path=ProductName}"/>
<TextBox Height="28" Width="Auto" Margin="3" 
  Name="txtOrderDate" 
  Text="{Binding Path=OrderDate, Converter={StaticResource myformatter}, ConverterParameter='d'}"/>
<TextBox Height="28" Width="Auto" Margin="3" 
  Name="txtOrderTotal"  
  Text="{Binding Path=Total, Converter={StaticResource myformatter}, ConverterParameter='c2'}"/>

Now when we run our form we will see the data displayed properly and you'll also notice that invalid characters that would cause an incompatible data type aren't allowed. We can also be lazy with how we enter dates, for instance if we type '3-1' then it will be converted to 3/1/2008 (current year). This is generally a nice thing to enable for users.

wpfformat2

Using String.Format instead of Format

If we don't want to (or can't) use the Format method then we can use the String.Format method instead to display our data by changing our Convert method code (notice that I have to explicitly specify the culture now):

If parameter IsNot Nothing Then
    Return String.Format(culture, parameter.ToString(), value)
End If

However, the style parameter also needs to be changed as well because String.Format requires the styles to be surrounded by curly braces. This doesn't play so nice with the curly braces in the XAML binding so we end up having to escape them:

<TextBox Height="28" Width="Auto" Margin="3" 
 Name="txtOrderDate" 
 Text="{Binding Path=OrderDate, Converter={StaticResource myformatter}, 
                                ConverterParameter='\{0:d\}'}"/>
<TextBox Height="28" Width="Auto" Margin="3" 
 Name="txtOrderTotal"  
 Text="{Binding Path=Total, Converter={StaticResource myformatter}, 
                            ConverterParameter='\{0:c\}'}"/>

Now you know why I like Format better that String.Format :-)

So as you can see it's pretty easy to set up formatting on controls in WPF. Look for the next WPF How Do I video on this coming to a Developer Center near you very soon. :-)

Enjoy!

Leave a Comment
  • Please add 7 and 3 and type the answer here:
  • Post
  • Beth, how about using Binding.StringFormat feature (.NET 3.5 SP1)? Does it provide the convert-back mechanism?

  • Hey Beth,

    Good post and I'm also married to the Format function.  You make a good point on enabling "lazy" users.  It also applies to careless users.  I'm a firm believer in restrictive data entry.

  • Hi Maximillian,

    Using Binding.StringFormat is great for view-only data but you are correct, it doesn't provide the full featured type conversion and defaults when users enter data. So for data entry it's better to support a converter like I showed above. However Binding.StringFormat (and the ContentStringFormat for content controls, HeaderStringFormat, ItemStringFormat, etc.) are easy for controlling the *display* of data.

    Cheers,

    -B

  • Hi John,

    Good to hear from you! Thanks for the feedback.

    -B

  • Beth,

    The documentation for IValueConverter shows the class decorated with the ValueConversion attribute.  The only indication I can find for why to use this attribute is "to indicate to development tools the data types involved in the conversion".  Can you elaborate on this?  Do you know of any specific examples where using this attribute buys functionality that you don't get without it?

    Thanks.

  • Hi Allan,

    I don't think it is used currently by the WPF designer but it will be in the future. It's used to indicate to designers which converters would apply to the bound data types on the controls.

    I'd ask the question in the WPF forum:  http://social.msdn.microsoft.com/forums/en-US/wpf/threads/

    -B

  • I thought freedom of speech was ok.  it seems to me that only positive blog posts will stay here.  Be warned everyone, Beth is a communist.

  • hi Rich,

    Please stop spamming my blog and I wont have to do that. Your posts are completely off topic. I spoke with Rich at Strangeloop and you are not him so quit it.

    Thanks,

    -B

  • How about if we wanted to keep the order date having date and time?

    thanks.

  • Hi Carlos,

    Take a look at the predefined date formats that you can specify to the Format function here: http://msdn.microsoft.com/en-us/library/362btx8f.aspx

    For instance instead of specifying a 'd' in the XAML you can supply a 'g' as the ConverterParameter and the short date and time will display.

    Cheers,

    -B

  • Hi Beth,

    thanks for the clarification. I think the StringFormat name should be changed to DisplayStringFormat since it was confusing to me at first.

    Anyway, I'll try your way using IValueConverter then.

    Thanks!

  • We just released a new How Do I Video onto the VB Dev Center on how to format controls on data entry

  • Today we released a new How Do I Video onto the VB Dev Center on how to format controls on data entry

  • Great post, Beth, thanks!

    For anyone interested, I just put up a somewhat related post on using IValueConverter to translate numeric values between different scales - this one is also in VB by the way:

    http://www.perceptible.net/post/2009/01/06/Translating-Numeric-Values-Between-Different-Scales-in-WPF-Data-Binding.aspx

  • Hi Beth,

    I am very beginner. for novice like me, could u please upload the sample project of the 'Formatting Data in WPF Controls' ?

    Thanks Beth.

Page 1 of 2 (20 items) 12