Feed on
Posts
Comments

Value Converters are a useful addition to a WPF. If a value converter is specified in a binding, the source data is funneled to the converter before arriving at the binding destination.

Before you can use the converter in your XAML you need to add an instance of the converter to your XAML, usually in a resource section. Once you have the converter in a ResourceDictionary you can refer to it in the binding code.

 [XAML] 
<Window x:Class="ValueConverterTips.CustomMarkupTip"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:converters='clr-namespace:ValueConverterTips.Converters'
        Title="CusomMarkupTip"
        Height="300"
        Width="300">
    <Window.Resources>
        <converters:NumberToBrushConverter x:Key='brushConverter' />
    </Window.Resources>

    <Grid>
        <Ellipse Fill='{Binding SomeIntData,
                    Converter={StaticResource brushConverter}}'
                     Width='10'
                     Height='10' />
    </Grid>
</Window>

 

This is perfectly valid XAML and you will find it in thousands of WPF projects around the world. Programmers are always looking for a way to reduce the amount of code they need to write however and there is a way to simplify your code by creating your own MarkupExtension.

The key to this tip is that our converter class will derive from MarkupExtension in addition to implementing the IValueConverter interface.

[C# code]
namespace ValueConverterTips.Converters
{
    class NumberToBrushConverter : MarkupExtension, IValueConverter
    {
        private static NumberToBrushConverter _converter = null;

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            // determine if we have an instance of converter
            // return converter to client
            return _converter ?? (_converter = new NumberToBrushConverter());
        }
        public object Convert(object value, 
                              Type targetType, 
                              object parameter, 
                              System.Globalization.CultureInfo culture)
        {

            return new SolidColorBrush(Colors.Orange);
        }

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

    }
}

Now that you have your custom MarkupExtension class you can remove the converter from the Resources sections and use the converter class directly.

<Window x:Class="ValueConverterTips.CustomMarkupTip"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:converters='clr-namespace:ValueConverterTips.Converters'
        Title="CustomMarkupTip">

    <!-- no longer need the resources section -->
    <Grid>
        <Ellipse Fill='{Binding SomeIntData, Converter={converters:NumberToBrushConverter}}'
                 Width='10'
                 Height='10' />
    </Grid>
</Window>

[Original article on TechTarget]

9 Responses to “Simplify your Binding Converter with a Custom Markup Extension”

  1. Steve says:

    No need to create *another* instance.

    return _converter ?? (_converter = new NumberToBrushConverter());

    replace this with

    return _converter ?? (_converter = this);

  2. Jeff Norman says:

    Am I wrong or in this case i have a converter instance for each binding instead of a single shared one?

  3. Christo says:

    Why do you need _converter at all? You could just always “return this;”.

    I agree with Jeff, I think you’ll be creating an instance everywhere you use it, which kind of beats the whole objective in the first place. This might be faster than a lookup, but will increases your memory footprint.

  4. Michael says:

    I think Walt intends to use the Singleton pattern (http://msdn.microsoft.com/en-us/library/ms998558.aspx). He should provide a static Instance property that does the initialization, so that you don’t have all of these extra converter objects running around. You would bind to it using something like:

    {Binding SomeIntData, Converter={converters:NumberToBrushConverter.Instance}}

    Any problems with the Singleton approach?

  5. Bryant Likes says:

    Very cool Walt! Made me jealous since there is no MarkupExtension in Silverlight. :P

  6. Michael says:

    How about this implementation:

    using System;
    using System.Globalization;
    using System.Windows.Data;
    using System.Windows.Markup;

    namespace SomeNamespace
    {
    public sealed class SomeConverter : MarkupExtension, IValueConverter
    {
    public static SomeConverter Instance { get { return mInstance; } }

    private static readonly SomeConverter mInstance = new SomeConverter();

    static SomeConverter()
    {
    // Explicit static constructor tells the C# compiler not to mark this
    // type as BeforeFieldInit
    }

    SomeConverter() { /* Do nothing */ }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
    return mInstance;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { … }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { … }
    }
    }

    You can bind to it using:

    Converter={x:Static someNamespace:SomeConverter.Instance}

  7. Michael says:

    After reading about markup extensions, I see that it’s not necessary to use it in my example if you are using the singleton pattern. You need one or the other, not both.

  8. Mike Strobel says:

    I don’t think your static “_converter” field does what you think it does. In your example, the Xaml loader will *always* instantiate a new NumberToBrushConverter instance. Your ProvideValue implementation will then return that same instance for the first usage, and will return the first instance for each subsequent use. Your code is no more efficient than simply changing the ProvideValue to “return this;”, which is arguably more “correct”. However, if you use this converter on a binding in an ItemsControl, you’ll still incur the overhead of a new converter being created for each item.

    On the other hand, if you expose a singleton Instance property and use “{x:Static ns:MyConverter.Instance}” in your Xaml instead, only one instance will ever be created.

  9. [...] Simplify your Binding Converter with a Custom Markup Extension (Walt Ritscher) [...]