Piping Converters in WPF and Silverlight

One limitation in WPF and Silverlight is that a Binding can only have one converter.  At least for me, this has meant that I've created a lot of very specific converters in my projects.  If I needed a converter to format a string then turn it into a uri, I'd make a class to do just that.  After getting frustrated by the abundance of single use converters, I decided there has to be another way.  I tried thinking up this clever scheme where I'd use ConverterParameter as a small-scripting language to tell it which steps to perform.  Anyone reading this is probably already thinking, "that's not clever, that's a crappy idea," and they're right.  It is a crappy idea, which is why I didn't do it.  You see, you have to think up a few bad ideas first so that you know your good idea is actually good.

I'm sure I'm not the only programmer who goes to bed with a problem in his head, and dreams the solution: Well, that's exactly how this came about.  So I wrote up this simple converter that has a list of child converters and pipes the output of each to the input of the next.  Each converter must have a CanConvertAttribute describing the types it can convert from and to so that the PipingConverter can pass the correct targetType to the next converter in line.  I've allowed for multiple CanConvertAttributes in case one converter can serve many purposes.  The PipingConverter performs a depth first search to find the first valid conversion path between the value and the final target type. I've attached the Silverlight project which can easily be ported to WPF to get the same effect.  I'll also leave it as an exercise for the reader to implement two way conversion.

To make use of this, simply create your PipingConverter in xaml or code, and add the child converters in the order you want them executed.  So if you wanted to take a list of strings from one of your data objects, sort it, convert it to a comma delimited string, then make it all caps, your code would look like:

<local:PipingConverter x:Key="ListConverter">
     <local:SortListConverter />
     <local:ListToStringConverter />
     <local:StringCaseConverter LetterCase="Upper" />
</local:PipingConverter>

and to bind, simply use:

<

TextBlock Text="{Binding Converter={StaticResource ListConverter}}" />

It fits so well that I'm surprised that it wasn't included at least in WPF.  Maybe someday, I'll try doing the same thing for multi converters.

 

NOTE: After writing this, I found that Josh Smith has posted a quite simmilar solution here.  I didn't know of his solution before coming up with mine, so that fact that they turned out so similarly just validates both of our approaches.

PipingConverter.zip