56

I have a control that I want to show/hide, depending on the value of a boolean.

I have a NegatedBooleanConverter (switches true to false and vice versa) and I need to run this converter first. I have a BooleanToVisibilityConverter and I need to run this converter after the NegatedBoolConverter.

How can I fix this problem? I want to do this in XAML.

edit: this is a possible solution.

That doesn't seem to work. It first converts the value with the separate converters and then does something with the converted values.

What I need is:

  • Convert the value with the first converter (this gives convertedValue).
  • Convert convertedValue with the second converter and it's this result that I need.
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Natrium
  • 30,772
  • 17
  • 59
  • 73
  • yes, the solution you linked to is probably the best... – Thomas Levesque Oct 20 '09 at 13:39
  • See also [Chaining multiple converters in XAML](http://stackoverflow.com/q/2607490/2032064) – Mifeet Jan 28 '14 at 13:05
  • Town said it best [here](http://stackoverflow.com/a/8326207/526704), his solution lets you chain as many as you like, similar to jberger's answer, but this one is even more elegant and short in implementation – DLeh Mar 14 '14 at 17:56

12 Answers12

70

This is what I did:

public class CombiningConverter : IValueConverter
{
    public IValueConverter Converter1 { get; set; }
    public IValueConverter Converter2 { get; set; }

    public object Convert(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        object convertedValue =
            Converter1.Convert(value, targetType, parameter, culture);
        return Converter2.Convert(
            convertedValue, targetType, parameter, culture);
    }

    public object ConvertBack(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

and I call it like this:

<converters:CombiningConverter
    x:Key="negatedBoolToVisibilityConverter"
    Converter1="{StaticResource NegatedBooleanConverter}"
    Converter2="{StaticResource BoolToVisibilityConverter}" />

A MultiValueConverter might also be possible I think. Maybe I'll try that later.

Steven
  • 166,672
  • 24
  • 332
  • 435
Natrium
  • 30,772
  • 17
  • 59
  • 73
38

Expanding on Natrium's great answer...

XAML

<conv:ConverterChain x:Key="convBoolToInverseToVisibility">
    <conv:BoolToInverseConverter />
    <BooleanToVisibilityConverter />
</conv:ConverterChain>

Class

/// <summary>Represents a chain of <see cref="IValueConverter"/>s to be executed in succession.</summary>
[ContentProperty("Converters")]
[ContentWrapper(typeof(ValueConverterCollection))]
public class ConverterChain : IValueConverter
{
    private readonly ValueConverterCollection _converters= new ValueConverterCollection();

    /// <summary>Gets the converters to execute.</summary>
    public ValueConverterCollection Converters
    {
        get { return _converters; }
    }

    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Converters
            .Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Converters
            .Reverse()
            .Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
    }

    #endregion
}

/// <summary>Represents a collection of <see cref="IValueConverter"/>s.</summary>
public sealed class ValueConverterCollection : Collection<IValueConverter> { }
Jonny Piazzi
  • 3,684
  • 4
  • 34
  • 81
Jake Berger
  • 5,237
  • 1
  • 28
  • 22
  • 3
    This [article](http://www.codeproject.com/KB/WPF/PipingValueConverters_WPF.aspx)'s solution provides run-time type-checking. – Jake Berger Dec 06 '11 at 16:31
  • 3
    This passes the wrong targetType to converters. It will pass the destination targetType to all converters, which even in the opening question's scenario is incorrect. The type of "Visibility" is passed to the first converter, when the targetType is in fact "bool". – CodePB Mar 11 '16 at 15:25
  • @JakeBerger, I think you should use ConvertBack into ConverBack function ? – Eric Ouellet Nov 26 '19 at 16:38
9

In this case, you don't need a converter chain. You just need a configurable converter. This is similar to Carlo's answer above, but explicitly defines the true and false values (which means you can use the same converters for Hidden, Visible or Collapsed conversions).

[ValueConversion(typeof(bool), typeof(Visibility))]
public class BoolToVisibilityConverter : IValueConverter
{
    public Visibility TrueValue { get; set; }
    public Visibility FalseValue { get; set; }

    public BoolToVisibilityConverter()
    {
        // set defaults
        FalseValue = Visibility.Hidden;
        TrueValue = Visibility.Visible;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? TrueValue : FalseValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Then in XAML:

<BoolToVisibilityConverter x:Key="BoolToVisibleConverter"
                           FalseValue="Hidden"
                           TrueValue="Visible" />
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
metao
  • 2,784
  • 1
  • 18
  • 15
  • 1
    If you can use XAML 2009, you can also make this generic. Attribute syntax: `Converter={c:BooleanConverter(Visibility) True=Visible, False=Collapsed}` element syntax: `` If you can't use XAML 2009, you can still make the base class generic but you need concrete derived classes for each generic type. – George Helyar May 13 '16 at 10:21
4

What we do in our project is make a regular BooleanToVisibilityConverter, said converter takes one parameter (anything at all, a string, an int, bool, whatever). If the parameter is set it inverts the result, if not, it spits out the regular result.

public class BooleanToVisibilityConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        bool? isVisible = value as bool?;
        if (parameter != null && isVisible.HasValue)
            isVisible = !isVisible;
        if (isVisible.HasValue && isVisible.Value == true)
            return Visibility.Visible;
        else
            return Visibility.Collapsed;
    }

    public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new System.NotImplementedException();
    }

    #endregion
}
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Carlo
  • 25,602
  • 32
  • 128
  • 176
3

To answer my own question again: I have been using this solution for years now:

Piping Value Converters in WPF - CodeProject

It makes a new converter of 2 existing converters, calling the first one first, and then the second one etc etc.

I'm pretty pleased with this solution.

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Natrium
  • 30,772
  • 17
  • 59
  • 73
1

At this point I'd like to suggest ValueConverters.NET (NuGet) which has a ton of useful ValueConverters, including the ValueConverterGroup that can be used to combine ValueConverters.

BoolToValueConverters also offer fields to define the TrueValue, FalseValue as well as if the input IsInverted, so a ValueConverterGroup is not even necessary in most cases.

Just to illustrate how easy the life can get, here is a sample demonstration which shows a converter that displays an element if the binding is not null:

<Window ...
        xmlns:vc="clr-namespace:ValueConverters;assembly=ValueConverters"
        ...>
...
    <vc:ValueConverterGroup x:Key="IsNotNullToVisibilityConverter">
      <vc:NullToBoolConverter IsInverted="True" />
      <vc:BoolToVisibilityConverter />
    </vc:ValueConverterGroup>

ValueConverters are a prime example of reinventions of the wheel in a lot of WPF applications. Why it has to be?

Also complex things can often be solved with StyleTriggers or within the ViewModels logic itself.

It almost never happens that I need to build a custom converter. In my opinion WPF has enough engineer requirements already.

Martin Braun
  • 10,906
  • 9
  • 64
  • 105
0

I've just created what I've called ReversedBooleanToVisibilityConverter to basically do what those 2 would do for you but in one step.

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Alan Mendelevich
  • 3,591
  • 4
  • 32
  • 50
  • Yes, that would a possible solution, but I prefer a solution where I can reuse my current converters without having to reinvent the wheel. If that solution exists... – Natrium Oct 20 '09 at 14:06
0

To address this specific problem, instead of using two converters your could write your own BoolToVisibilityConverter that uses the ConverterParameter (as a bool) to determine whether or not to negate the original Boolean.

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
John Myczek
  • 12,076
  • 4
  • 31
  • 44
  • Yes, that would a possible solution, but I prefer a solution where I can reuse my current converters without having to reinvent the wheel. If that solution exists... – Natrium Oct 20 '09 at 14:07
0

Personally I would just make 1 single converter that does the full conversion. Unless you desperately need the converters (like the negation) in other places, it will be easier to maintain (imo) if the conversion is done once, in one place.

Alastair Pitts
  • 19,423
  • 9
  • 68
  • 97
  • 1
    Yes, that would a possible solution, but I prefer a solution where I can reuse my current converters without having to reinvent the wheel. If that solution exists... – Natrium Oct 20 '09 at 14:08
0

I think you may want to use a Multiconverter here instead of two separate converters. You should be able to reuse the logic from your existing converters. Check out this discussion for a start.

Scott Lawrence
  • 6,993
  • 12
  • 46
  • 64
0

Here is a combination of Natrium and metao answers to save you some time:

public class ComparisonConverter : IValueConverter
{
    public object TrueValue { get; set; } = true;
    public object FalseValue { get; set; } = false;


    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value?.Equals(parameter) == true? TrueValue : FalseValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value?.Equals(TrueValue) == true ? parameter : Binding.DoNothing;
    }
}

And how you use it:

<converter:ComparisonConverter x:Key="ComparisonConverter" />
<converter:ComparisonConverter TrueValue="{x:Static Visibility.Visible}"
                               FalseValue="{x:Static Visibility.Collapsed}"
                               x:Key="ComparisonToVisibilityConverter" />
...

<RadioButton IsChecked="{Binding Type, ConverterParameter={x:Static entities:LimitType.MinMax}, Converter={StaticResource ComparisonConverter}}"/>
<TextBox Visibility="{Binding Type, ConverterParameter={x:Static entities:LimitType.MinMax}, Converter={StaticResource ComparisonToVisibilityConverter}}"/>
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Athanviel
  • 199
  • 1
  • 3
  • 15
0

This is a pretty good use case for MathConverter.

<SomeControl Visibility="{Binding SomeBool,
    ConverterParameter=' x == false ? `Visible` : `Collapsed` ',
    Converter={StaticResource Math}}" />

-or-

<SomeControl Visibility="{math:Convert ' x == true ? `Collapsed` : `Visible` ',
    x={Binding SomeBool}}" />
Tim Cooke
  • 862
  • 7
  • 14