A better way to declare your ValueConverters in XAML

Generally most people define a resource like this "<conv:AdditionConverter x:Key="AdditionConverter" />" and then use the converter like this "{Binding Converter="{StaticResource NegatingConverter}} "

This is OK but it’s fairly verbose and leads to converters being defined all over the place. An alternative is to use a MarkupExtension to minimize the amount of XAML code required. E.g.:


    public class AdditionConverter : MarkupExtension, IValueConverter
    {
        private static AdditionConverter _converter;

        public object Convert(object value, Type targetType,
        object parameter, System.Globalization.CultureInfo culture)
        {
            // convert and return something
        }

        public object ConvertBack(object value, Type targetType,
        object parameter, System.Globalization.CultureInfo culture)
        {
            // convert and return something (if needed)
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (_converter == null)
                _converter = new AdditionConverter();
            return _converter;
        }
    }

Doing this you no longer need to define a resource as the XAML parser knows how to ask the markup extension to supply the converter. You end up with a syntax like this:
{Binding Converter={conv:MyConverter}} 

This approach has an added advantage of ensuring that all your converters are singletons.



Taking it further: Making a wrapper base class

This article explains a generic markup extension class that hides the MarkupExtension implementation details and makes the process above even easier.

ConverterMarkupExtension.cs

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

namespace sfc.Converters
{
    [MarkupExtensionReturnType(typeof(IValueConverter))]
    public abstract class ConverterMarkupExtension :
    MarkupExtension where T : class, IValueConverter, new()
    {
        private static T _converter;

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (_converter == null)
            {
                _converter = new T();
            }
            return _converter;
        }
    }

    [MarkupExtensionReturnType(typeof(IMultiValueConverter))]
    public abstract class MultiConverterMarkupExtension :
    MarkupExtension where T : class, IMultiValueConverter, new()
    {
        private static T _converter;

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (_converter == null)
            {
                _converter = new T();
            }
            return _converter;
        }
    }
}

There are 2 classes here, one for IValueConverter's and one for IMultiValueConverters.

You can then modify your classes to inherit from this class and they nolonger need to worry about implementing MarkupExtension E.g.:

    public class AdditionConverter : ConverterMarkupExtension<AdditionConverter>
    {
        public object Convert(object  value, Type targetType, 
        object  parameter, System.Globalization.CultureInfo culture)
        {
            // convert and return something
        }

        public object  ConvertBack(object value, Type  targetType, 
        object parameter,  System.Globalization.CultureInfo culture)
        {
            // convert and return something (if needed)
        }
    }



No comments:

Post a Comment