Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / WPF

Using a TypeConverter for Binding in WPF

Rate me:
Please Sign up or sign in to vote.
4.91/5 (15 votes)
20 Aug 2012CPOL4 min read 56.5K   685   20   5
This article will show you how to implement a class that will provide type conversion for another class. In this particular case will be providing conversion to and from a string.

Introduction

Basically what brought on this example was the need to convert a delimited list to a collection. In the application I was working on, I was using the TypeConverter class to convert values from string to specific types using Reflection. I could define a class with the proper types and then take string values and create a new instance of the class. Thus I was familiar with the TypeConverter, but had never implemented a TypeConverter. Creating a TypeConverter for a collection from a delimited list made sense. Then I thought: could I use this for binding in WPF to both a Text property and a ItemsSource property? It turns out that you can.

The title indicates that this article is specific to binding to an object in WPF, but it is really about implementing a TypeConverter for a class. This article will show you how to implement a class that will provide type conversion for another class. In this particular case, we will be providing conversion to and from a string. In WPF, a TypeConverter can be used to reduce the complexity of interfacing a custom object in a ViewModel to a View.

The TypeConverter

The TypeConverter is used to convert values between data types, and to assist property configuration at design time by providing text-to-value conversion. It uses InstanceDescriptor and System.Reflection to provide the information necessary to initialize a property at run time. Most native types have an associated TypeConverter. The default type converters are in the System.ComponentModel namespace and are named TypeConverterNameConverter. WPF and Silverlight make extensive use of the TypeConverter in binding.

To define a TypeConverter to support binding conversion for a specific class, you have to create a class that inherits from TypeConverter, and overrides the ConvertTo or ConvertFrom and also the corresponding CanConvertTo and CanConvertFrom:

C#
public class StringListTypeConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, 
        Type sourceType)
    {
      return sourceType == typeof(string);
    }
 
    public override object ConvertFrom(ITypeDescriptorContext context,
        System.Globalization.CultureInfo culture, object value)
    {
      return new StringList((string)value);
    }
 
    public override bool CanConvertTo(ITypeDescriptorContext context, 
        Type destinationType)
    {
      return destinationType == typeof(string);
    }
 
    public override object ConvertTo(ITypeDescriptorContext context,
        System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
      return value == null ? null : string.Join(", ", (StringList)value);
    }
}

If you try to delete the CanConvertTo and CanConvertFrom you will see that binding will no longer work.

There are a lot of other properties that can be overridden in the TypeConverter class, but only these four are important for binding. The TypeConverter is also used for Drag and Drop, which may be why these methods are needed. Obviously if you are not going to need to support two-way binding, you do not need to override and implement the corresponding methods.

The class that I am writing this TypeConverter for is intended to take a string that would take a comma or semicolon delimited list and convert it to a list of strings. In this case the target is IEnumerable<string>:

C#
[TypeConverter(typeof(StringListTypeConverter))]
class StringList : IEnumerable<string>
{
    private readonly IEnumerable<string> _enumerable;
    private readonly string _original;
 
    public StringList(string value)
    {
      _original = value;
      if (!string.IsNullOrEmpty(value))
      {
        _enumerable = value.Split(",;".ToCharArray()).
          Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => i.Trim());
      }
    }
 
    protected StringList(IEnumerable<string> value)
    {
      _enumerable = value;
      _original = string.Join(", "value);
    }
 
    public StringList() { }
 
    public IEnumerator<string> GetEnumerator()
    {
      return _enumerable.GetEnumerator();
    }
 
    IEnumerator IEnumerable.GetEnumerator()
    {
      return _enumerable.GetEnumerator();
    }
 
    public override string ToString()
    {
      return _original;
    }
}

You will notice that the class is decorated with a TypeConverter attribute that provides information on the class that provides the TypeConverter for the class. This provides the magic.

Now we have a class that can take a string and can convert it to a list, and be bound both to a DependencyProperty that is expecting a list of string objects and a string object. The XAML for the simple example I created to demonstrate using the TypeConverter is as follows:

XML
<Window x:Class="TypeConverterExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TypeConverterExample"
        Title="TypeConverter Binding Example"
        Height="250"
        Width="375">
  <Window.DataContext>
    <local:ViewModel />
  </Window.DataContext>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <TextBox Grid.Row="0"
             Text="{Binding Source}"
 />
    <ListBox Grid.Row="1"
             ItemsSource="{Binding Source, Mode=TwoWay}"
 />
  </Grid>
</Window>

The XAML above is actually simplified from what is in the code. When run and a list entered into the text box, the following will appear:

Using the TypeConverter Directly

Using a TypeConverter directly just requires a little bit of code which is used to find the associated TypeConverter with the class using the GetConverter method of the TypeDescriptor class:

C#
TypeConverter converter = TypeDescriptor.GetConverter(targetType);
string convertedValue = converter.ConvertFrom(value);

This code will convert to the target for the TypeConverter. Notice that the signature used is not the same as coded, which has a signature of ConvertTo(ITypeDescriptorContext, CultureInfo, object, Type). This is because the parent TypeConverter class has another signature that takes one argument, and calls the overridden TypeConverter method. Within WPF I have used the TypeConverter within a IValueConverter.

Summary

I really do not have any immediate need for a class that can used in WPF and be bound to both an IEnumerable and a string, this could become useful in some future project. The concept, however, was very useful in my current application. I can see that there would be cases where TypeConverter can simplify coding in support of binding of a custom object to WPF controls without adding unnecessary conversions in the ViewModel. I think that the flexibility of the TypeConverter is another indication of the quality of tools that Microsoft has provided the developer community.

License

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


Written By
Software Developer (Senior) Clifford Nelson Consulting
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

Comments and Discussions

 
QuestionMessage Closed Pin
20-Jan-21 14:23
Member 1505177520-Jan-21 14:23 
PraiseMy vote of 5 Pin
Gary Wheeler25-Jan-16 7:57
Gary Wheeler25-Jan-16 7:57 
GeneralMy vote of 4 Pin
Christian Amado20-Aug-12 5:56
professionalChristian Amado20-Aug-12 5:56 
GeneralRe: My vote of 4 Pin
Septimus Hedgehog20-Aug-12 6:01
Septimus Hedgehog20-Aug-12 6:01 
Excellent. Thanks Clifford.

The job I started three weeks uses an app that is exclusively WPF and there are more type converters than there are grains of sand on a beach. I don't understand much about them (being a WPF noobie) but your article simplifies the concepts for me in chunks I can understand. Thanks a lot for sharing.
"I do not have to forgive my enemies, I have had them all shot." — Ramón Maria Narváez (1800-68).
"I don't need to shoot my enemies, I don't have any." - Me (2012).

AnswerRe: My vote of 4 Pin
Clifford Nelson20-Aug-12 14:16
Clifford Nelson20-Aug-12 14:16 
GeneralRe: My vote of 4 Pin
Septimus Hedgehog24-Aug-12 3:58
Septimus Hedgehog24-Aug-12 3:58 

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.