Multi-Value Converters, Value Converters and the Case of the Wrong Target Type

<TextBlock>
    <TextBlock.Text>
        <MultiBinding>
            <MultiBinding.Converter>
                <local:UnwrapperConverter />
            </MultiBinding.Converter>
            <Binding ...>
                <Binding.Converter>
                    <local:WrapperConverter />
                </Binding.Converter>
            </Binding>
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

The above XAML places a MultiBinding expression on the Text dependency property of TextBlock. This MultiBinding in turn takes its value from a Binding expression and passes it through a custom converter. The Binding expression itself has a converter of its own.

Here is the code for the converters. It is pretty straightforward:

public class Wrapper
{
public readonly object Wrapped;
public Wrapper(object wrapped)
{
this.Wrapped = wrapped;
}
}
public class WrapperConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture) { return new Wrapper(value); } public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture) { return Binding.DoNothing; } } public class UnwrapperConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture) { Debug.Assert(values[0] is Wrapper, "value received is not a Wrapper"); return ((Wrapper)values[0]).Wrapped; } public object[] ConvertBack(object value, Type[] targetTypes,

object parameter, CultureInfo culture) { throw new NotSupportedException(); } }

The WrapperConverter wraps the item returned from the simple Binding and the UnwrapperConverter in the MultiBinding unwraps it and sends it to the TextBlock‘s Text property.
Pretty simple up until now, right? Right. If only it would work.

You see, the assertion in UnwrapperConverter throws an exception because the value it receives is not a Wrapper, but rather a string.
Why does this happen, you ask. I’m sure you could guess by yourself if I told you the string is “MyNamespace.Wrapper”.
Yes – Wrapper.ToString is called on the object returned from the WrapperConverter. Buy why?!

To understand this, we must first look at the Convert method’s targetType parameter in IValueConverter and IMultiValueConverter. The value we get in this parameter is the type the property we set the Binding or MultiBinding on must be of. This is very helpful when writing a multi-purpose converter, where we can test the value of this parameter to see what type we should return.

But what value is the parameter set to when the converter works on a Binding inside a MultiBinding? It doesn’t have any property to set, so you’d think the type would be the generic object.

You’d be wrong.

The target type sent to the converter, and therefore the target type of the binding itself, is taken from the containing MultiBinding, so in our case, when you return a value from the converter to the Binding, it is then converted to a string – the type of the TextBlock.Text dependency property – and only then passed on to the MultiBinding‘s converter.

Fix? In the next service pack, I hope.

Note: When not sending the value through a converter, the type will not be changed to the target type.

Advertisements

One thought on “Multi-Value Converters, Value Converters and the Case of the Wrong Target Type

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s