将ViewModel中的bool值与按钮的可见性绑定

151

如何将视图模型中的布尔值与按钮的可见性绑定?

<Button Height="50" Width="50" Style="{StaticResource MyButtonStyle}"
    Command="{Binding SmallDisp}" CommandParameter="{Binding}" Cursor="Hand"
    Visibility="{Binding Path=AdvancedFormat}" />

请查看CalcBinding - VivekDev
7个回答

248

假设 AdvancedFormat 是一个 bool 类型,你需要声明并使用一个名为 BooleanToVisibilityConverter 的转换器:

<!-- In your resources section of the XAML -->
<BooleanToVisibilityConverter x:Key="BoolToVis" />

<!-- In your Button declaration -->
<Button
 Height="50" Width="50"
 Style="{StaticResource MyButtonStyle}"
 Command="{Binding SmallDisp}" CommandParameter="{Binding}" 
Cursor="Hand" Visibility="{Binding Path=AdvancedFormat, Converter={StaticResource BoolToVis}}"/>

请注意添加的 Converter={StaticResource BoolToVis}

这是在使用MVVM时非常常见的模式。理论上,你可以在ViewModel属性上自己进行转换(即仅将属性本身作为类型Visibility),但我不希望这样做,因为现在你正在干扰关注点的分离。一个条目的可见性应该由View来确定。


3
@raym0nd 当然可以。ViewModel 只返回一个布尔值,表示一种条件。如果你的 View 恰好将该布尔值解释为是否显示某些内容,那就由 View 自己决定。请注意,另一个 View 仍然可以有不同的解释方式。 - dlev
2
是的,因为这只是一个帮助类来处理值。视图模型仍将位于您的模型和视图之间。 - CodeWarrior
2
此外,请记住MVVM是一种设计模式,因此您必须强制执行自己的规则来实现它。此外,有时候完成某些事情的唯一方法将在Model、ViewModel或View的XAML部分之外。把东西放在Codebehind中并不是罪过。如果可能的话,把它放在ViewModel中更符合MVVM模式。 - CodeWarrior
3
个人而言,我不介意在我的ViewModels中加入Visibility类型的属性。我知道这对我来说是异端邪说,但在我看来,这样做使得View更加灵活,而不是更加局限。如果一个View不想使用它,它就可以不用,而如果一个View想要使用它,它也能避免使用转换器或者样式触发器带来的麻烦。是的,这可能会让我的ViewModel与某种表示技术(例如WPF vs. ASP.Net MVC)有一定的耦合性,但我很少需要混用这些技术,即使我真的需要进行重构,也不会让我感到害怕。 - Jacob Proffitt
1
BooleanToVisibilityConverter目前不适用于Windows Phone UI,但是这个答案提供了一种实现方法https://dev59.com/pGIj5IYBdhLWcg3waEZe#20344739 - CosworthTC
显示剩余3条评论

107

还有第三种方法,不需要转换器或更改您的视图模型:使用样式:

<Style TargetType="Button">
   <Setter Property="Visibility" Value="Collapsed"/>
   <Style.Triggers>
      <DataTrigger Binding="{Binding IsVisible}" Value="True">
         <Setter Property="Visibility" Value="Visible"/>
      </DataTrigger>
   </Style.Triggers>
</Style>

我倾向于使用这个技术,因为在很多情况下我绑定的内容并不是布尔值 - 例如仅当DataContext不为空时才显示元素,或者实现多状态显示,在其中根据视图模型中枚举的设置显示不同的布局。


6
总体而言,我觉得转换器是一种折中的解决方案,我不喜欢它们。我认为这是出于我个人挑剔的口味,而不是工程角度上理性的利弊评估,但我会避免使用它们。 - Robert Rossney
1
我也不能说我经常使用它们。它们往往有点棘手。在看到你的帖子之后,我记得我在以前的项目中使用了相当多的样式/触发器... - CodeWarrior
我曾经给一个TextBlock设置了TextWrapping="Wrap",但现在这个属性已经被取消了。 - amit jha
+1,这非常有用,我现在可以避免为任何外观相关的转换编写自定义代码,例如基于数据中某个值的背景颜色。 - Piotr Golacki

10

如何在C#中将布尔值转换为可见性的双向转换

using System;
using System.Windows;
using System.Windows.Data;

namespace FaceTheWall.converters
{
    class BooleanToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is Boolean && (bool)value)
            {
                return Visibility.Visible;
            }
            return Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is Visibility && (Visibility)value == Visibility.Visible)
            {
                return true;
            }
            return false;
        }
    }
}

8
正如已经提到的,WPF 中已经内置了一个。你不需要自己创建。 - jamesSampica

7
自 Windows 10 15063 版本以后,有一个名为“隐式可见性转换”的新功能,将可见性与布尔值本地绑定 - 不再需要使用转换器。查看链接:https://social.technet.microsoft.com/wiki/contents/articles/34846.uwp-compiled-binding-windows-10-anniversary-update.aspx#Implicit_Visibility_conversion 如果我假设使用了 MVVM 和 Template 10,我的代码如下:
<!-- In XAML -->
<StackPanel x:Name="Msg_StackPanel" Visibility="{x:Bind ViewModel.ShowInlineHelp}" Orientation="Horizontal" Margin="0,24,0,0">
    <TextBlock Text="Frosty the snowman was a jolly happy soul" Margin="0,0,8,0"/>
    <SymbolIcon Symbol="OutlineStar "/>
    <TextBlock Text="With a corncob pipe and a button nose" Margin="8,0,0,0"/>
</StackPanel>

<!-- in companion View-Model -->
public bool ShowInlineHelp // using T10 SettingsService
{ 
    get { return (_settings.ShowInlineHelp); }
    set { _settings.ShowInlineHelp = !value; base.RaisePropertyChanged(); }
}

1
这应该是新的最佳答案。我们应该停止使用转换器。 - laishiekai
3
问题要求提供一个WPF答案。编译绑定,也就是{x:Bind},目前不支持WPF。以下是相关问题,包括用户制作的x:Bind实现链接:https://github.com/dotnet/wpf/issues/130 - ChoKaPeek
在WinUI 3上,我可以这样做:Visibility="{Binding IsVisible} - Slion

4

通常有两种方法可以做到这一点,一个是转换器类,另一个是在Viewmodel中使用属性来为您转换值。

如果只需要一次转换,我倾向于使用属性方法。如果您想要重复使用它,请使用转换器。下面是一个转换器的示例:

<ValueConversion(GetType(Boolean), GetType(Visibility))> _
Public Class BoolToVisibilityConverter
    Implements IValueConverter

    Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert

        If value IsNot Nothing Then
            If value = True Then 
                Return Visibility.Visible
            Else
                Return Visibility.Collapsed
            End If
        Else
            Return Visibility.Collapsed
        End If
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
        Throw New NotImplementedException
    End Function
End Class

一个ViewModel属性方法只需检查布尔属性值,并根据此返回可见性。请确保实现了INotifyPropertyChanged并在布尔和可见性属性上调用它以正确更新。


14
WPF已经内置了一个BooleanToVisibilityConverter - CodeNaked
我之前没有意识到这一点。这个实际上是我编辑以适应这种情况的。如果有一个预先构建的,那就更好了。 - CodeWarrior

3
这可以通过一种非常简单的方式实现: 1. 在视图中编写以下内容。
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Width="50" Height="30">
<Button.Style>
        <Style TargetType="Button">
                <Setter Property="Visibility" Value="Collapsed"/>
                        <Style.Triggers>
                                <DataTrigger Binding="{Binding IsHide}" Value="True">
                                        <Setter Property="Visibility" Value="Visible"/>
                                    </DataTrigger>
                            </Style.Triggers>
            </Style>
    </Button.Style>

  1. The following is the Boolean property which holds the true/ false value. The following is the code snippet. In my example this property is in UserNote class.

    public bool _isHide = false;
    
    public bool IsHide
    {
    
    get { return _isHide; }
    
    set
        {
            _isHide = value;
                OnPropertyChanged("IsHide");
        }
    } 
    
  2. This is the way the IsHide property gets the value.

    userNote.IsHide = userNote.IsNoteDeleted;
    

2

在视图中:

<Button
 Height="50" Width="50"
 Style="{StaticResource MyButtonStyle}"
 Command="{Binding SmallDisp}" CommandParameter="{Binding}" 
Cursor="Hand" Visibility="{Binding Path=AdvancedFormat}"/>

在视图模型中:

public _advancedFormat = Visibility.visible (whatever you start with)

public Visibility AdvancedFormat
{
 get{return _advancedFormat;}
 set{
   _advancedFormat = value;
   //raise property changed here
}

您需要拥有一个属性更改事件

 protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) 
    { 
        PropertyChanged.Raise(this, e); 
    } 

    protected void OnPropertyChanged(string propertyName) 
    { 
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); 
    } 

这是他们如何使用Model-view-viewmodel

但是,如果您希望将其绑定到布尔值,则需要一些转换器。 另一种方法是设置一个布尔变量,当单击该按钮时,将property_advancedFormat属性设置为所需的可见性。


私有的Visibility _advancedFormat = Visibility.visible,这在UWP上很好用,谢谢。 - rubStackOverflow

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