So What’s a Silverlight Value Converter Anyway?
Silverlight has an excellent data binding engine that allows data to be bound through XAML or programmatically with code. My number one reason for using Silverlight on client projects is the data binding support. Once you get into it and understand how it works it can save a lot of time when building applications.
When you’re binding data to controls there will be times when the data needs to be modified or tweaked some on the way into a control or as the data leaves a control and goes back to the source property (during a TwoWay binding for example). Sure, you can always write code to change a given value, but in many cases it’s much easier to write a simple value converter instead that can be re-used. In this post I’ll walk through creating a value converter and then show the code for a few of the value converters I find myself using fairly frequently.
Creating a Value Converter
Let’s look at a simple example of creating a value converter to get started. Many applications work with DateTime objects but don’t want the time displayed or want the date formatted a specific way. If you bind a DateTime property to something like a TextBlock in Silverlight you’ll get the standard output based upon the current culture:
In the case of a birthday you want to show the date but don’t need the time of course. You’d want something like the following:
Although a separate property could be created in the source object being bound that handles formatting the date, a value converter can be created and re-used over and over anytime a specific date format needs to be created. To create a value converter you’ll need to add a Silverlight class into your project and implement an interface named IValueConverter (located in the System.Windows.Data namespace). This interface defines two members including Convert and ConvertBack. Convert is used to modify data as its bound from the source object to the control. ConvertBack works the other way. As a user changes data in a control such as a TextBox the data can be "converted back" to the original data type in the source object by using ConvertBack.
Both Convert and ConvertBack accept the same parameters including the data being bound, the type of the data being bound, any parameter data passed in that can be used in the data conversion process as well as the target culture that the entire process is running under (English, Spanish, French, etc.). Here's what the method signatures look like:
object Convert(object value, Type targetType, object parameter, CultureInfo culture);
object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
Once IValueConverter is added to your class you can right-click it in Visual Studio and select Implement Interface from the menu to automatically fill in the Convert and ConvertBack methods. If you're using Visual Basic you can simply hit enter after the interface name to accomplish the same thing. Here’s an example of a date value converter that allows dates to be formatted:
using System; using System.Windows; using System.Windows.Data; using System.Globalization; namespace View.Converters { public class DateConverter : IValueConverter { #region IValueConverter Members //Called when binding from an object property to a control property public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null || (DateTime)value == DateTime.MinValue) return null; DateTime dt = (DateTime)value; return dt.ToString((string)parameter, culture); } //Called with two-way data binding as value is pulled out of control and put back into the property public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string val = (string)value; DateTime outDate; if (DateTime.TryParse(val, culture, DateTimeStyles.None, out outDate)) { return outDate; } return DependencyProperty.UnsetValue; } #endregion } }
To use a value converter you’ll need to first reference the class’s namespace (and assembly if the class is in a separate project). This can be done in the XAML file where the value converter will be used, in App.xaml or in a merged resource dictionary file. An example of defining a namespace in a resource dictionary file named Styles.xaml is shown next:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converters="clr-namespace:View.Converters" > … </ResourceDictionary>
Once the namespace is defined you need to define the converter using XAML and give it a key that can be used to reference the value converter (similar to an ID in ASP.NET):
<converters:DateConverter x:Key="DateConverter" />
Now that the converter is defined you can use it in any data binding that handles a DateTime object using the Binding object’s Converter and ConverterParameter properties. Looking at the code below you can see that the converter is referenced using the StaticResource keyword (since it’s defined as a resource within your Silverlight project) which locates the appropriate key for the converter.
<TextBox x:Name="txtBirthday" Text="{Binding Birthday, Mode=TwoWay, Converter={StaticResource DateConverter},ConverterParameter=d}" FontFamily="Arial" Width="200" Height="20" Margin="5" />
This automatically routes all in and out data binding operations (since it’s a TwoWay binding) through the value converter. If data is being bound to a control such as a TextBox the parameter passed (“d” in this example) is used to format the DateTime object data. If data is updated in the control it’ll automatically be converted from a string back into a DateTime object.
Value Converter Examples
I have quite a few value converters that I use from time to time in client applications and have listed some of them below. If you have a favorite one you’re willing to share with others please add a comment to this post with your name, a description of the value converter as well as the code and I’ll update the blog (and give you credit of course) so that a nice library of value converters built-up over time. Update: Shawn Wildermuth let me know that the Silverlight Contrib project has some similar value converters as well as others so check that out as well if you get a chance.
BoolToVisibilityConverter
Handles converting boolean values into Visibility enumeration values. This is useful when controls need to be shown and hidden based upon the value of a boolean property. The Inverse parameter value can be passed when you want to hide a control when the property being bound is true or show the control when the value is false.
using System; using System.Windows; using System.Windows.Data; namespace JobPlan.View.Converters { public class BoolToVisibilityConverter : IValueConverter { #region IValueConverter Members public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (parameter == null) { return ((bool)value == true) ? Visibility.Visible : Visibility.Collapsed; } else if (parameter.ToString() == "Inverse") { return ((bool)value == true) ? Visibility.Collapsed : Visibility.Visible; } return false; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } #endregion } }
DecimalFormatterConverter
Converts decimal values into different formats.
using System; using System.Windows.Data; namespace JobPlan.View.Converters { public class DecimalFormatterConverter : IValueConverter { #region IValueConverter Members public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string format = (parameter == null) ? "#.##" : parameter.ToString(); return (value == null) ? String.Empty : decimal.Parse(value.ToString()).ToString(format); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { try { if (value != null && value.ToString() != String.Empty) { return decimal.Parse(value.ToString()); } } catch { } return null; } #endregion } }
ListCountVisibilityConverter
Handles showing and hiding controls such as ListBox based upon the number of items in the collection the control is bound to. For example if there is only one item in a collection you may not want to show a ListBox control due to the data being shown with other controls in the UI. You could pass a value of 1 for the ConverterParameter and the ListBox would automatically be hidden.
using System; using System.Windows; using System.Windows.Data; using System.Collections; namespace JobPlan.View.Converters { public class ListCountVisibilityConverter : IValueConverter { #region IValueConverter Members public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value != null) { IList list = value as IList; if (list != null) { int minCount = int.Parse(parameter.ToString()); return (list.Count > minCount) ? Visibility.Visible : Visibility.Collapsed; } } return Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } #endregion } }
NullToVisibilityConverter
Handles showing and hiding a control based upon a value being null or not.
using System; using System.Windows; using System.Windows.Data; namespace JobPlan.View.Converters { public class NullToVisibilityConverter : IValueConverter { #region IValueConverter Members public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (parameter == null) //Not invert parameter passed { return (value == null) ? Visibility.Collapsed : Visibility.Visible; } else if (parameter.ToString() == "Inverse") { return (value == null) ? Visibility.Visible : Visibility.Collapsed; } return Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } #endregion } }
StringTruncateConverter
Trims a string down to a specific length for display in the user interface.
using System; using System.Windows.Data; namespace JobPlan.View.Converters { public class StringTruncateConverter : IValueConverter { #region IValueConverter Members public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { int maxLength; if (int.TryParse(parameter.ToString(), out maxLength)) { string val = (value == null) ? null : value.ToString(); if (val != null && val.Length > maxLength) { return val.Substring(0, maxLength) + ".."; } } return value; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } #endregion } }