¿Cómo invierto BooleanToVisibilityConverter?

143

Estoy usando a BooleanToVisibilityConverteren WPF para vincular la Visibilitypropiedad de un control a a Boolean. Esto funciona bien, pero me gustaría que uno de los controles se oculte si el booleano es truey muestre si es así false.

Ruben Bartelink
fuente
nota: a partir de la versión beta 4: silverlight no incluye BooleanToVisibility, por lo que deberá implementarlo usted mismo de todos modos
Simon_Weaver
Se ha añadido una sugerencia de voz del usuario para obtener el apoyo invertido visualstudio.uservoice.com/forums/121579-visual-studio-2015/...
Thraka
No puedo creer que no hayan implementado algunos parámetros de conversión para hacer tales cosas.
Kamil

Respuestas:

250

En lugar de invertir, puede lograr el mismo objetivo utilizando una IValueConverterimplementación genérica que puede convertir un valor booleano en valores objetivo configurables para verdadero y falso. A continuación se muestra una de esas implementaciones:

public class BooleanConverter<T> : IValueConverter
{
    public BooleanConverter(T trueValue, T falseValue)
    {
        True = trueValue;
        False = falseValue;
    }

    public T True { get; set; }
    public T False { get; set; }

    public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value is bool && ((bool) value) ? True : False;
    }

    public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value is T && EqualityComparer<T>.Default.Equals((T) value, True);
    }
}

Luego, subclase donde Testá Visibility:

public sealed class BooleanToVisibilityConverter : BooleanConverter<Visibility>
{
    public BooleanToVisibilityConverter() : 
        base(Visibility.Visible, Visibility.Collapsed) {}
}

Finalmente, así es como podría usar BooleanToVisibilityConverterarriba en XAML y configurarlo para, por ejemplo, usar Collapsedpara verdadero y Visiblepara falso:

<Application.Resources>
    <app:BooleanToVisibilityConverter 
        x:Key="BooleanToVisibilityConverter" 
        True="Collapsed" 
        False="Visible" />
</Application.Resources>

Esta inversión es útil cuando desea enlazar a una propiedad booleana denominada IsHiddenopuesta IsVisible.

Atif Aziz
fuente
Puede que me falte algo, pero ¿no solo necesitas una propiedad negada? stackoverflow.com/questions/534575/…
OscarRyz
9
@OscarRyz: con interfaces de usuario más complejas, eso comienza a agregar una gran cantidad de desorden realmente molesto a los modelos de vista, sin mencionar otra propiedad que teóricamente tienes que probar en la unidad para mantener la cobertura del código. Los modelos de vista no deberían tener que acercarse tanto a los detalles de implementación de la vista, de lo contrario, también podría tener Visibilitypropiedades en su modelo de vista.
Aaronaught
Esto es muy simple, pero significativamente útil. Gracias @AtifAziz.
TheLastGIS
48
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

public sealed class BooleanToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var flag = false;
        if (value is bool)
        {
            flag = (bool)value;
        }
        else if (value is bool?)
        {
            var nullable = (bool?)value;
            flag = nullable.GetValueOrDefault();
        }
        if (parameter != null)
        {
            if (bool.Parse((string)parameter))
            {
                flag = !flag;
            }
        }
        if (flag)
        {
            return Visibility.Visible;
        }
        else
        {
            return Visibility.Collapsed;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var back = ((value is Visibility) && (((Visibility)value) == Visibility.Visible));
        if (parameter != null)
        {
            if ((bool)parameter)
            {
                back = !back;
            }
        }
        return back;
    }
}

y luego pasar un verdadero o falso como ConverterParameter

       <Grid.Visibility>
                <Binding Path="IsYesNoButtonSetVisible" Converter="{StaticResource booleanToVisibilityConverter}" ConverterParameter="true"/>
        </Grid.Visibility>
Simón
fuente
44
En la else if (value is bool?)parte, ReSharper me dice "La expresión siempre es falsa". Además, la if (flag)parte se puede reescribir de manera más concisa como return flag ? Visibility.Visible : Visibility.Collapsed;.
Danilo Bargen
1
Puede que me falte algo, pero ¿no solo necesitas una propiedad negada? stackoverflow.com/questions/534575/…
OscarRyz
1
var nullable = (bool?)value; flag = nullable.GetValueOrDefault();puede hacerse mucho más corto y simple:flag = (bool?)value ?? false;
ANeves
45

Escribe la tuya es la mejor solución por ahora. Aquí hay un ejemplo de un convertidor que puede hacer ambas cosas: normal e invertido. Si tiene algún problema con esto, solo pregunte.

[ValueConversion(typeof(bool), typeof(Visibility))]
public class InvertableBooleanToVisibilityConverter : IValueConverter
{
    enum Parameters
    {
        Normal, Inverted
    }

    public object Convert(object value, Type targetType,
                          object parameter, CultureInfo culture)
    {
        var boolValue = (bool)value;
        var direction = (Parameters)Enum.Parse(typeof(Parameters), (string)parameter);

        if(direction == Parameters.Inverted)
            return !boolValue? Visibility.Visible : Visibility.Collapsed;

        return boolValue? Visibility.Visible : Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        return null;
    }
}
<UserControl.Resources>
  <Converters:InvertableBooleanToVisibilityConverter x:Key="_Converter"/>
</UserControl.Resources>

<Button Visibility="{Binding IsRunning, Converter={StaticResource _Converter}, ConverterParameter=Inverted}">Start</Button>
Michael Hohlios
fuente
2
Solo una cosa maravillosa. El código xaml "Binding IsRunning", ¿dónde está el código ácido o el valor para el objeto "IsRunning"?
What'sUP
IsRunning es una propiedad en mi modelo de vista. El contexto de este código es largo, pero en resumen, necesitaba tener algunas cosas ocultas cuando ejecutaba algunos cálculos y otras cosas que no estaban ocultas. Creé este convertidor para hacerlo así no tendría que tener múltiples propiedades en mi modelo de vista.
Michael Hohlios
2
Puede convertirlo en un reemplazo BooleanToVisibilityConverterParameter direction = Parameter.Normal; if (parameter != null) direction = (Parameter)Enum.Parse(typeof(Parameter), (string)parameter);
directo
20

También está el proyecto WPF Converters en Codeplex. En su documentación, dicen que puede usar su MapConverter para convertir de la enumeración de Visibilidad a bool

<Label>
    <Label.Visible>
        <Binding Path="IsVisible">
            <Binding.Converter>
                <con:MapConverter>
                    <con:Mapping From="True" To="{x:Static Visibility.Visible}"/>
                    <con:Mapping From="False" To="{x:Static Visibility.Hidden}"/>
                </con:MapConverter>
            </Binding.Converter>
        </Binding>
    </Label.Visible>
</Label>
Cameron MacFarland
fuente
1
WPF Converters ahora incluye un BooleanToVisibilityConverter que se puede revertir.
Vinod
17

Una forma más de vincular el valor booleano de ViewModel (IsButtonVisible) con la propiedad de visibilidad de control xaml. Sin codificación, sin conversión, solo estilo.

<Style TargetType={x:Type Button} x:Key="HideShow">
   <Style.Triggers>
      <DataTrigger Binding="{Binding IsButtonVisible}" Value="False">
          <Setter Property="Visibility" Value="Hidden"/>
      </DataTrigger>
   </Style.Triggers>
</Style>

<Button Style="{StaticResource HideShow}">Hello</Button>
Marat Batalandabad
fuente
15

O la verdadera manera del hombre perezoso, solo usa lo que ya está allí y dale la vuelta:

public class InverseBooleanToVisibilityConverter : IValueConverter
{
    private BooleanToVisibilityConverter _converter = new BooleanToVisibilityConverter();

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var result = _converter.Convert(value, targetType, parameter, culture) as Visibility?;
        return result == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var result = _converter.ConvertBack(value, targetType, parameter, culture) as bool?;
        return result == true ? false : true;
    }
}
Ross Oliver
fuente
5

Si no le gusta escribir un convertidor personalizado, puede usar disparadores de datos para resolver esto:

<Style.Triggers>
        <DataTrigger Binding="{Binding YourBinaryOption}" Value="True">
                 <Setter Property="Visibility" Value="Visible" />
        </DataTrigger>
        <DataTrigger Binding="{Binding YourBinaryOption}" Value="False">
                 <Setter Property="Visibility" Value="Collapsed" />
        </DataTrigger>
</Style.Triggers>
Haim Bendanan
fuente
3

Acabo de hacer una publicación sobre esto. Usé una idea similar a la de Michael Hohlios. Solo que usé Propiedades en lugar de usar el "parámetro objeto".

Visibilidad vinculante a un valor bool en WPF

Usar propiedades lo hace más legible, en mi opinión.

<local:BoolToVisibleOrHidden x:Key="BoolToVisConverter" Collapse="True" Reverse="True" />
Rhyous
fuente
Solo un seguimiento de mi propio comentario. Si usa Propiedades, debe crear un objeto separado si desea crear en convertidores, uno que sea inverso y otro no. Si usa parámetros, puede usar un objeto para varios elementos, pero puede ser confuso si no presta atención. Entonces hay pros y contras para ambos.
Rhyous
Encontré esto muy útil para realizar convertidores booleanos a colores. Gracias
Federinik
3

Aquí hay uno que escribí y uso mucho. Utiliza un parámetro de convertidor booleano que indica si invertir o no el valor y luego usa XOR para realizar la negación:

[ValueConversion(typeof(bool), typeof(System.Windows.Visibility))]
public class BooleanVisibilityConverter : IValueConverter
{
    System.Windows.Visibility _visibilityWhenFalse = System.Windows.Visibility.Collapsed;

    /// <summary>
    /// Gets or sets the <see cref="System.Windows.Visibility"/> value to use when the value is false. Defaults to collapsed.
    /// </summary>
    public System.Windows.Visibility VisibilityWhenFalse
    {
        get { return _visibilityWhenFalse; }
        set { _visibilityWhenFalse = value; }
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool negateValue;
        Boolean.TryParse(parameter as string, out negateValue);

        bool val = negateValue ^ System.Convert.ToBoolean(value); //Negate the value when negateValue is true using XOR
        return val ? System.Windows.Visibility.Visible : _visibilityWhenFalse;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool negateValue;
        Boolean.TryParse(parameter as string, out negateValue);

        if ((System.Windows.Visibility)value == System.Windows.Visibility.Visible)
            return true ^ negateValue;
        else
            return false ^ negateValue;
    }
}

Aquí hay una tabla de verdad XOR para referencia:

        XOR
        x  y  XOR
        ---------
        0  0  0
        0  1  1
        1  0  1
        1  1  0
xr280xr
fuente
2

Estaba buscando una respuesta más general, pero no pude encontrarla. Escribí un convertidor que podría ayudar a otros.

Se basa en el hecho de que necesitamos distinguir seis casos diferentes:

  • Verdadero 2 Visible, Falso 2 Oculto
  • Verdadero 2 Visible, Falso 2 Contraído
  • Verdadero 2 Oculto, Falso 2 Visible
  • Verdadero 2 Colapsado, Falso 2 Visible
  • Verdadero 2 Oculto, Falso 2 Contraído
  • Verdadero 2 Colapsado, Falso 2 Oculto

Aquí está mi implementación para los primeros 4 casos:

[ValueConversion(typeof(bool), typeof(Visibility))]
public class BooleanToVisibilityConverter : IValueConverter
{
    enum Types
    {
        /// <summary>
        /// True to Visible, False to Collapsed
        /// </summary>
        t2v_f2c,
        /// <summary>
        /// True to Visible, False to Hidden
        /// </summary>
        t2v_f2h,
        /// <summary>
        /// True to Collapsed, False to Visible
        /// </summary>
        t2c_f2v,
        /// <summary>
        /// True to Hidden, False to Visible
        /// </summary>
        t2h_f2v,
    }
    public object Convert(object value, Type targetType,
                          object parameter, CultureInfo culture)
    {
        var b = (bool)value;
        string p = (string)parameter;
        var type = (Types)Enum.Parse(typeof(Types), (string)parameter);
        switch (type)
        {
            case Types.t2v_f2c:
                return b ? Visibility.Visible : Visibility.Collapsed; 
            case Types.t2v_f2h:
                return b ? Visibility.Visible : Visibility.Hidden; 
            case Types.t2c_f2v:
                return b ? Visibility.Collapsed : Visibility.Visible; 
            case Types.t2h_f2v:
                return b ? Visibility.Hidden : Visibility.Visible; 
        }
        throw new NotImplementedException();
    }

    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        var v = (Visibility)value;
        string p = (string)parameter;
        var type = (Types)Enum.Parse(typeof(Types), (string)parameter);
        switch (type)
        {
            case Types.t2v_f2c:
                if (v == Visibility.Visible)
                    return true;
                else if (v == Visibility.Collapsed)
                    return false;
                break;
            case Types.t2v_f2h:
                if (v == Visibility.Visible)
                    return true;
                else if (v == Visibility.Hidden)
                    return false;
                break;
            case Types.t2c_f2v:
                if (v == Visibility.Visible)
                    return false;
                else if (v == Visibility.Collapsed)
                    return true;
                break;
            case Types.t2h_f2v:
                if (v == Visibility.Visible)
                    return false;
                else if (v == Visibility.Hidden)
                    return true;
                break;
        }
        throw new InvalidOperationException();
    }
}

ejemplo:

Visibility="{Binding HasItems, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter='t2v_f2c'}"

Creo que los parámetros son fáciles de recordar.

Espero que ayude a alguien.

Ron
fuente
2

Puedes usar QuickConverter .

Con QuickConverter puede escribir la lógica del convertidor en línea con su BindingExpression

Aquí hay un convertidor BooleanToVisibility invertido:

Visibility="{qc:Binding '!$P ? Visibility.Visible : Visibility.Collapsed', P={Binding Example}}"

Puede agregar QuickConverter a través de NuGet. Echa un vistazo a la documentación para la configuración. Enlace: https://quickconverter.codeplex.com/

Felix Keil
fuente
1

Escribe tu propio converso.

public class ReverseBooleanToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
   {
       // your converter code here
   }
}
Muad'Dib
fuente
0

Una versión simple de una vía que se puede usar así:

Visibility="{Binding IsHidden, Converter={x:Static Ui:Converters.BooleanToVisibility}, ConverterParameter=true}

puede implementarse así:

public class BooleanToVisibilityConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    var invert = false;

    if (parameter != null)
    {
      invert = Boolean.Parse(parameter.ToString());
    }

    var booleanValue = (bool) value;

    return ((booleanValue && !invert) || (!booleanValue && invert)) 
      ? Visibility.Visible : Visibility.Collapsed;
  }

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

Convierta todo a todo (bool, string, enum, etc.):

public class EverythingConverterValue
{
    public object ConditionValue { get; set; }
    public object ResultValue { get; set; }
}

public class EverythingConverterList : List<EverythingConverterValue>
{

}

public class EverythingConverter : IValueConverter
{
    public EverythingConverterList Conditions { get; set; } = new EverythingConverterList();

    public object NullResultValue { get; set; }
    public object NullBackValue { get; set; }

    public object Convert(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        return Conditions.Where(x => x.ConditionValue.Equals(value)).Select(x => x.ResultValue).FirstOrDefault() ?? NullResultValue;
    }
    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        return Conditions.Where(x => x.ResultValue.Equals(value)).Select(x => x.ConditionValue).FirstOrDefault() ?? NullBackValue;
    }
}

Ejemplos de XAML:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:conv="clr-namespace:MvvmGo.Converters;assembly=MvvmGo.WindowsWPF"
                xmlns:sys="clr-namespace:System;assembly=mscorlib">

<conv:EverythingConverter x:Key="BooleanToVisibilityConverter">
    <conv:EverythingConverter.Conditions>
        <conv:EverythingConverterValue ResultValue="{x:Static Visibility.Visible}">
            <conv:EverythingConverterValue.ConditionValue>
                <sys:Boolean>True</sys:Boolean>
            </conv:EverythingConverterValue.ConditionValue>
        </conv:EverythingConverterValue>
        <conv:EverythingConverterValue ResultValue="{x:Static Visibility.Collapsed}">
            <conv:EverythingConverterValue.ConditionValue>
                <sys:Boolean>False</sys:Boolean>
            </conv:EverythingConverterValue.ConditionValue>
        </conv:EverythingConverterValue>
    </conv:EverythingConverter.Conditions>

</conv:EverythingConverter>

<conv:EverythingConverter x:Key="InvertBooleanToVisibilityConverter">
    <conv:EverythingConverter.Conditions>
        <conv:EverythingConverterValue ResultValue="{x:Static Visibility.Visible}">
            <conv:EverythingConverterValue.ConditionValue>
                <sys:Boolean>False</sys:Boolean>
            </conv:EverythingConverterValue.ConditionValue>
        </conv:EverythingConverterValue>
        <conv:EverythingConverterValue ResultValue="{x:Static Visibility.Collapsed}">
            <conv:EverythingConverterValue.ConditionValue>
                <sys:Boolean>True</sys:Boolean>
            </conv:EverythingConverterValue.ConditionValue>
        </conv:EverythingConverterValue>
    </conv:EverythingConverter.Conditions>
</conv:EverythingConverter>

<conv:EverythingConverter x:Key="MarriedConverter" NullResultValue="Single">
    <conv:EverythingConverter.Conditions>
        <conv:EverythingConverterValue ResultValue="Married">
            <conv:EverythingConverterValue.ConditionValue>
                <sys:Boolean>True</sys:Boolean>
            </conv:EverythingConverterValue.ConditionValue>
        </conv:EverythingConverterValue>
        <conv:EverythingConverterValue ResultValue="Single">
            <conv:EverythingConverterValue.ConditionValue>
                <sys:Boolean>False</sys:Boolean>
            </conv:EverythingConverterValue.ConditionValue>
        </conv:EverythingConverterValue>
    </conv:EverythingConverter.Conditions>
    <conv:EverythingConverter.NullBackValue>
        <sys:Boolean>False</sys:Boolean>
    </conv:EverythingConverter.NullBackValue>
</conv:EverythingConverter>

Ali Yousefi
fuente
0

En lugar de escribir su propio código / reinventar, considere usar CalcBinding :

Automatic two way convertion of bool expression to Visibility and back if target property has such type: description

    <Button Visibility="{c:Binding !IsChecked}" /> 
    <Button Visibility="{c:Binding IsChecked, FalseToVisibility=Hidden}" />

CalcBinding también es bastante útil para muchos otros escenarios.

UuDdLrLrSs
fuente
-2

Sé que esto está anticuado, pero no es necesario volver a implementar nada.

Lo que hice fue negar el valor de la propiedad de esta manera:

<!-- XAML code -->
<StackPanel Name="x"  Visibility="{Binding    Path=Specials, ElementName=MyWindow, Converter={StaticResource BooleanToVisibilityConverter}}"></StackPanel>    
<StackPanel Name="y"  Visibility="{Binding Path=NotSpecials, ElementName=MyWindow, Converter={StaticResource BooleanToVisibilityConverter}}"></StackPanel>        

....

//Code behind
public bool Specials
{
    get { return (bool) GetValue(SpecialsProperty); }
    set
    {
        NotSpecials= !value; 
        SetValue(SpecialsProperty, value);
    }
}

public bool NotSpecials
{
    get { return (bool) GetValue(NotSpecialsProperty); }
    set { SetValue(NotSpecialsProperty, value); }
}

¡Y funciona bien!

¿Me estoy perdiendo de algo?

OscarRyz
fuente
77
Cree que esta es una solución más fácil, y para una sola propiedad, este podría incluso ser el caso (no es reutilizable para múltiples propiedades, debe implementarla para cada una). Sin embargo, creo que este es el lugar incorrecto para la implementación, ya que no tiene nada que ver con viewmodel / codeBehind y todo con la vista.
Mike Fuchs