如何反转BooleanToVisibilityConverter?

169

在WPF中,我正在使用BooleanToVisibilityConverter将一个控件的Visibility属性绑定到一个Boolean。这个方法是可行的,但是如果这个布尔值是true,我希望其中一个控件隐藏,如果它是false,则显示。


注意:截至 beta 4 版本,Silverlight 不包括 BooleanToVisibility - 因此您仍需要自己实现它。 - Simon_Weaver
已添加用户意见建议以获取倒置支持 http://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/11083446-booleantovisibilityconverter-should-allow-inverted - Thraka
我简直不敢相信他们没有实现一些转换器参数来完成这样的事情。 - Kamil
2
我发现最简单的解决方案是在模型中创建一个属性,该属性返回原始值的负数。 public bool ShowMyControl { get { return !theValue; } }在WPF中 Visibility="{Binding MyModel.ShowMyControl, Converter={StaticResource BooleanToVisibilityConverter}}" - Zamir
18个回答

279

不必反转,您可以通过使用通用的 IValueConverter 实现来实现相同的目标,该实现可以将布尔值转换为可配置的 true 和 false 目标值。以下是一个这样的实现:

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

接下来,将其子类化,其中 TVisibility:
public sealed class BooleanToVisibilityConverter : BooleanConverter<Visibility>
{
    public BooleanToVisibilityConverter() : 
        base(Visibility.Visible, Visibility.Collapsed) {}
}

最后,以下是如何在XAML中使用BooleanToVisibilityConverter并配置它,例如将Collapsed用于true,将Visible用于false:

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

当你想绑定到一个名为IsHidden的布尔属性而不是IsVisible时,这种反转很有用。


我可能漏掉了什么,但是你只需要一个被否定的属性吗?https://dev59.com/y3RB5IYBdhLWcg3wuZbo#11800656 - OscarRyz
11
随着UI的复杂性增加,这将在视图模型中添加许多非常烦人的混乱内容,更不用说另一个你理论上必须进行单元测试以维护代码覆盖率的属性了。视图模型不应该与视图的实现细节那么接近,否则你可能就只需要在视图模型中有Visibility属性了。 - Aaronaught
这很简单,但非常有帮助。谢谢@AtifAziz。 - TheLastGIS
请注意:我在2008年发布了相同的解决方案:https://social.msdn.microsoft.com/Forums/en-US/f2154f63-ccb5-4d6d-8c01-81f9da9ab347/invert-booleantovisibilityconverter?forum=wpf - undefined

53

自己编写代码是目前最佳的解决方案。这里有一个可以实现正常和反转两种方式转换的示例。如果您在使用过程中遇到任何问题,请随时提问。

[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" 的源代码或值在哪里? - HelloWorld1
IsRunning是我的视图模型上的一个属性。这段代码的上下文很长,但简单来说,当我运行某些计算时,我需要将某些东西隐藏,而其他东西则不隐藏。我创建了这个转换器,以便我不必在我的视图模型上拥有多个属性。 - Michael Hohlios
2
你可以通过检查参数是否为空,将其作为普通 BooleanToVisibilityConverter 的替代品:Parameter direction = Parameter.Normal; if (parameter != null) direction = (Parameter)Enum.Parse(typeof(Parameter), (string)parameter); - JCH2k

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

然后将一个布尔值作为ConverterParameter传递

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

4
else if (value is bool?) 这一部分,ReSharper 告诉我“表达式总是为 false”。另外,if (flag) 部分可以更简洁地重写为 return flag ? Visibility.Visible : Visibility.Collapsed; - Danilo Bargen
1
我可能漏掉了什么,但你只需要一个否定的属性吗?https://dev59.com/y3RB5IYBdhLWcg3wuZbo#11800656 - OscarRyz
1
var nullable = (bool?)value; flag = nullable.GetValueOrDefault(); can be made much shorter and simple: flag = (bool?)value ?? false; - ANeves

22

另一种将 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>

21

20

此外,在Codeplex上还有WPF转换器项目。在他们的文档中,他们说你可以使用他们的MapConverter从Visibility枚举转换为布尔值。

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

2
WPF转换器现在包括一个可逆的BooleanToVisibilityConverter。 - vinod

19

或者真正的懒人方法,只需利用已经存在的内容并翻转它:

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

12

如果您不喜欢编写自定义转换器,您可以使用数据触发器来解决此问题:

<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

我刚刚写了一篇关于这个的文章。我使用了和Michael Hohlios类似的想法,只不过我使用了Properties而不是使用“object parameter”。

在WPF中将可见性绑定到布尔值

在我看来,使用Properties使得代码更易读。

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

只是对我自己评论的跟进。如果您使用属性,如果要创建转换器,则必须创建单独的对象,一个是反向的,一个不是。如果您使用参数,则可以为多个项目使用一个对象,但如果您不注意,可能会感到困惑。因此,两者都有优缺点。 - Rhyous
我发现这对于实现布尔值到颜色转换器非常有帮助。谢谢。 - Federinik

3

如果您不想自己编写代码/重复造轮子,可以考虑使用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在许多其他场景中也非常有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接