I’m working on a super quick Silverlight 2 application for a restaurant kiosk. We’re using EF and Astoria / ADO.NET Data Services as our data provider, and to structure the client I’m using an implementation of MVVM based on Shawn Wildermuth’s great MSDN article showing how to implement simple MVVM using Silverlight 2. (the article is great in that it doesn’t go nuts trying to solve the command / trigger issue, it just makes the most of what is available and keeps things simple)
I love binding in Silverlight, but sometimes you need to do things with binding that you know how you would do in inline code, but maybe aren’t sure how to handle in binding. Nine times out of ten, a value converter can help you out.
For example, I have a DataGrid full of guests. The grid needs to show the full address for the customer. In order to make the most efficient use of screen space, I want to format that as a single cell, multi-line field. Of course, the data is modeled as separate fields for StreetAddress1, 2, City, State, Zip and Country.
Enter the value converter!
So I wrote a really quick value converter that takes in the whole object and returns back just the formatted address:
public class GuestSingleFieldAddressConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType,
object parameter,
CultureInfo culture)
{
if (!(value is Guests))
throw new ArgumentException("...", "value");
Guests guest = (Guests)value;
StringBuilder builder = new StringBuilder();
if (!string.IsNullOrEmpty(guest.StreetAddress1))
builder.AppendFormat("{0}\r\n", guest.Address1.Trim());
if (!string.IsNullOrEmpty(guest.StreetAddress2))
builder.AppendFormat("{0}\r\n", guest.Address2.Trim());
builder.AppendFormat("{0}, {1} {2}\r\n",
guest.City.TrimEvenNulls(),
guest.State.TrimEvenNulls(),
guest.ZipCode.TrimEvenNulls());
if (!string.IsNullOrEmpty(guest.Country))
builder.AppendFormat("{0}", guest.Country.Trim());
return builder.ToString();
}
public object ConvertBack(object value, Type targetType,
object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Very simple. You’ll notice that I also used a simple extension method to take care of formatting null strings:
public static class StringExtensions
{
public static string TrimEvenNulls(this string source)
{
if (source == null)
return string.Empty;
else
return source.Trim();
}
}
To use this, I then create a Static Resource in my xaml pointing to the value converter (note this is also where my View Model reference lives. Page is a base class I have that inherits from UserControl and adds some UI helper code):
<pages:Page.Resources>
<vm:GuestsViewModel x:Key="ViewModel" />
<converters:GuestSingleFieldAddressConverter
x:Key="AddressConverter" />
</pages:Page.Resources>
And when binding in the data grid, simply bind to the whole object (don’t specify a path or include a comma) and specify the value converter static resource key:
<data:DataGridTextColumn Header="Mailing Address"
Width="220"
FontSize="11"
Binding="{Binding Converter={StaticResource AddressConverter}}"
/>
Value Converters don’t solve everything in binding, but they certainly do give you a ton of options for handling different situations. They’re really easy to build and use, plus, I just like having the code for them compartmentalized out in a separate class rather than some function in a screen, or a compound field polluting my model. It makes for cleaner code and easy binding.