Formatting Data in WPF Controls

Published 12 December 08 04:32 PM

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!

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Maximilian Haru Raditya said on December 13, 2008 12:22 AM:

Beth, how about using Binding.StringFormat feature (.NET 3.5 SP1)? Does it provide the convert-back mechanism?

# John Koziol said on December 14, 2008 1:35 AM:

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.

# Beth Massi said on December 15, 2008 12:41 PM:

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

# Beth Massi said on December 15, 2008 12:52 PM:

Hi John,

Good to hear from you! Thanks for the feedback.

-B

# Allan said on December 15, 2008 2:02 PM:

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.

# Beth Massi said on December 16, 2008 6:56 PM:

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

# Rich said on December 16, 2008 9:21 PM:

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.

# Beth Massi said on December 16, 2008 10:05 PM:

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

# Carlos said on December 18, 2008 5:07 AM:

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

thanks.

# Beth Massi said on December 18, 2008 12:05 PM:

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

# Maximilian Haru Raditya said on December 23, 2008 1:55 PM:

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!

# Beth Massi - Sharing the goodness that is VB said on January 6, 2009 2:18 PM:

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

# VB Feeds said on January 6, 2009 8:46 PM:

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

# Daniel Stolt said on January 7, 2009 3:21 AM:

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

# sohan said on January 13, 2009 2:27 AM:

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.

# Beth Massi said on January 13, 2009 11:16 AM:

Hi sohan,

The video and sample code can be downloaded here: http://msdn.microsoft.com/en-us/vbasic/dd367843.aspx

Cheers,

-B

# [A HotDocument] developer said on January 24, 2009 3:14 AM:

Thank you for the good information.

I could get from yours.

# Dominique Gratpain said on February 12, 2009 6:50 AM:

Hi Beth,

IValueConverter works with WPF.

How can I do the same in WinForms ?  (the same is binding with convert).

Thanks

Dominique

# Beth Massi said on July 7, 2009 6:27 PM:

Hi Dominique,

Take a look at the Binding class' Parse and Format events.

http://msdn.microsoft.com/en-us/library/system.windows.forms.binding.parse.aspx

http://msdn.microsoft.com/en-us/library/system.windows.forms.binding.format.aspx

HTH,

-B

Leave a Comment

(required) 
(optional)
(required) 

About Beth Massi

Beth is a Program Manager on the Visual Studio Community Team at Microsoft and is responsible for producing and managing content for business application developers, driving community features and team participation onto MSDN Developer Centers (http://msdn.com), and helping make Visual Studio one of the best developer tools in the world. She also produces regular content on her blog (http://blogs.msdn.com/bethmassi), Channel 9, and a variety of other developer sites and magazines. As a community champion and a long-time member of the Microsoft developer community she also helps with the San Francisco East Bay .NET user group and is a frequent speaker at various software development events. Before Microsoft, she was a Senior Architect at a health care software product company and a Microsoft Solutions Architect MVP. Over the last decade she has worked on distributed applications and frameworks, web and Windows-based applications using Microsoft development tools in a variety of businesses. She loves teaching, hiking, mountain biking, and driving really fast.
Page view tracker