Data Formatting
Your raw data would likely benefit from some dressing up before showing it to your users. For example, they probably think that "12.5%" looks better than "0.0125."
Consider the following Sale business class which represents a sale and its details.
// C#:
class Sale
{
// simplifed sale class
// for demo purposes
public Double CommisionRate { get; set; }
public DateTime SaleDate { get; set; }
// allowed values
// R = retail, W = wholesale
// G = goverment, I = internal
public Char SaleType { get; set; }
public Decimal Amount { get; set; }
}
...
// initializing the Sale class
// and setting data context.
Sale sale = new Sale();
sale.Amount = 1200;
sale.CommisionRate = .0125;
sale.SaleDate = DateTime.Now;
sale.SaleType = 'W'; // wholesale
this.DataContext = sale;
In VB, the equivalent code is:
' VB
Class Sale
' simplifed sale class
' for demo purposes
Private privateCommissionRate As Double
Public Property CommissionRate() As Double
Get
Return privateCommissionRate
End Get
Set(ByVal value As Double)
privateCommissionRate = value
End Set
End Property
 | |
Figure 3. Raw Data: Here's the raw input before applying any formatting to the bound data. |
'...
' the rest of the properties omitted for brevity
End Class
...
' initializing the Sale class
' and setting data context.
Dim sale As New Sale()
sale.Amount = 1200
sale.CommissionRate =.0125
sale.SaleDate = DateTime.Now
sale.SaleType = "W"c ' wholesale
Me.DataContext = sale
Figure 3 shows the raw data in the UI before applying any formats.
I'd wager that string formatting is the most common conversion operation applied to data before showing it in the UI. In this example Sale class, every property except the
SaleType would look better with some formatting. To help with this common scenario, Microsoft added string formatting to WPF data binding in .NET 3.5 SP1, so now it's a snap to apply a string format using standard formatter characters such as "C" for currency, "P" for percentage, and "D" for long date. Just use the
StringFormat or
ContentStringFormat attributes as shown in the following example:
<!-- applies the currency format-->
Text="{Binding Path=Amount,StringFormat=C}"
<!-- use curly braces for more complex
string formatting-->
Text="{Binding Path=CommissionRate,
StringFormat=2008 Rates :: {0:P}}"
 | |
Figure 4. Formatted Strings: Here's the result of applying some string formatters to the binding. |
<!-- some controls have direct support
for formatting-->
<Button ContentStringFormat="{}{0:P}">
<sys:Double>3.14</sys:Double>
</Button>
Figure 4 shows the results of applying these formats.
Data Metamorphosis
String formatting is useful but sometimes you want a bigger hammer to knock your data into shape. For that, WPF has binding converters. Once again WPF demonstrates its foresight when you examine how binding converters work.
Binding converters, implemented via the IValueConverter interface, grant you full control over your data representation as it moves through the binding pipeline. The converter sits between the data source and the data target, and offers you the opportunity to transform the data as it flows through the converter. Data moving from source to target goes through the
IValueConverter.Convert method. Data moving in the reverse direction goes through the
IValueConverter.ConvertBack method before arriving back at the data source.
The possibilities are tantalizing if you think about what you can do with the conversion infrastructure. Not only can you change from one data type to another but you can run code to examine the data and alter it based on various conditions. Do you want to convert a series of numbers (
1,
2,
3,
4) to an ordinal representation (
1st,
2nd,
3rd,
4th)? Use an Integer-to-String converter. How about changing the foreground color of a text box based on the sales amount? No problem, create a Decimal-to-SolidColorBrush converter.
The keys to the conversion process are the
Convert and
ConvertBack methods, both of which have the following parameters:
- value: The original value from the data source.
- targetType: The target property data type. In the following example, the target type would be the Brush type for the first binding and the String type for the second binding:
Background='{Binding Path=Amount,
Converter={StaticResource bonusConverter}}
Text= '{Binding Path=SaleDate,
Converter={StaticResource saleConverter}}
- parameter: This is an optional parameter specified in the binding.
- culture: The current CultureInfo as determined by the .NET Framework. Useful for culturally-aware formatting.
Look at
Listing 3 to see an implementation of the AmountToBonusBrushConverter, and then examine
Listing 4 to see how to configure the AmountToBonusBrushConverter binding in your XAML. Pay close attention to the three properties
BonusBrush,
BonusGoal, and
DefaultBrush in the converter class, which permit customization of the converter in the page XAML.
 | |
Figure 5. Converters in Action: The BonusBrushConverter has been applied to the second TextBlock in the UI. |
Figure 5 shows the results in the UI. As you can see in the screenshot, the first and third TextBlocks are painted the default color (set as Yellow in the following XAML block). The second TextBlock is painted green because the underlying value is greater than the BonusGoal amount of 4000:
<local:AmountToBonusBrushConverter
x:Key="bonusConverter"
BonusBrush='LightGreen'
BonusGoal='4000'
DefaultBrush='Yellow' />
As a final example here's a short snippet that shows how to convert the
SaleType char value to a more readable string.
// C#:
public object Convert(object value,
Type targetType, object parameter,
CultureInfo culture)
{
char candidate = (char)value;
switch (candidate){
case 'R':
return "Retail";
break;
case 'W':
return "Wholesale";
break;
}
return "Unknown sale type";
}
' VB:
Public Function Convert(ByVal value As Object, _
ByVal targetType As Type, _
ByVal parameter As Object, _
ByVal culture As CultureInfo) As Object _
Implements IValueConverter.Convert
Dim candidate As Char = CChar(value)
Select Case candidate
Case "R"c
Return "Retail"
Case "W"c
Return "Wholesale"
End Select
Return "Unknown sale type"
End Function
What's Left?
I hope this article series has been a good starting point on your journey to data binding mastery. I've spent a few pages covering the wonders of the WPF binding world—but I'm not finished. There is still a mountain of binding topics left to cover. Data templates, data selectors, data triggers, data providers, MultiBinding converters, and managing hierarchical data are some of the essential topics you should explore. I also haven't covered the CollectionView's sorting, filtering, grouping, and navigation abilities, or looked at list binding, or at any of the
ItemsControls elements. I hope that you see the power and potential of WPF binding by now and are ready to learn more.