Click here to Skip to main content
15,879,535 members
Articles / Desktop Programming / WPF

A Universal Value Converter for WPF

Rate me:
Please Sign up or sign in to vote.
5.00/5 (13 votes)
9 Jul 2010CPOL3 min read 37.2K   36   6
A simple IValueConverter implementation that makes use of the framework type converters in order to convert between a large range of source / target types

This post provides a simple IValueConverter implementation that makes use of the framework type converters in order to convert between a large range of source / target types. This converter can be used both within bindings and in code-behind to give more concise property setters.

Introduction

One of the great features of the XAML language is that it is flexible, concise and expressive (yes, I know that XML can be a little verbose, but if you try to create a complex UI purely in code-behind, I think you will agree with my observations!). For example, you can set the fill of a rectangle by simply specifying the named color:

XML
<Rectangle Fill="Green"/)

or … you can specify the RGB values directly:

XML
<Rectangle Fill="#00FF00"/)

Looking at the above examples, you might be fooled into thinking that the Fill property is of type Color. People who are new to WPF often find that this is not the case the first time they try to bind a property of type Color to the Fill property (they would never set the Fill property directly in code behind, because that would be a cardinal sin!). The Fill property is actually of type Brush, and the XAML parser is performing some cunning type conversions in order to make the above markup work.

The solution to this problem of binding a Color to the Fill property is to create a value converter:

C#
public class ColorToBrushConverter : IValueConverter
{
  public object Convert(object value, Type targetType, 
		object parameter, CultureInfo culture)
  {
    if (value is Color)
    {
      return new SolidColorBrush((Color)value);
    }
    return null;
  }
 
  public object ConvertBack(object value, Type targetType, 
		object parameter, CultureInfo culture)
  {
    throw new Exception("The method or operation is not implemented.");
  }
}

Which can be used as follows:

XML
<Rectangle Fill="{Binding Path=MyColorProperty,
                                  Converter={StaticResource ColorToBrushConverter}} "/)

If you search Google for ColorToBrushConverter, you can see that there are a great many people who have implemented this simple little converter. But what happens if you want to bind to a string representation of color? or you want to bind to a stroke dash property or path geometry? Whilst value converters are simple to implement, it is a shame that you have to create so many of them!

A Universal Value Converter

Wouldn’t it be great to have a single value converter that has the same flexibility as the XAML parser? It is actually very simple to create such as converter (and after creating probably my 5th ColorToBrushConverter, I have no idea why it took so long before I realized this!). The .NET Framework has had an API for conversion between different types via TypeConverters for a long time. They were used extensively in .NET technologies for databinding and designer support, and much more.

A value converter can obtain a suitable TypeConverter for the target property, then perform the required conversion:

C#
public class UniversalValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, 
		object parameter, CultureInfo culture)
    {
        // obtain the converter for the target type
        TypeConverter converter = TypeDescriptor.GetConverter(targetType);
 
        try
        {
            // determine if the supplied value is of a suitable type
            if (converter.CanConvertFrom(value.GetType()))
            {
                // return the converted value
                return converter.ConvertFrom(value);
            }
            else
            {
                // try to convert from the string representation
                return converter.ConvertFrom(value.ToString());
            }
        }
        catch (Exception)
        {
            return value;
        } 
    }
 
    public object ConvertBack
	(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Note that this value converter first tries to convert directly from the source to the target type, if this is not possible it then tries to convert via a string representation (I am not sure whether it is more correct to obtain a TypeConverter for the String type, then use this rather that invoking ToString on the value being converted, but the above works for me :) ).

You can see this converter in action below where a range of type conversions are demonstrated:

XML
<TextBlock Text="Converting String to Brush ...."/>
<Rectangle Fill="{Binding ElementName=colorTextBox, Path=Text, 
	Converter={StaticResource UniversalValueConverter}}"/>
<TextBox x:Name="colorTextBox"
             Text="Red"/>
 
<TextBlock Text="Converting String to Geometry ...."/>
<Path Data="{Binding ElementName=geometryText, Path=Text, 
	Converter={StaticResource UniversalValueConverter}}"/>
<TextBox x:Name="geometryText"                  
             Text="M 10,20 C 10,2.5 40,35 40,17 H 28"/>
 
<TextBlock Text="Converting String to DoubleCollection (stroke dash) ...."/>
<Line StrokeDashArray="{Binding ElementName=dashText, Path=Text, 
	Converter={StaticResource UniversalValueConverter}}"/>
<TextBox x:Name="dashText"                  
            Text="2 2 4 5"/>

For the first conversion, string to brush, you can use named colors, and the hex notation in its range of abbreviated forms (#AF7, #AAFF77, #FFAAFF77 …). You can also use this converter to convert from string to their corresponding enum values, for example binding the string Collapsed” to the Visibility property.

Value Conversion in Code Behind

The above converter really is Swiss army knife for bindings, but what about code-behind? You are still constrained by the type requirements of the property being set:

C#
rect1.Fill = new SolidColorBrush()
{
    Color = Colors.Red
};

Value converters, whilst typically used in binding, can also be used directly in code-behind. The following extension method extends SetValue method for setting dependency properties to make use of the above value converter:

C#
/// <summary>
/// Sets the given dependency property, applying type conversion where required
/// </summary>
public static void SetValueEx(this DependencyObject element, 
		DependencyProperty property, object value)
{
    var conv = new UniversalValueConverter();
    var convertedValue = conv.Convert(value, property.PropertyType, 
		null, CultureInfo.InvariantCulture);
    element.SetValue(property, convertedValue);
}

Which provides a more flexible mechanism for setting property values:

C#
rect1.SetValueEx(Rectangle.FillProperty, Colors.Red);
rect2.SetValueEx(Rectangle.FillProperty, "Blue");
rect3.SetValueEx(Rectangle.FillProperty, "#FFEE55");
rect4.SetValueEx(Rectangle.FillProperty, new SolidColorBrush(Colors.Orange));

… and Silverlight?

Unfortunately, Silverlight lacks the TypeDescriptor class which is used to obtain TypeConveters. I am guessing that type conversion within Silverlight is ‘baked-in’ to the XAML parser, which means that it is not possible to re-use this logic. :(

You can download the full source for this blog post from here.

Regards,
Colin E.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Scott Logic
United Kingdom United Kingdom
I am CTO at ShinobiControls, a team of iOS developers who are carefully crafting iOS charts, grids and controls for making your applications awesome.

I am a Technical Architect for Visiblox which have developed the world's fastest WPF / Silverlight and WP7 charts.

I am also a Technical Evangelist at Scott Logic, a provider of bespoke financial software and consultancy for the retail and investment banking, stockbroking, asset management and hedge fund communities.

Visit my blog - Colin Eberhardt's Adventures in .NET.

Follow me on Twitter - @ColinEberhardt

-

Comments and Discussions

 
QuestionGreat Work Pin
Abinash Bishoyi3-Nov-11 6:29
Abinash Bishoyi3-Nov-11 6:29 
GeneralMy vote of 5 Pin
Evandro Viter6-Apr-11 2:08
Evandro Viter6-Apr-11 2:08 
GeneralGood stuff... Pin
Andrew Rissing13-Jul-10 6:34
Andrew Rissing13-Jul-10 6:34 
GeneralRe: Good stuff... Pin
Colin Eberhardt13-Jul-10 21:21
Colin Eberhardt13-Jul-10 21:21 
GeneralMy vote of 5 Pin
Henke Andersson12-Jul-10 21:54
Henke Andersson12-Jul-10 21:54 
GeneralMy vote of 5 Pin
dimsprin3dx12-Jul-10 21:23
dimsprin3dx12-Jul-10 21:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.