Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF: how to use 2 converters in 1 binding?

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.
like image 750
Natrium Avatar asked Oct 20 '09 12:10

Natrium


4 Answers

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.

like image 112
Natrium Avatar answered Nov 16 '22 21:11

Natrium


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> { }
like image 43
Jake Berger Avatar answered Nov 16 '22 19:11

Jake Berger


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
}
like image 4
Carlo Avatar answered Nov 16 '22 19:11

Carlo


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" />
like image 9
metao Avatar answered Nov 16 '22 21:11

metao