Comment inverser BooleanToVisibilityConverter?

143

J'utilise un BooleanToVisibilityConverterdans WPF pour lier la Visibilitypropriété d'un contrôle à un Boolean. Cela fonctionne bien, mais j'aimerais que l'un des contrôles masque si le booléen est trueet montre s'il l'est false.

Ruben Bartelink
la source
Remarque: à partir de la version bêta 4 - silverlight n'inclut pas BooleanToVisibility - vous devrez donc l'implémenter vous-même de toute façon
Simon_Weaver
Ajout d'une suggestion vocale utilisateur pour obtenir la prise en charge de l'inversion visualstudio.uservoice.com/forums/121579-visual-studio-2015
...
Je ne peux pas croire qu'ils n'aient pas implémenté certains paramètres de conversion pour faire de telles choses.
Kamil

Réponses:

250

Au lieu d'inverser, vous pouvez atteindre le même objectif en utilisant une IValueConverterimplémentation générique qui peut convertir une valeur booléenne en valeurs cibles configurables pour true et false. Voici une de ces implémentations:

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

Ensuite, sous-classez-le où Test Visibility:

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

Enfin, voici comment vous pouvez utiliser BooleanToVisibilityConverterci-dessus en XAML et le configurer pour, par exemple, utiliser Collapsedpour true et Visiblepour false:

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

Cette inversion est utile lorsque vous souhaitez vous lier à une propriété booléenne nommée IsHiddenpar opposition IsVisible.

Atif Aziz
la source
Il me manque peut-être quelque chose, mais n'avez-vous pas juste besoin d'une propriété annulée? stackoverflow.com/questions/534575/…
OscarRyz
9
@OscarRyz: Avec des interfaces utilisateur plus complexes, cela commence à ajouter beaucoup de fouillis vraiment ennuyeux aux modèles de vue, sans parler d'une autre propriété que vous devez théoriquement tester unitaire afin de maintenir la couverture du code. Les modèles de vue ne devraient pas avoir à être aussi proches des détails d'implémentation de la vue, sinon vous pourriez tout aussi bien avoir des Visibilitypropriétés dans votre modèle de vue.
Aaronaught
C'est si simple, mais très utile. Merci @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;
    }
}

puis passez un vrai ou faux comme ConverterParameter

       <Grid.Visibility>
                <Binding Path="IsYesNoButtonSetVisible" Converter="{StaticResource booleanToVisibilityConverter}" ConverterParameter="true"/>
        </Grid.Visibility>
Simon
la source
4
A la else if (value is bool?)partie, ReSharper me dit "L'expression est toujours fausse". En outre, la if (flag)partie peut être réécrite de manière plus concise comme return flag ? Visibility.Visible : Visibility.Collapsed;.
Danilo Bargen
1
Il me manque peut-être quelque chose, mais n'avez-vous pas juste besoin d'une propriété annulée? stackoverflow.com/questions/534575/…
OscarRyz
1
var nullable = (bool?)value; flag = nullable.GetValueOrDefault();peut être beaucoup plus courte et simple:flag = (bool?)value ?? false;
ANeves
45

Écrire le vôtre est la meilleure solution pour le moment. Voici un exemple de convertisseur qui peut faire les deux sens normal et inversé. Si vous avez des problèmes avec cela, demandez simplement.

[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
la source
2
Je me demande juste une chose. Le code xaml "Binding IsRunning", où se trouve le code source ou la valeur de l'objet "IsRunning"?
What'sUP
IsRunning est une propriété sur mon viewmodel. Le contexte de ce code est long, mais le problème est que j'avais besoin d'avoir quelque chose de caché lorsque j'exécutais un calcul et d'autres choses non cachées. J'ai créé ce convertisseur pour le rendre ainsi je n'aurais pas à avoir plusieurs propriétés sur mon viewmodel.
Michael Hohlios
2
Vous pouvez en faire un remplacement BooleanToVisibilityConverterParameter direction = Parameter.Normal; if (parameter != null) direction = (Parameter)Enum.Parse(typeof(Parameter), (string)parameter);
instantané
20

Il y a aussi le projet WPF Converters sur Codeplex. Dans leur documentation, ils disent que vous pouvez utiliser leur MapConverter pour convertir de l'énumération de visibilité en booléen

<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
la source
1
Les convertisseurs WPF incluent désormais un BooleanToVisibilityConverter qui peut être inversé.
vinod
17

Une autre façon de lier la valeur booléenne ViewModel (IsButtonVisible) avec la propriété de visibilité de contrôle xaml. Pas de codage, pas de conversion, juste du style.

<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
la source
15

Ou à la manière des vrais paresseux, utilisez simplement ce qui existe déjà et retournez-le:

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
la source
5

Si vous n'aimez pas écrire un convertisseur personnalisé, vous pouvez utiliser des déclencheurs de données pour résoudre ce problème:

<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
la source
3

Je viens de publier un article à ce sujet. J'ai utilisé une idée similaire à celle de Michael Hohlios. Seulement, j'ai utilisé Propriétés au lieu d'utiliser le "paramètre d'objet".

Lier la visibilité à une valeur booléenne dans WPF L'

utilisation de propriétés le rend plus lisible, à mon avis.

<local:BoolToVisibleOrHidden x:Key="BoolToVisConverter" Collapse="True" Reverse="True" />
Rhyous
la source
Juste un suivi de mon propre commentaire. Si vous utilisez Propriétés, vous devez créer un objet distinct si vous souhaitez créer des convertisseurs, un objet inversé et l'autre non. Si vous utilisez des paramètres, vous pouvez utiliser un objet pour plusieurs éléments, mais cela peut être déroutant si vous ne faites pas attention. Il y a donc des avantages et des inconvénients aux deux.
Rhyous
J'ai trouvé cela très utile pour réaliser des convertisseurs booléens en couleurs. Merci
Federinik
3

En voici un que j'ai écrit et que j'utilise beaucoup. Il utilise un paramètre de convertisseur booléen qui indique s'il faut ou non inverser la valeur, puis utilise XOR pour effectuer la négation:

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

Voici une table de vérité XOR pour référence:

        XOR
        x  y  XOR
        ---------
        0  0  0
        0  1  1
        1  0  1
        1  1  0
xr280xr
la source
2

Je cherchais une réponse plus générale, mais je ne l'ai pas trouvée. J'ai écrit un convertisseur qui pourrait aider les autres.

Il est basé sur le fait que nous devons distinguer six cas différents:

  • Vrai 2 visible, faux 2 masqué
  • Vrai 2 Visible, Faux 2 Réduit
  • Vrai 2 Caché, Faux 2 Visible
  • Vrai 2 Réduit, Faux 2 Visible
  • Vrai 2 Caché, Faux 2 Réduit
  • Vrai 2 effondré, Faux 2 masqué

Voici ma mise en œuvre pour les 4 premiers cas:

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

exemple:

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

Je pense que les paramètres sont faciles à retenir.

J'espère que ça aide quelqu'un.

Ron
la source
2

Vous pouvez utiliser QuickConverter .

Avec QuickConverter, vous pouvez écrire la logique du convertisseur en ligne avec votre BindingExpression

Voici un convertisseur BooleanToVisibility inversé:

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

Vous pouvez ajouter QuickConverter via NuGet. Jetez un œil à la documentation pour l'installation. Lien: https://quickconverter.codeplex.com/

Felix Keil
la source
1

Écrivez votre propre converti.

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

Une version simple à sens unique qui peut être utilisée comme ceci:

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

peut être implémenté comme ceci:

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
la source
0

Convertissez tout en tout (booléen, chaîne, énumération, 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;
    }
}

Exemples 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
la source
0

Plutôt que d'écrire votre propre code / de réinventer, pensez à utiliser 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 est également très utile pour de nombreux autres scénarios.

UuDdLrLrSs
la source
-2

Je sais que c'est daté, mais vous n'avez rien à réimplémenter.

Ce que j'ai fait, c'est d'annuler la valeur de la propriété comme ceci:

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

Et cela fonctionne très bien!

Est-ce que je manque quelque chose?

OscarRyz
la source
7
Vous pensez que c'est une solution plus simple, et pour une seule propriété, cela pourrait même être le cas (ce n'est pas réutilisable pour plusieurs propriétés, vous devez l'implémenter pour chacune). Je pense que ce n'est pas le bon endroit pour l'implémentation, car cela n'a rien à voir avec le viewmodel / codeBehind et tout avec la vue.
Mike Fuchs