Vinculando a visibilidade de um botão a um valor bool no ViewModel

122

Como vincular a visibilidade de um botão a um valor bool no meu ViewModel?

<Button Height="50" Width="50" Style="{StaticResource MyButtonStyle}"
    Command="{Binding SmallDisp}" CommandParameter="{Binding}" Cursor="Hand"
    Visibility="{Binding Path=AdvancedFormat}" />
raym0nd
fonte
Dê uma olhada no CalcBinding
VivekDev

Respostas:

204

Assumindo que AdvancedFormaté um bool, você precisa declarar e usar um 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}}"/>

Observe o adicionado Converter={StaticResource BoolToVis}.

Esse é um padrão muito comum ao trabalhar com o MVVM. Em teoria, você pode fazer a conversão você mesmo na propriedade ViewModel (ou seja, apenas tornar a propriedade do tipo Visibility), embora eu prefira não fazer isso, já que agora você está mexendo na separação de preocupações. A visibilidade de um item deve realmente estar na visualização.

dlev
fonte
2
@ raym0nd Claro. O ViewModel retorna apenas um booleano, indicando uma condição. Se o seu View interpreta esse booleano como mostrar ou não algo, isso é com o View. Observe que outro modo de exibição ainda pode interpretá-lo de maneira diferente.
dlev
2
Sim, pois essa é apenas uma classe auxiliar que massageia um valor. O modelo de visualização ainda ficará entre o modelo e a visualização.
CodeWarrior
2
Além disso, lembre-se de que o MVVM é um padrão de design e, portanto, você deve aplicar suas próprias regras em relação à sua implementação. Além disso, haverá momentos em que a única maneira de realizar algo estará fora do Model, do ViewModel ou da parte XAML do View. Não é pecado colocar algo no Codebehind. É apenas mais alinhado com o padrão MVVM para colocá-lo no ViewModel, se possível.
CodeWarrior
3
Pessoalmente, não me importo de colocar uma propriedade do tipo Visibility em meus ViewModels. Eu sei que isso é herege de minha parte, mas para mim, isso oferece uma flexibilidade maior, não menor. Se um modo de exibição não quiser usá-lo, ele não precisa e, se houver, elimina a dor de ter que brincar com conversores ou gatilhos de estilo. Sim, isso vincula meu ViewModel a uma tecnologia de apresentação (WPF vs. ASP.Net MVC, por exemplo), mas raramente preciso misturar essas tecnologias e refatorar, se é que isso não me assusta.
Jacob Proffitt
1
BooleanToVisibilityConverter não está disponível para Windows Phone UIs, no entanto esta resposta desde uma implementação stackoverflow.com/a/20344739/595473
CosworthTC
97

Existe uma terceira maneira que não requer um conversor ou uma alteração no seu modelo de exibição: use um estilo:

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

Eu tendem a preferir essa técnica porque a uso em muitos casos em que o que estou vinculando não é booleano - por exemplo, exibindo um elemento apenas se DataContextnão for nulo ou implementando exibições de vários estados em que diferentes layouts aparecem com base no configuração de uma enumeração no modelo de vista.

Robert Rossney
fonte
5
Geralmente, sinto que os conversores são um hack e não gosto deles. Acho que isso é uma questão do meu gosto pessoal irritadiço, em vez de uma avaliação sóbria dos prós e contras do ponto de vista da engenharia, mas eu os evito.
Robert Rossney
1
Também não posso dizer que também os uso com frequência. Eles tendem a ser meio meticulosos (sic?). Após o seu post, eu me lembrei que eu usei muito poucos estilos / gatilhos em projectos anteriores ...
CodeWarrior
Eu tinha um TextBlocka que TextWrapping="Wrap"foi dado. Agora essa propriedade de quebra automática não está definida nela.
Amit jha
10

Conversão bidirecional em c # de booleano para visibilidade

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;
        }
    }
}
Berty
fonte
7
Como já foi mencionado, já existe um incorporado no WPF. Você não precisa fazer o seu próprio.
Shoe
4

Geralmente, existem duas maneiras de fazer isso, uma classe de conversor ou uma propriedade no Viewmodel que basicamente converte o valor para você.

Costumo usar a abordagem de propriedade se for uma conversão única. Se você deseja reutilizá-lo, use o conversor. Abaixo, encontre um exemplo do conversor:

<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

Um método de propriedade ViewModel apenas verifica o valor da propriedade booleana e retorna uma visibilidade com base nisso. Certifique-se de implementar INotifyPropertyChanged e chame-o nas propriedades booleana e de visibilidade para atualizar corretamente.

CodeWarrior
fonte
12
WPF já um BooleanToVisibilityConverter construído dentro.
CodeNaked
Eu não tinha percebido isso. Este foi, na verdade, outra coisa que editei para ajustar-se a esse cenário. Tanto melhor se houver um pré-construído.
CodeWarrior
3

Isso pode ser conseguido de uma maneira muito simples 1. Escreva isso na exibição.

<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. A seguir está a propriedade booleana que contém o valor verdadeiro / falso. A seguir está o trecho de código. No meu exemplo, essa propriedade está na classe UserNote.

    public bool _isHide = false;
    
    public bool IsHide
    {
    
    get { return _isHide; }
    
    set
        {
            _isHide = value;
                OnPropertyChanged("IsHide");
        }
    } 
  2. É assim que a propriedade IsHide obtém o valor.

    userNote.IsHide = userNote.IsNoteDeleted;
Joy Fernandes
fonte
2

Em vista:

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

Em vista Modelo:

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

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

Você precisará ter um evento alterado de propriedade

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

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

É assim que eles usam o Model-view-viewmodel

Mas como você deseja que ele seja vinculado a um booleano, você precisará de algum conversor. Outra maneira é definir um booleano fora e, quando esse botão for clicado, defina o property_advancedFormat com a visibilidade desejada.

Kevin
fonte
private Visibility _advancedFormat = Visibility.visibleIsso funciona bem no UWPobrigado.
rubStackOverflow
1

Desde o Windows 10 15063 para cima

Desde o Windows 10 compilação 15063, há um novo recurso chamado "Conversão implícita de visibilidade" que vincula Visibilidade ao valor bool nativo - Não é mais necessário usar um conversor.

(consulte https://social.technet.microsoft.com/wiki/contents/articles/34846.uwp-compiled-binding-windows-10-anniversary-update.aspx#Implicit_Visibility_conversion ).

Meu código (que supõe que o MVVM seja usado e o Modelo 10 também):

<!-- 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(); }
}
Varus Septimus
fonte