Write more generic WPF Converters

 
 
  • Gérald Barré

In XAML, converters are commonly used in bindings to adapt source data to the required destination type. For example, BooleanToVisibilityConverter converts a Boolean value to a value in the Visibility enumeration.

This mechanism is very useful, but it can quickly lead to a proliferation of converters such as:

  • BooleanToVisibilityConverter
  • InverseBooleanConverter
  • EnumerableToBooleanConverter
  • etc.

Instead of creating a converter for each specific case, it is better to make them generic so they can be reused across more scenarios, avoiding unnecessary proliferation. For example, all the converters listed above can be replaced by a single BooleanToValueConverter. In each case, the source evaluates to a Boolean expression (equality check, null check, empty check), and only the output values differ. By exposing those output values as configurable properties, a single converter can handle all these cases:

C#
public class BooleanToValueConverter : IValueConverter
{
    public object TrueValue { get; set; }
    public object FalseValue { get; set; }
    public object DefaultValue { get; set; }

    private object GetValue(bool value)
    {
        return value ? TrueValue : FalseValue;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
            return GetValue(false);

        if (value is bool)
            return GetValue((bool)value);

        // Install-Package CodeFluentRuntimeClient (https://www.softfluent.com/products/codefluent-runtime-client)
        // Ou replace with Boolean.TryParse(string.Format("{0}", value), out boolean)
        bool boolean;
        if (ConvertUtilities.TryChangeType(value, culture, out boolean))
            return GetValue(boolean);

        var str = value as string;
        if (str != null)
            return GetValue(!string.IsNullOrEmpty(str));

        var enumerable = value as IEnumerable;
        if (enumerable != null)
        {
            var enumerator = enumerable.GetEnumerator();
            bool any = enumerator.MoveNext();
            return GetValue(any);
        }

        return DefaultValue;
    }
}

You can now use this converter in multiple scenarios:

XAML
<Application.Resources>
    <system:Boolean x:Key="TrueValue">True</system:Boolean>
    <system:Boolean x:Key="FalseValue">False</system:Boolean>

    <utilities:BooleanToValueConverter x:Key="ValueToBooleanConverter" TrueValue="{StaticResource TrueValue}" FalseValue="{StaticResource FalseValue}" DefaultValue="{StaticResource FalseValue}" />
    <utilities:BooleanToValueConverter x:Key="ReverseValueToBooleanConverter" TrueValue="{StaticResource FalseValue}" FalseValue="{StaticResource TrueValue}" DefaultValue="{StaticResource TrueValue}" />
    <utilities:BooleanToValueConverter x:Key="ValueToVisibilityConverter" TrueValue="{x:Static Visibility.Visible}" FalseValue="{x:Static Visibility.Collapsed}" DefaultValue="{x:Static Visibility.Visible}" />
</Application.Resources>

We now have a single converter that is no longer limited to one use case.

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?