WPF的BooleanToVisibilityConverter在为false时将其转换为Hidden而不是Collapsed的方法是什么?

53

是否有一种方法可以使用现有的WPF BooleanToVisibilityConverter转换器,但将False值转换为Hidden,而不是默认的Collapsed,还是我应该自己编写?我正在一个项目上工作,在这个项目中,做这样简单的事情会产生很大的开销(共享的东西放在一个单独的解决方案中,重建/检入/合并过程是一个过度膨胀的巨兽级过程),因此我更喜欢如果我可以只通过传递参数来使用现有的转换器,而不必跳过刚才提到的障碍。


检查我的答案。一旦将其添加到您的代码库中,您就可以在不需要任何代码更改的情况下以各种配置重复使用它。我理解您的痛苦,这是我找到的最佳解决方案。祝你好运。 - Drew Noakes
这个回答解决了你的问题吗?如何反转BooleanToVisibilityConverter? - StayOnTarget
7个回答

135

我发现最简单和最好的解决方案是这样的:

[ValueConversion(typeof(bool), typeof(Visibility))]
public sealed class BoolToVisibilityConverter : IValueConverter
{
  public Visibility TrueValue { get; set; }
  public Visibility FalseValue { get; set; }

  public BoolToVisibilityConverter()
  {
    // set defaults
    TrueValue = Visibility.Visible;
    FalseValue = Visibility.Collapsed;
  }

  public object Convert(object value, Type targetType, 
      object parameter, CultureInfo culture)
  {
    if (!(value is bool))
      return null;
    return (bool)value ? TrueValue : FalseValue;    
  }

  public object ConvertBack(object value, Type targetType, 
      object parameter, CultureInfo culture)
  {
    if (Equals(value, TrueValue))
      return true;
    if (Equals(value, FalseValue))
      return false;
    return null;
  }
}

在使用它时,只需像这样在XAML中配置一个版本,使其完全符合您的需求:

<Blah.Resources>
  <local:BoolToVisibilityConverter
         x:Key="BoolToHiddenConverter"
         TrueValue="Visible" FalseValue="Hidden" />
</Blah.Resources>
然后在一个或多个绑定中像这样使用它:
<Foo Visibility="{Binding IsItFridayAlready, 
                          Converter={StaticResource BoolToHiddenConverter}}" />

这个简单的解决方案解决了隐藏/折叠的首选项以及反转/否定效果。

SILVERLIGHT用户必须删除[ValueConversion]声明,因为该属性不是Silverlight框架的一部分。在WPF中也不是严格必需的,但与内置转换器保持一致。


当你在XAML中有一个绑定代码"Binding IsItFridayAlready"时,这是“IsItFridayAlready”的源代码。 - HelloWorld1
@FullmetalBoy,啊,好的,我明白你的意思了。IsItFridayAlready是你通过本地设置为DataContext的任何内容绑定到的某个属性。在绑定表达式中,它代表要绑定的路径。 - Drew Noakes
你可以基于这个,创建一个更通用的 BoolToWhateverConverter。 - szx
1
@szx,确实。如果您只想针对可见性进行目标设置,那么将其限制为“Visibility”会使从XAML使用它变得更简单,因为您不必完全限定枚举。 - Drew Noakes
这样怎么样?#if !SILVERLIGHT [ValueConversion(typeof(bool), typeof(Visibility))] #endif - Kamil
显示剩余4条评论

32

很遗憾,它只能转换为Visible或Collapsed,因此您需要编写自己的方法。以下是根据Reflector提供的Convert方法:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    bool flag = false;
    if (value is bool)
    {
        flag = (bool)value;
    }
    else if (value is bool?)
    {
        bool? nullable = (bool?)value;
        flag = nullable.HasValue ? nullable.Value : false;
    }
    return (flag ? Visibility.Visible : Visibility.Collapsed);
}

我发现(value is bool?)的检查是不必要的。 如果一个可空布尔值作为参数传递,它会自动转换为普通布尔值,如果该值被设置,则第一个检查将成功。 如果该值未被设置(即为null),则它是一个对象,两个检查都将失败。 - memory of a dream
1
你确定这是原始代码吗?因为按照它的写法,它转换成的是隐藏(Hidden),而不是折叠(Collapsed)…… - motoDrizzt
@motoDrizzt RJFalconer的编辑更改了示例,使用了hidden而不是collapsed,使得原始前言不正确。 - Adam
@Adam:现在我变得更加困惑了。BooleanToVisibilityConverter转换为Collapsed,所以我希望反射器中的代码显示它。上面的答案说“这是根据反射器的转换方法”,但仍然没有使用Collapsed而是用了Hidden。你是不是意思是RJFalconer基本上编辑了错误的答案?因为如果是这样,我会把Collapsed改回来。 - motoDrizzt
1
@motoDrizzt 是的,我相信RJFalconer错误地将示例代码更改为“Hidden”。我建议进行编辑,以便答案再次显示“Collapsed”(这是Reflector显示的行为,也是答案最初的内容)。 - Adam
我刚刚在一个.NET Core 3.1 WPF应用程序中尝试了一下,发现可以返回Visibility.Hidden - JansthcirlU

6

你能不能使用样式而不是转换器?代码将类似于:

<Style x:Key="Triggers" TargetType="Button">
    <Style.Triggers>
    <Trigger Property="{Binding ...}" Value="false">
        <Setter Property = "Visibility" Value="Hidden"/>
    </Trigger>
    </Style.Triggers>
</Style>

您需要自己提供属性绑定,以指向您的布尔属性。


2
每次进行这样常见的转换都使用样式和触发器非常浪费资源,这就是我们拥有常见转换器的原因! - Dan Puzey
1
我同意。这绝对有效,但我不喜欢它在我的xaml中添加了太多的代码。这个项目有很多类似的东西,我发现随着数量的增加,它使得读起来变得非常困难。我想知道这两种方法是否有性能差异。 - Rich
我不太清楚 - 我是WPF新手。在Dan提到可能会有性能问题之前,我从未考虑过这个问题,但这是一个有趣的问题。但这真的会给你的XAML代码增加那么多吗?它是一个键入样式,所以您只需在需要引用它的地方引用即可。或者,您还可以基于其他样式创建样式,从而添加可能的重用点。请参见http://msdn.microsoft.com/en-us/library/system.windows.style.basedon.aspx - cristobalito
只有在像我正在处理的这个大型项目中才会出现这个问题。实际上,这取决于每个案例。在我的情况下,制作任何可重用的新内容意味着重新构建外部项目并部署dll文件,而当您至少有50个开发人员在同一个项目中整天签入时,您会尽力避免这种情况。此外,我们的xaml文件不断增长,因此我真的希望尽可能保持所有内容最小化,除非更冗长的解决方案可以带来显著的性能提升。 - Rich
我认为这是一个非常有用的解决方案,但前提是它能够正常工作!我遇到了以下错误:“无法在类型为'Trigger'的'Property'属性上设置'Binding'。'Binding'只能在DependencyObject的DependencyProperty上设置。” 如果只需要一次性应用此解决方案,例如将其应用于整个应用程序中随处可用的导航栏,则此解决方案非常适用。 - usefulBee
@usefulBee 使用DataTrigger而不是Trigger来修复您的错误。 - odyth

4

我喜欢使用参数来反转可见性逻辑: 只需在xaml代码中加入:ConverterParameter=Reverse即可反转逻辑。

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    bool flag = false;
    if (value is bool)
    {
        flag = (bool)value;
    }

    var reverse = parameter as string;
    if(reverse != null && reverse == "Reverse")
        flag != flag;

    return (flag ? Visibility.Visible : Visibility.Collapsed);
}

我最喜欢你的答案。反转的想法很棒,我自己没有想到。 - memory of a dream
1
这不是对OP问题的回答,OP的问题是关于转换为Visibility.Hidden。 - Jim Balter

1
我写了一个名为BoolToVisibilityConverter的转换器,您可以在参数中传递不可见状态:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    var boolValue = (bool) value;
    return boolValue ? Visibility.Visible : (parameter ?? Visibility.Hidden);
}

所以你可以像这样绑定:

Visibility="{Binding SomeBool, Converter={StaticResource ResourceKey=BooleanToVisibilityConverter}, ConverterParameter={x:Static Visibility.Collapsed}}"

希望这有所帮助 :)

0
使用Caliburn.Micro,我有以下代码适用于我的简单用例,并且可能适用于其他情况。
在我的视图中,我有一个按钮,我希望它在我的变量之一具有正长度字符串时隐藏:
<Button x:Name="SelectBinaryFilePath" Content="Select" Visibility="{Binding CanSelectBinaryFile}" />

在我的ViewModel中,我有以下属性:
public Visibility CanSelectBinaryFile
{
    get
    {
        return String.IsNullOrEmpty(FileSystemPath) ? Visibility.Hidden : Visibility.Visible;
    }
}

确保执行NotifyOfPropertyChange以确保更新CanSelectBinaryFile属性。


0

我遇到了这个问题,我的解决方案可能非常特殊,但我还是愿意分享一下。由于我的情况,我能够通过一小段代码模仿转换器,而不需要真正的转换器。只有在绑定到文本框的变量使数字框(通过正则表达式确保其为数字)不为0时,可见性才会发生变化。以下是整个代码,但如果您想在代码中的其他地方更改布尔值,则只需要WPF和C#的前半部分。

wpf:

    Visibility="{Binding Path=Visible}"

C#

    public class foo : INotifyPropertyChanged
    {
        private bool vis = false;
        public object Visible
        {
            get 
            { 
                if (vis == true) 
                { 
                    return Visibility.Visible; 
                } 
                else 
                { 
                    return Visibility.Hidden; 
                } 
            }
            set
            { 
                vis = (bool)value; 
                OnPropertyChanged(nameof(Visible)); 
            }
        }
        public int Value_b
        {
            get 
            { 
                return base_value;
            }
            set
            { 
                base_value = value; 
                OnPropertyChanged(nameof(Value_b));
                if (base_value == 0) 
                { 
                    Visible = false; 
                } 
                else 
                { 
                    Visible = true; 
                } 
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new 
            PropertyChangedEventArgs(propertyName));
        }
    }

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