BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Windows Presentation Foundation
.NET 4.0+

WPF Data Binding - Custom Value Converters

The ninety-eighth part of the Windows Presentation Foundation Fundamentals tutorial continues to look at value converters and their use in data binding. This article explains how to create custom value converters.

Registering the Custom Value Converter

Using a custom value converter requires the same three steps as described in the previous article. Firstly, you need to make sure that the XAML can find the converter class by registering the namespace. Next, you create a resource for the converter. Finally, you configure the data bindings that use the custom converter.

You register the namespace in the Window element. Currently, two namespaces are registered using xmlns attributes. These define two standard namespaces used by WPF We can add a third that points to the namespace containing our converter.

To register a namespace you need to give it an alias, such as the "x" used by one of the existing items, and a path to the namespace. For .NET namespaces, you can use the prefix, "clr-namespace" before the namespace name. For example:

xmlns:conv="clr-namespace:CustomValueConverterDemo"

The above provides the XAML with access to the CustomValueConverterDemo namespace with the alias, conv. To include it in the Window, update the Window element's opening tag, as follows:

<Window x:Class="CustomValueConverterDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:conv="clr-namespace:CustomValueConverterDemo"
        Title="Custom Value Converter Demo" Height="200" Width="200">

Next, add a new resource. We'll have one for each of the converters that we wish to use. Note that the conv prefix is used to identify the namespace alias needed to find the new converter.

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="B2VConverter"/>
    <conv:InverseBooleanToVisibilityConverter x:Key="InvB2VConverter"/>
</Window.Resources>

Finally, we can configure the rectangle control's Visibility property, binding it to the check box and using the InverseBooleanToVisibility converter:

<Rectangle Width="50" Height="50"
        HorizontalAlignment="Left" Fill="Blue" Margin="0 10 0 0"
        Visibility="{Binding ElementName=Circle,Path=IsChecked,
                                Converter={StaticResource InvB2VConverter}}"/>

Run the program again and toggle the check box to see the results. You should see that either the circle or the square is visible according to the status of the check box.

Parameterised Value Converters

Some value converters require additional configuration. This can be included in the binding expression using the ConverterParameter option. The value specified is passed to the parameter argument of the value converter's methods.

Let's make a new Boolean to Visibility converter that can be used for both of our bindings. We'll allow it to be configured so that true and false can be mapped to any of the Visibility options, opening up the possibility of using Hidden instead of Collapsed. To do so, we'll accept a parameter that maps the two desired Visibilities. We'll separate the values for true and false with the bar symbol. For example, the following will convert true to Visible and false to Hidden:

ConverterParameter=Visible|Hidden

To begin, create a new class file named, "FlexibleBooleanToVisibilityConverter". Modify the file to add the namespaces we used before. Replace the code for the class with the following:

public class FlexibleBooleanToVisibilityConverter : IValueConverter
{
    public object Convert(
            object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool booleanValue = (bool)value;
        return VisibilityFromParameter(parameter.ToString(), booleanValue);
    }

    public object ConvertBack(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        Visibility visibility = (Visibility)value;
        Visibility trueVisibility = VisibilityFromParameter(parameter.ToString(), true);
        return visibility = trueVisibility;
    }

    private Visibility VisibilityFromParameter(string parameter, bool booleanValue)
    {
        string[] visibilities = parameter.Split('|');
        string visibility = booleanValue ? visibilities[0] : visibilities[1];
        return (Visibility)Enum.Parse(typeof(Visibility), visibility);
    }
}

You can see that the structure of the code is the same as that for the simpler converter. The key difference is that the "parameter" parameter is used to specify the mappings for the Boolean values.

We can now use the new converter instead of the two existing ones. The namespace is the same, so we don't need to register it again. We do need to create a resource for the converter, which can replace the current resources:

<Window.Resources>
    <conv:FlexibleBooleanToVisibilityConverter x:Key="B2VConverter"/>
</Window.Resources>

Finally, replace the XAML for the ellipse and rectangle with the following code. Note the use of the new converter and the ConverterParameter options, which specify the mappings.

<Ellipse Width="50" Height="50" Fill="Red" 
        HorizontalAlignment="Left" Margin="0 10 0 0"
        Visibility="{Binding ElementName=Circle,Path=IsChecked,
                                Converter={StaticResource B2VConverter},
                                ConverterParameter=Visible|Hidden}"/>
<Rectangle Width="50" Height="50"
        HorizontalAlignment="Left" Fill="Blue" Margin="0 10 0 0"
        Visibility="{Binding ElementName=Circle,Path=IsChecked,
                                Converter={StaticResource B2VConverter},
                                ConverterParameter=Hidden|Visible}"/>

Run the program again and toggle the check box. You will see that the results are slightly different to the previous example. This is because the updated bindings use Hidden instead of Collapsed when the control is not visible. This repositions the square, as the circle continues to reserve screen space when hidden. Changing the converter parameters to "Visible|Collapsed" and "Collapsed|Visible" would give the original results.

23 September 2014