Jerry Nixon @Work: Mango Sample: 3 Solutions to Formatting Problems

Jerry Nixon on Windows

Tuesday, January 10, 2012

Mango Sample: 3 Solutions to Formatting Problems

imageOne of the most powerful aspects of XAML is it’s rich and dynamic support for data binding.

MSDN: Data binding provides a simple way for Silverlight-based applications to display and interact with data. The way data is displayed is separated from the management of the data. A connection, or binding, between the UI and a data object allows data to flow between the two. When a binding is established and the data changes, the UI elements that are bound to the data can reflect changes automatically. Similarly, changes made by the user in a UI element can be reflected in the data object. For example, if the user edits the value in a TextBox, the underlying data value is automatically updated to reflect that change.

In XAML, data binding to an object’s property returns that property’s value. If the value is “Jerry” then you get “Jerry”.

In this article we will discuss how we can convert the value of a property to something else – something we need in our UI.

Data Binding

Let’s take a minute to look at simple data binding in XAML.

Consider this DTO model:

image

To data bind to it, we do this:

image

Then we give it data, like this:

image

Then, the UI looks like this:

image

This is great. Data binding is working, and there is no code behind necessary to put values into our UI controls.

Sidebar: since the DTO model does not implement the INotifyPropertyChanged interface (which introduces the PropertyChanged event) the UI will not reflect changes to the property values if they are changed behind the scenes. I talk more about this here.

The Problem

The output is handy, but not perfect. What use is “true” in the output?

We need to convert the value of “true” to “Boy” or “Girl” respectively.

Solution 1: Class Changes

Class Changes allow us to encapsulate complex formatting logic into the class like this:

image

This let’s you do this:

image

This is handy because formatting travels with the class wherever it goes.

This is horrible because UI logic is inside your DTO model – it might be right for this UI, but it might be wrong for the next UI.

We don’t want MailFormattedVersion1 and then MailFormattedVersion2 and then more!

We need another option.

Solution 2: String Formatting

String formatting is a great part of the XAML data binding engine. It lets you format the property’s value by using standard .Net format strings. Best of all it lets you do it right in the XAML.

Note: Support for StringFormat was introduced with Mango.

Note: the following samples use the following class:

image

Appending Text

A simple binding would look like this:

image

Looks like:

image

We could append to the values with the following syntax:

image

In the code above, see the StringFormat added to the Binding syntax? This property gives you the ability to specify surrounding text to append to the current value.

Looks like:

image

Formatting Text

In a addition to the ability to append text to the value, you can also use the standard .Net format strings to format the value.

image

In the code above, see where we changed the {0} to {0:d}? This is a standard string that formats a date to MM\DD\YYYY (or short date).

In the code above, see where we changed the {0} to {0:c0}? This is a standard string that formats a number to $#,### (or currency).

Looks like:

image

Unfortunately

I had to mention this option. Unfortunately, Silverlight does not apply StringFormat syntax to Booleans.

We need another option.

Solution 3: Custom Converters

A converter is a class that implements IValueConverter. (There is also the IMultiValueConverter for WPF XAML – which supports MultiBinding).

When a value is about to be inserted into the Text property of the TextBlock, it is first passed to the converter. It is the output of the converter that is then placed in the Text property of the TextBlock.

Note: A TextBlock only supports OneWay binding. As a result we need only implement Convert(). If we used a control that supported TwoWay binding, like TextBox, we would need to implement the Convert() and the ConvertBack().

Here’s the converter:

image

In the code above, see the Convert() method? There is our logic to convert true to Boy and false to Girl. Extracting the logic from the class and putting it in a converter does two things:

  1. It keeps our data properly data bound. If we were to intercept the data in some other way, it would then introduce the need to handle the subsequent binding. A converter intercepts a binding without interfering with the binding engine.
  2. It keeps the logic in the UI where it belongs. A converter can be reused as many times as you want. It is a simple class in your UI project. However, it is specific to the UI. This solves the problem we had above where the UI logic was stick in the model.

Here’s the XAML:

image

In the code above, see MyConverter? This is a static reference to my converter class that I defined above. I pass this to the Converter property of the Binding syntax (as a StaticResource). When this property is rendered, it first passes through the converter’s Convert() method.

Looks like:

image

Problem solved! Converters are really great.

But did you notice one problem? My “BooleanConverter” converts a Boolean value to Boy or Girl. What I had a Boolean property whose property meant IsAdministrator? I don’t think Boy or Girl are the correct for the UI.

The solution simply is to create more than one converter – as many as you need for your UI scenario. And, I know what you are thinking. Aren’t we OOP developers? Wouldn’t a reusable converter be nice? It would be nice but not necessary.

However, if you insist on a reusable converter – let me show you how.

Reusable Converters

Here’s a nice bonus. How to create a converter that can be used in more than one scenario.

I happen to already have one written:

image
image

Get the code here.

In the code above, hopefully you see this is a OneWay converter. See those properties?

Here’s what they mean:

Match Gets or Sets the value which will be compared to the binding value.
Then Gets or Sets the value which will be returned if the compare is true.
Else Gets or Sets the value which will be returned if the compare is false.
UseValueAsElse Gets or Sets if the binding value will be returned if the compare is false.
When true, this overrides the Else property.

Here’s the XAML setup:

image

In the code above we create four converters. Actually we are just configuring four converters. It’s the same reused converter every time.

Caveat: Setting a date value from a string in XAML does not work in Silverlight. My example tests for null. It would be very difficult to test for an exact date because of this Silverlight XAML limitation.

It’s worth pointing out the TextBlock binding to Date: you can use StringFormat in conjunction with a Converter.

With this data:

image

We get this:

image

With this data:

image

We get this:

image

A complex example

In this example, the button is visible only when IsAdminisrtator property is true. The reusable converter effectively switches between Visible and Collapsed based on the true/false value of the Boolean.

image

In the code above, see how I make a reference to System.Windows so that I can set the value of Visibility enumeration? You can do this with any enumeration – even more complex types like Thickness.

A reusable converter is stinking handy. It can’t be reused for every single circumstance, I realize that. But it can be reused for many of them (maybe even 90% of them).

The best part of a reusable converter is that the definition of the converter is in the XAML, where it is used. That’s handy for developers.

Conclusion

Data binding is a core feature of Silverlight and you should not ignore it. It saves code behind and accomplishes UI updates when backend data changes occur.

However, you need to learn how to use it.

As for formatting data from your objects, you have different solutions to choose.

  1. Change your class – this is very effective but has the unfortunate consequence of putting your UI logic in your DTO. That may not matter to you.
  2. Use FormatString – this is very effective for most data types but does not work for all, like Boolean. Also your formatting requirements may be too complex for FormatString.
  3. Use a Converter – this is very effective to componentize your UI code and for every single data type – even complex types. However, too many converters in your project may become confusing so you might consider a reusable converter.

You have lots of options and good reasoning for each. You are your developer, not me. I cannot make the choice for you – I can just give you the choices and the thinking behind them. Choose wisely.

Best of luck!