Thursday, March 16, 2017

WPF Tip #4 - Data Binding with IValueConverter

The first three tips have all been related to WPF data binding. Let's continue the theme with IValueConverter. IValueConverter is used in conjunction with the Converter parameter on a property's Binding.

Converters are used whenever the type of the property being bound does not match the type of the property in the binding source. Converting a boolean value to Visibility enum value is the most common conversion throughout all Xaml code. It is so common that the .NET Framework has its own BooleanToVisibilityConverter class that implements IValueConverter.

To implement your own converter, simply create a class that implements the IValueConverter interface. The interface has two methods, Convert() and ConvertBack(). If your property will never update its binding source, the ConvertBack() method does not need to be implemented. If you leave the default code Visual Studio puts in newly generated methods to throw a NotImplementedException, you will quickly find out if the ConvertBack() method is necessary for your converter.

This simple example is a variation on the BooleanToVisibilityConverter. It is an IndecisiveEnumToVisibilityConverter. Here is what my IndecisiveEnum looks like:

public enum IndecisiveEnum
{
    Yes,
    Probably,
    ThinkSo,
    NotSure,
    No,
    WhyWouldAnyoneDoThat,
    NotUnlessEveryoneElseIsDoingIt
}

The converter needs to take one of these values and return a Visibility. Here's the implementation of Convert() and ConvertBack().

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    if (!(value is IndecisiveEnum))
        return Visibility.Collapsed;

    var typedValue = (IndecisiveEnum)value;

    switch (typedValue)
    {
        case IndecisiveEnum.Yes:
        case IndecisiveEnum.Probably:
        case IndecisiveEnum.ThinkSo:
            return Visibility.Visible;
        case IndecisiveEnum.No:
        case IndecisiveEnum.WhyWouldAnyoneDoThat:
            return Visibility.Collapsed;
        case IndecisiveEnum.NotUnlessEveryoneElseIsDoingIt:
            return IsEveryoneElseDoingIt() ? Visibility.Visible : Visibility.Collapsed;
        case IndecisiveEnum.NotSure:  // return a 50/50 random chance of yes/no
            Random gen = new Random();
            int prob = gen.Next(100);
            return prob <= 50 ? Visibility.Visible : Visibility.Collapsed;
        default:
            return Visibility.Collapsed;
    }
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
    if (!(value is Visibility))
        return IndecisiveEnum.No;

    var typedValue = (Visibility)value;

    switch (typedValue)
    {
        case Visibility.Collapsed:
        case Visibility.Hidden:
            return IndecisiveEnum.No;
        case Visibility.Visible:
            return IndecisiveEnum.Yes;
        default:
            return IndecisiveEnum.No;
    }
}

For the IndecisiveEnum.NotUnlessEveryoneElseIsDoingIt value, the case calls a method which we'll pretend calls a web service to find out if everyone else is actually doing it or not. If the input value is NotSure, then a random call will return either Visible or Collapsed. Converting back is a little more straightforward.

Consuming this converter in the WPF Xaml consists of two parts. First, the converter must be defined as a resource.

<Window.Resources>
    <local:IndecisiveEnumToVisibilityConverter x:Key="IndecisiveEnumToVisibilityConverter" />
</Window.Resources>

Finally, the resource can be used in the binding for our TextBox Visibility property.

<TextBox Visibility="{Binding IsVisible, Converter={StaticResource IndecisiveEnumToVisibilityConverter}}"/>

That's it. Next time we'll look at the IMultiValueConverter, which is used with MultiBinding.

 

del.icio.us Tags: ,,,

No comments:

Post a Comment