Lier la visibilité d'un Button à une valeur booléenne dans ViewModel


122

Comment lier la visibilité d'un bouton à une valeur booléenne dans mon ViewModel?

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

Jetez un oeil à CalcBinding
VivekDev

Réponses:


204

En supposant qu'il AdvancedFormats'agit d'un bool, vous devez déclarer et utiliser un 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}}"/>

Notez l'ajout Converter={StaticResource BoolToVis}.

Il s'agit d'un modèle très courant lorsque vous travaillez avec MVVM. En théorie , vous pourriez faire la conversion vous - même sur la propriété ViewModel (soit juste faire le bien immobilier de type Visibility) mais je préférerais ne pas le faire, puisque maintenant vous êtes déconner avec la séparation des préoccupations. La visibilité d'un élément doit vraiment être à la hauteur de la vue.


2
@ raym0nd Bien sûr. Le ViewModel renvoie uniquement un booléen, indiquant une condition. Si votre vue interprète ce booléen comme s'il faut ou non afficher quelque chose, c'est à la vue. Notez qu'une autre vue peut toujours l'interpréter différemment.
dlev

2
Oui, car ce n'est qu'une classe d'aide qui masse une valeur. Le modèle de vue sera toujours placé entre votre modèle et votre vue.
CodeWarrior

2
Gardez également à l'esprit que MVVM est un modèle de conception et que vous devez donc appliquer vos propres règles concernant sa mise en œuvre. De plus, il y aura des moments où le seul moyen d'accomplir quelque chose sera en dehors du modèle, du ViewModel ou de la partie XAML de la vue. Ce n'est pas un péché de mettre quelque chose dans le Codebehind. Il est juste plus conforme au modèle MVVM de le mettre dans le ViewModel si possible.
CodeWarrior

3
Personnellement, cela ne me dérange pas de mettre une propriété de type Visibility dans mes ViewModels. Je sais que c'est hérétique de moi, mais pour moi, cela donne à une vue plus de flexibilité, pas moins. Si une vue ne veut pas l'utiliser, elle n'est pas obligée de le faire et si elle le fait, elle évite d'avoir à jouer avec des convertisseurs ou des déclencheurs de style. Oui, cela lie un peu mon ViewModel à une technologie de présentation (WPF vs ASP.Net MVC, par exemple), mais j'ai rarement besoin de mélanger ces technologies et de refactoriser si jamais je le fais ne me fait pas peur, beaucoup.
Jacob Proffitt

1
BooleanToVisibilityConverter n'est actuellement pas disponible pour les interfaces utilisateur Windows Phone, mais cette réponse a fourni une implémentation stackoverflow.com/a/20344739/595473
CosworthTC

97

Il existe une troisième méthode qui ne nécessite pas de convertisseur ou de modification de votre modèle de vue: utilisez un style:

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

J'ai tendance à préférer cette technique car je l'utilise dans de nombreux cas où ce à quoi je me lie n'est pas booléen - par exemple, afficher un élément uniquement s'il DataContextn'est pas nul, ou implémenter des affichages multi-états où différentes mises en page apparaissent en fonction du définition d'une énumération dans le modèle de vue.


5
En général, j'ai l'impression que les convertisseurs sont un hack et je ne les aime pas. Je pense que c'est une question de goût personnel grincheux plutôt qu'une évaluation sobre des avantages et des inconvénients du point de vue de l'ingénierie, mais je les évite.
Robert Rossney

1
Je ne peux pas dire que je les utilise aussi souvent. Ils ont tendance à être un peu capricieux (sic?). Après votre message, je me suis souvenu que j'avais utilisé pas mal de styles / déclencheurs dans des projets précédents ...
CodeWarrior

J'avais un TextBlockà qui a TextWrapping="Wrap"été donné. Maintenant, cette propriété d'encapsulation n'y est pas définie.
amit jha

10

Conversion bidirectionnelle en c # de booléen à visibilité

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

7
Comme cela a déjà été mentionné, il en existe un déjà intégré à WPF. Vous n'avez pas besoin de créer le vôtre.
Chaussure du

4

En règle générale, il existe deux façons de le faire, une classe de convertisseur ou une propriété dans le Viewmodel qui convertit essentiellement la valeur pour vous.

J'ai tendance à utiliser l'approche de propriété s'il s'agit d'une conversion unique. Si vous souhaitez le réutiliser, utilisez le convertisseur. Ci-dessous, trouvez un exemple du convertisseur:

<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

Une méthode de propriété ViewModel vérifierait simplement la valeur de la propriété booléenne et retournerait une visibilité basée sur cela. Veillez à implémenter INotifyPropertyChanged et à l'appeler sur les propriétés Boolean et Visibility pour être mis à jour correctement.


12
WPF a déjà un BooleanToVisibilityConverter intégré.
CodeNaked

Je n'avais pas réalisé cela. Celui-ci était en fait quelque chose d'autre que j'ai édité pour s'adapter à ce scénario. Tant mieux s'il y en a un pré-construit.
CodeWarrior

3

Ceci peut être réalisé d'une manière très simple 1. Ecrivez ceci dans la vue.

<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. Voici la propriété booléenne qui contient la valeur vrai / faux. Voici l'extrait de code. Dans mon exemple, cette propriété est dans la classe UserNote.

    public bool _isHide = false;
    
    public bool IsHide
    {
    
    get { return _isHide; }
    
    set
        {
            _isHide = value;
                OnPropertyChanged("IsHide");
        }
    } 
  2. C'est ainsi que la propriété IsHide obtient la valeur.

    userNote.IsHide = userNote.IsNoteDeleted;

2

En vue:

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

En vue Modèle:

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

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

Vous devrez avoir un événement de changement de propriété

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

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

Voici comment ils utilisent Model-view-viewmodel

Mais puisque vous voulez qu'il soit lié à un booléen, vous aurez besoin d'un convertisseur. Une autre méthode consiste à définir un booléen à l'extérieur et, lorsque vous cliquez sur ce bouton, définissez property_advancedFormat sur la visibilité souhaitée.


private Visibility _advancedFormat = Visibility.visibleCela fonctionne très bien sur UWPmerci.
rubStackOverflow

1

Depuis Windows 10 15063 et plus

Depuis Windows 10 build 15063, il existe une nouvelle fonctionnalité appelée «Conversion de visibilité implicite» qui lie la visibilité à la valeur booléenne de manière native - Il n'est plus nécessaire d'utiliser un convertisseur.

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

Mon code (qui suppose que MVVM est utilisé, et le modèle 10 également):

<!-- 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(); }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.