Como faço para inverter o BooleanToVisibilityConverter?


143

Estou usando um BooleanToVisibilityConverterno WPF para vincular a Visibilitypropriedade de um controle a um Boolean. Isso funciona bem, mas eu gostaria que um dos controles oculte se o booleano for truee mostre se é false.


note: a partir do beta 4 - o silverlight não inclui BooleanToVisibility - então você precisará implementá-lo de qualquer maneira #
Simon_Weaver


Não acredito que eles não implementaram alguns parâmetros do conversor para fazer essas coisas.
Kamil

Respostas:



250

Em vez de inverter, você pode atingir o mesmo objetivo usando uma IValueConverterimplementação genérica que pode converter um valor booleano em valores de destino configuráveis para true e false. Abaixo está uma dessas implementações:

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);
    }
}

Em seguida, subclasse-o onde Testá Visibility:

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

Por fim, é assim que você pode usar BooleanToVisibilityConverteracima no XAML e configurá-lo para, por exemplo, usar Collapsedpara verdadeiro e Visiblefalso:

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

Essa inversão é útil quando você deseja vincular a uma propriedade booleana chamada IsHiddenoposta IsVisible.


Talvez esteja faltando alguma coisa, mas você não precisa apenas de uma propriedade negada? stackoverflow.com/questions/534575/…
OscarRyz

9
@OscarRyz: com interfaces de usuário mais complexas, isso começa a adicionar muita confusão aos modelos de visualização, sem mencionar outra propriedade que você teoricamente precisa fazer para testar a unidade, a fim de manter a cobertura do código. Os modelos de visualização não precisam chegar tão perto dos detalhes de implementação da visualização, caso contrário, você também pode ter Visibilitypropriedades no seu modelo de visualização.
Aaronaught

Isso é tão simples, mas significativamente útil. Obrigado @AtifAziz.
TheLastGIS 5/03

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;
    }
}

e, em seguida, passe um verdadeiro ou falso como o ConverterParameter

       <Grid.Visibility>
                <Binding Path="IsYesNoButtonSetVisible" Converter="{StaticResource booleanToVisibilityConverter}" ConverterParameter="true"/>
        </Grid.Visibility>

4
Na else if (value is bool?)parte, ReSharper me diz "Expressão é sempre falsa". Além disso, a if (flag)peça pode ser reescrita de forma mais concisa como return flag ? Visibility.Visible : Visibility.Collapsed;.
Danilo Bargen

1
Talvez esteja faltando alguma coisa, mas você não precisa apenas de uma propriedade negada? stackoverflow.com/questions/534575/…
OscarRyz

1
var nullable = (bool?)value; flag = nullable.GetValueOrDefault();pode ser feito muito mais curto e simples:flag = (bool?)value ?? false;
ANeves

45

Escreva o seu próprio é a melhor solução por enquanto. Aqui está um exemplo de um conversor que pode funcionar tanto de maneira normal quanto invertida. Se você tiver algum problema com isso, basta perguntar.

[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>

2
Apenas imaginando uma coisa. O código xaml "Binding IsRunning", onde está o código-fonte ou o valor do objeto "IsRunning"?
#

IsRunning é uma propriedade no meu viewmodel. O contexto desse código é longo, mas o mais curto é que eu precisava ocultar algumas coisas quando estava executando algum cálculo e outras coisas não ocultas. Eu criei este conversor para torná-lo para que eu não precise ter várias propriedades no meu modelo de exibição.
Michael Hohlios

2
Você pode substituí-lo pelo normal BooleanToVisibilityConverter, verificando o parâmetro como null:Parameter direction = Parameter.Normal; if (parameter != null) direction = (Parameter)Enum.Parse(typeof(Parameter), (string)parameter);
JCH2k

20

Há também o projeto WPF Converters no Codeplex. Na documentação deles, eles dizem que você pode usar o MapConverter para converter da enumeração Visibility para 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>

1
Os conversores WPF agora incluem um BooleanToVisibilityConverter que pode ser revertido.
Vinod #

17

Mais uma maneira de vincular o valor booleano do ViewModel (IsButtonVisible) com a propriedade xaml Control Visibility. Sem codificação, sem conversão, apenas com 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>

15

Ou o jeito preguiçoso, apenas use o que já existe e vire-o:

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;
    }
}

5

Se você não gosta de escrever um conversor personalizado, pode usar gatilhos de dados para resolver isso:

<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>

3

Acabei de fazer um post sobre isso. Eu usei uma idéia semelhante à de Michael Hohlios. Somente usei Propriedades em vez de usar o "parâmetro de objeto".

Vincular a visibilidade a um valor bool no WPF

Using Properties torna mais legível, na minha opinião.

<local:BoolToVisibleOrHidden x:Key="BoolToVisConverter" Collapse="True" Reverse="True" />

Apenas um acompanhamento do meu próprio comentário. Se você usar Propriedades, precisará criar um objeto separado, se desejar criar para conversores, um que seja Reverso e outro que não. Se você usar parâmetros, poderá usar um objeto para vários itens, mas pode ser confuso se você não prestar atenção. Portanto, existem prós e contras para ambos.
Rhyous

Achei isso muito útil na realização de conversores booleanos em cores. Obrigado
Federinik 07/04/2015

3

Aqui está um que eu escrevi e uso muito. Ele usa um parâmetro conversor booleano que indica se deve ou não inverter o valor e, em seguida, usa XOR para executar a negação:

[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;
    }
}

Aqui está uma tabela de verdade XOR para referência:

        XOR
        x  y  XOR
        ---------
        0  0  0
        0  1  1
        1  0  1
        1  1  0

2

Eu estava procurando uma resposta mais geral, mas não consegui encontrá-la. Eu escrevi um conversor que pode ajudar outras pessoas.

É baseado no fato de que precisamos distinguir seis casos diferentes:

  • Verdadeiro 2 visível, falso 2 oculto
  • Verdadeiro 2 visível, Falso 2 recolhido
  • Verdadeiro 2 oculto, falso 2 visível
  • Verdadeiro 2 recolhido, Falso 2 visível
  • Verdadeiro 2 oculto, falso 2 recolhido
  • Verdadeiro 2 recolhido, falso 2 oculto

Aqui está minha implementação para os 4 primeiros 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();
    }
}

exemplo:

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

Eu acho que os parâmetros são fáceis de lembrar.

Espero que ajude alguém.


2

Você pode usar o QuickConverter .

Com o QuickConverter, você pode escrever a lógica do conversor em linha com a sua BindingExpression

Aqui está um conversor BooleanToVisibility invertido:

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

Você pode adicionar o QuickConverter via NuGet. Dê uma olhada na documentação para instalação. Link: https://quickconverter.codeplex.com/


1

Escreva seu próprio convertido.

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

0

Uma versão simples de mão única que pode ser usada assim:

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

pode ser implementado assim:

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();
  }
}

0

Converta tudo em tudo (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;
    }
}

Exemplos 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>


0

Em vez de escrever seu próprio código / reinventar, considere usar o 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}" />

O CalcBinding também é bastante útil para vários outros cenários.


-2

Eu sei que isso é datado, mas você não precisa reimplementar nada.

O que fiz foi negar o valor da propriedade assim:

<!-- 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); }
}

E funciona muito bem!

Estou esquecendo de algo?


7
Você acha que essa é uma solução mais fácil e, para uma única propriedade, esse pode até ser o caso (não é reutilizável para várias propriedades, você deve implementá-la para todas). Eu sinto que este é o lugar errado para a implementação, pois não tem nada a ver com o viewmodel / codeBehind e tudo com a view.
Mike Fuchs
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.