Home > View Post

WPF Quick Tip: Converters as MarkupExtensions

Everybody's gotta love their IValueConverters, right? And not forgetting IMultiValueConverters too.

However, they're a bit of a pain when it comes to Xaml. I hate having to wonder all the way up to the resources section(!), create my converter, think of a name for the key and finally reference the converter through a StaticResource markup extension.

Another thing I hate is the parsing of Converter Parameters back into the appropriate type. Boo.

Not anymore with this quick tip. Hooray.

Take this MultiplyConverter (we've all written one of these):

public class MultiplyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double val = System.Convert.ToDouble(value);
        double factor = System.Convert.ToDouble(parameter);
        return val * factor;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // left as an exercise for the reader
        // don't be a wimp, it's not hard
    }
}

Which might be used something like...
    
<!-- somewhere in the resources -->
<local:MultiplyConverter x:Key="BarryTheConverter" />    
    
<Rectangle Width="{Binding ElementName=otherElement, Path=(Canvas.Left), Converter={StaticResource BarryTheConverter}, ConverterParameter=-0.5}" />

Sort of yuck.

V2

I think it's a big improvement to forego the ConverterParameter and use a property on the Value Converter itself:

public class MultiplyConverter : IValueConverter
{
    public double Factor { get; set; }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double val = System.Convert.ToDouble(value);
        return val * Factor;
    }
    
Which would be used like so...
    
<!-- somewhere in the resources -->
<local:MultiplyConverter Factor="-0.5" x:Key="BarryTheConverter" />    
    
<Rectangle Width="{Binding ElementName=otherElement, Path=(Canvas.Left), Converter={StaticResource BarryTheConverter}}" />

A minor improvement IMO. But better nonetheless, provided we don't need to create lots of converters for all the different 'Factors' we might use... unless... we implement the Converter as a markup extension.

V3

public class MultiplyConverter : MarkupExtension, IValueConverter
{
    public double Factor { get; set; }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double val = System.Convert.ToDouble(value);
        return val * Factor;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // left as an exercise for the reader
        // don't be a wimp, it's not hard
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
}
    
Which might be used like so

<!-- no need for any resources in this case! -->

<Rectangle Width="{Binding ElementName=otherElement, Path=(Canvas.Left), Converter={local:MultiplyConverter Factor=-0.5}}" />

Much better and reads easier. Hooray!

Tags: WPF

 
Josh Post By Josh Twist
11:29 PM
17 Apr 2009

» Next Post: Improving Performance of images used with 3D
« Previous Post: Cracking an XPS in WPF

Comments are closed for this post.

Posted by Sven @ 18 Apr 2009 2:01 PM
Sweet ! I see no reason not to "upgrade" all my existing converters to this state so I can choose whether to declare them as a resource or instantiate them inline withing the Converter={} clause.

I guess the disadvantage of this new instantiation method is that it creates a converter instance per Converter={} clause, right ?

Posted by josh @ 18 Apr 2009 11:48 PM
Yes, if you think of that as a disadvantage. Of course, WPF is object heavy so any saving here (in terms of reducing the number of objects) is going to be negligible.

Posted by OAB @ 27 Apr 2009 7:26 AM
Very cool. I had no idea it was that easy to write a markup extension. I will definitely be using this.

Posted by sinan @ 24 Jul 2009 12:18 PM
hey guys,
There is far simpler method of doing V3.
I am not inheriting from MarkupExtension class also,
Regular value converter.
Just use tags!! Not inline coding with curly braces.
As the following:

<Window.Title>
<Binding>
<Binding.Converter>
<ConverterEx:MyConverter MyParam="Sinan" />
</Binding.Converter>
</Binding>
</Window.Title>

class MyConverter:IValueConverter
{
public string MyParam { get; set; }

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return MyParam;
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Any ideas?

Posted by josh @ 25 Jul 2009 12:14 AM
Hi sSinan,

I've always known of this approach but, to be brutally honest, I think it's even worse than using a StaticResource. I like the shorthand format that MarkupExtensions provide.

Thanks for posting though - it's a good point!

Josh

Posted by Oliver @ 08 Sep 2009 1:29 AM
The MarkExtension worked brilliantly! Thank you.

Posted by Grant BlahaErath @ 15 Oct 2009 1:01 AM
This is a great approach. You can define as many parameters as you need this way. No need to implement a tokenizer in your converter.

Posted by Serg @ 10 Jun 2011 1:46 PM
Thanks. Was useful to me.

Posted by Brian Kejser @ 04 Jan 2012 10:42 PM
Hi

The other advantage of this approach is it is a workaround to the following problem.

http://www.hardcodet.net/2008/04/nested-markup-extension-bug

Posted by FRED @ 11 Jan 2012 3:21 PM
I prefer to have all my Converters implement singleton pattern and then use
Converter="{x:Static MyConverter.Instance}"

© 2005 - 2017 Josh Twist - All Rights Reserved.