Saturday, February 26, 2011

BooleanToVisibilityConverter

Update April 14th, 2012: I was so sick of copy/pasting this baby that it’s now part of my WPF Converters project.

This is a small thing, really, but how often do you curse WPF's BooleanToVisibilityConverter (or Silverlight’s lack thereof) because it doesn't allow you to convert true to Visibility.Collapsed instead of Visibility.Visible? Or what about converting false to Visibility.Hidden instead of Visibility.Collapsed?

Well, it happens to me in just about every non-trivial project, so I tend to use my own BooleanToVisibilityConverter that addresses these requirements. Here it is:

public sealed class BooleanToVisibilityConverter : IValueConverter
{
    public bool IsReversed { get; set; }
    public bool UseHidden { get; set; }
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var val = System.Convert.ToBoolean(value, CultureInfo.InvariantCulture);
        if (this.IsReversed)
        {
            val = !val;
        }
        if (val)
        {
            return Visibility.Visible;
        }
        return this.UseHidden ? Visibility.Hidden : Visibility.Collapsed;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Now I can convert true to Visibility.Collapsed like this:

<local:BooleanToVisibilityConverter x:Key="whatever" IsReversed="true"/>
...
<Button Visibility="{Binding IsChecked, ElementName=someCheckBox, Converter={StaticResource whatever}}"/>

Or I can convert false to Visibility.Hidden rather than Visibility.Collapsed like this:

<local:BooleanToVisibilityConverter x:Key="whatever" UseHidden="true"/>
...
<Button Visibility="{Binding IsButtonEnabled, Converter={StaticResource whatever}}"/>

I can even convert true to Visibility.Hidden and false to Visibility.Visible like this:

<local:BooleanToVisibilityConverter x:Key="whatever" IsReversed="true" UseHidden="true"/>
...
<Button Visibility="{Binding IsButtonDisabled, Converter={StaticResource whatever}}"/>

Anyway, that's as much flexibility as I've needed when converting Boolean values to Visibility values.

6 comments:

SimpleM said...

Nice one. I have been creating inverse boolean converters all this time. This is a nice one stop shop approach

Valeriu Caraulean said...

I've been using similar generic class for a long time. But now I tend using two separate things:
- VisibleIfTrueConverter
- VisibleIfFalseConverter
I found it to be more readable in xaml and it's more straightforward.
And I skip jumping to converter's code to see what Reversed means...

Kent Boogaart said...

@Valeriu: yeah, I name my resources appropriately to avoid that problem (eg. "ReverseBooleanToVisibilityConverter") Was just too lazy to type that in my post.

Josh Einstein said...

Interestingly enough, you can generalize this even further. I created a BooleanConverter that can be used like: Visibility="{Binding Value, WhenTrue=Visible, WhenFalse=Hidden}". The WhenTrue and WhenFalse properties are typed as Object and so they can target any bindable property.

If you're interested, I also posted about a SwitchConverter that takes it even a step further and let's you add simple "select case" statements inline.

AnEnglishmanInNorway said...

Kent, I've often wanted to pay something back for the effort that you and many others put in to so much benefit for so many - and finally I feel I might be able to offer something useful, even though I'm fairly new to WPF.

One of the features that I like about WPF binding is that if the returned object from an IValueConverter implementation is not of the correct type for the property to which it is bound, then binding will try to convert automatically. If the value converter returns a string then binding has plenty of scope, since many .NET types - in particular, the primitives - offer conversion to and from string. To make use of this I have created 2 simple converters, BoolToText and EnumToText. The input value to the converter is used as a lookup in a colon separated list of strings provided in the ConverterParameter - then it's just a question of giving strings which can be converted to the value you need. Here's an example of EnumToText being used to drive a checkbox visibility from a (3 membered) enum:

<CheckBox Content="Visibility bound to Orientation" Visibility="{Binding Orientation,
Converter={my:EnumToTextConverter}, ConverterParameter=Visible:Hidden:Collapsed}" />

Obviously you can easily drive a visibility from a bool in the same way with BoolToText. Here I am setting the background based on a boolean:

<Window.Background>
<Binding ElementName="DesignModeHelper" Path="IsInDesignMode"
Converter="{StaticResource BoolToText}" ConverterParameter="LightBlue:Wheat" />
</Window.Background>

I've used the converters to produce several different types of values based on input enums and bools, without writing a line of code. I particularly like the solution because it keeps the mapping between the bound variable and the effect localised at the same place - I don't need to create several different instantiations for various results.

I hope this might be interesting.
Regards
Pete Story

Gishu said...

Love it! I was out shopping for a converter that does False=>Hidden. Thanks for posting (& saving me a bunch of time)...