Как мне инвертировать BooleanToVisibilityConverter?

146

Я использую BooleanToVisibilityConverterв WPF для привязки Visibilityсвойства элемента управления к Boolean. Это работает нормально, но я бы хотел, чтобы один из элементов управления скрыл, если логическое значение есть true, и показал, если он false.

Рубен Бартелинк
источник
примечание: начиная с бета-версии 4 - silverlight не включает BooleanToVisibility - поэтому вам все равно придется реализовать его самостоятельно
Simon_Weaver
Добавлено голосовое предложение пользователя для поддержки инвертирования visualstudio.uservoice.com/forums/121579-visual-studio-2015/…
Thraka
Я не могу поверить, что они не реализовали некоторые параметры конвертера для таких вещей.
Камил

Ответы:

258

Вместо инвертирования вы можете достичь той же цели, используя универсальную IValueConverterреализацию, которая может преобразовывать логическое значение в настраиваемые целевые значения для истинного и ложного. Ниже представлена ​​одна из таких реализаций:

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

Затем подклассифицируйте его, где Tэто Visibility:

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

Наконец, вот как вы можете использовать BooleanToVisibilityConverterприведенное выше в XAML и настроить его, например, Collapsedдля использования для true и Visibleдля false:

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

Эта инверсия полезна, когда вы хотите выполнить привязку к логическому свойству с именем IsHiddenвместоIsVisible .

Атиф Азиз
источник
Возможно, я что-то упускаю, но разве вам не нужно просто отрицательное свойство? stackoverflow.com/questions/534575/…
OscarRyz 03
9
@OscarRyz: с более сложными пользовательскими интерфейсами, это начинает добавлять много действительно раздражающего беспорядка в модели представления, не говоря уже о другом свойстве, которое вам теоретически необходимо для модульного тестирования, чтобы поддерживать покрытие кода. Посмотреть модели не должны получить , что близко к детали реализации мнению, в противном случае вы можете также просто Visibilityсвойство в модели представления.
Aaronaught
Это так просто, но очень полезно. Спасибо @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;
    }
}

а затем передайте true или false в качестве ConverterParameter

       <Grid.Visibility>
                <Binding Path="IsYesNoButtonSetVisible" Converter="{StaticResource booleanToVisibilityConverter}" ConverterParameter="true"/>
        </Grid.Visibility>
Саймон
источник
4
В else if (value is bool?)части, ReSharper говорит мне «Expression всегда ложно». Кроме того, if (flag)часть можно более кратко переписать как return flag ? Visibility.Visible : Visibility.Collapsed;.
Данило Барген
1
Возможно, я что-то упускаю, но разве вам не нужно просто отрицательное свойство? stackoverflow.com/questions/534575/…
OscarRyz 03
1
var nullable = (bool?)value; flag = nullable.GetValueOrDefault();можно сделать намного короче и проще:flag = (bool?)value ?? false;
Анвс считает SE злом
45

Напишите свой собственный - лучшее решение на данный момент. Вот пример конвертера, который может работать как в нормальном, так и в инвертированном режиме. Если у вас есть проблемы с этим, просто спросите.

[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
Просто интересно одно. Код xaml "Binding IsRunning", где исходный код или значение для объекта "IsRunning"?
What'sUP
IsRunning - это свойство моей модели просмотра. Контекст этого кода длинный, но суть в том, что мне нужно было что-то скрывать, когда я выполнял некоторые вычисления, а другие вещи не скрывались. Я создал этот конвертер, чтобы мне не пришлось иметь несколько свойств в моей модели просмотра.
Майкл Хохлиос,
2
Вы можете сделать его заменой обычного BooleanToVisibilityConverter, проверив параметр на Parameter direction = Parameter.Normal; if (parameter != null) direction = (Parameter)Enum.Parse(typeof(Parameter), (string)parameter);
ноль
20

Также на Codeplex есть проект WPF Converters . В их документации говорится, что вы можете использовать их MapConverter для преобразования из перечисления Visibility в 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
Конвертеры WPF теперь включают BooleanToVisibilityConverter, который можно отменить.
vinod
18

Еще один способ привязать логическое значение ViewModel (IsButtonVisible) к свойству видимости элемента управления xaml. Никакого кодирования, никакой конвертации, только стили.

<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

Или по-настоящему ленивый мужчина, просто используйте то, что уже есть, и переверните его:

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;
    }
}
Росс Оливер
источник
7

Если вам не нравится писать собственный конвертер, вы можете использовать триггеры данных для решения этой проблемы:

<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

Я только что написал об этом. Я использовал ту же идею, что и Майкл Холиос. Только я использовал Свойства вместо использования «параметра объекта».

На

мой взгляд, привязка видимости к значению типа bool в WPF с помощью свойств делает его более читаемым.

<local:BoolToVisibleOrHidden x:Key="BoolToVisConverter" Collapse="True" Reverse="True" />
Rhyous
источник
Просто продолжение моего собственного комментария. Если вы используете свойства, вам нужно создать отдельный объект, если вы хотите создать преобразователи, один из которых является обратным, а другой нет. Если вы используете параметры, вы можете использовать один объект для нескольких элементов, но это может сбивать с толку, если вы не обращаете внимания. Так что у обоих есть свои плюсы и минусы.
Rhyous
Я нашел это очень полезным при реализации преобразователей логических значений в цвета. Спасибо
Федериник
3

Вот один, который я написал и много использую. Он использует логический параметр преобразователя, который указывает, нужно ли инвертировать значение, а затем использует XOR для выполнения отрицания:

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

Вот таблица истинности XOR для справки:

        XOR
        x  y  XOR
        ---------
        0  0  0
        0  1  1
        1  0  1
        1  1  0
xr280xr
источник
2

Я искал более общий ответ, но не нашел. Я написал конвертер, который может помочь другим.

Он основан на том, что нам нужно различать шесть разных случаев:

  • True 2 Visible, False 2 Hidden
  • True 2 Visible, False 2 свернут
  • True 2 Hidden, False 2 Visible
  • Истина 2 свернута, ложь 2 видна
  • Истина 2 скрыта, ложь 2 свернута
  • Истина 2 свернута, ложь 2 скрыта

Вот моя реализация для первых 4 случаев:

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

пример:

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

Думаю, параметры легко запомнить.

Надеюсь, это кому-то поможет.

Рон
источник
2

Вы можете использовать QuickConverter .

С QuickConverter вы можете написать логику конвертера, встроенную в ваше BindingExpression

Вот преобразователь инвертированного BooleanToVisibility:

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

Вы можете добавить QuickConverter через NuGet. Посмотрите документацию по настройке. Ссылка: https://quickconverter.codeplex.com/

Феликс Кейл
источник
1

Напишите свой собственный конверт.

public class ReverseBooleanToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
   {
       // your converter code here
   }
}
Муад Диб
источник
0

Простая односторонняя версия, которую можно использовать так:

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

можно реализовать так:

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

Преобразуйте все во все (bool, string, enum и т. Д.):

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

Примеры 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

Вместо того, чтобы писать свой собственный код / ​​изобретать заново, рассмотрите возможность использования 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 также весьма полезен для множества других сценариев.

UuDdLrLrSs
источник
-2

Я знаю, что это устарело, но вам не нужно ничего переделывать.

Что я сделал, так это отменил значение свойства следующим образом:

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

И работает отлично!

Я что-то упускаю?

OscarRyz
источник
7
Вы думаете, что это более простое решение, и для одного свойства это может быть даже так (оно не может быть повторно использовано для нескольких свойств, вы должны реализовать его для каждого). Я считаю, что это неподходящее место для реализации, поскольку оно не имеет ничего общего с viewmodel / codeBehind и всем остальным, что связано с представлением.
Майк Фукс,