Powiązanie widoczności Button z wartością bool w ViewModel

122

Jak powiązać widoczność przycisku z wartością bool w moim ViewModel?

<Button Height="50" Width="50" Style="{StaticResource MyButtonStyle}"
    Command="{Binding SmallDisp}" CommandParameter="{Binding}" Cursor="Hand"
    Visibility="{Binding Path=AdvancedFormat}" />
raym0nd
źródło
Spójrz na CalcBinding
VivekDev

Odpowiedzi:

204

Zakładając, że AdvancedFormatjest bool, musisz zadeklarować i użyć 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}}"/>

Zwróć uwagę na dodane Converter={StaticResource BoolToVis}.

Jest to bardzo powszechny wzorzec podczas pracy z MVVM. Teoretycznie można zrobić konwersję siebie na własność ViewModel (czyli po prostu zrobić właściwość samego typu Visibility), chociaż ja nie preferuje tego robić, ponieważ teraz brudząc z separacji obawy. Widoczność elementu powinna naprawdę zależeć od widoku.

dlev
źródło
2
@ raym0nd Jasne. ViewModel zwraca tylko wartość logiczną, wskazującą warunek. Jeśli Twój widok zdarzy się zinterpretować tę wartość logiczną jako, czy coś pokazać, czy nie, to zależy od widoku. Zauważ, że inny widok może nadal interpretować to inaczej.
dlev
2
Tak, ponieważ jest to tylko klasa pomocnicza, która masuje wartość. Viewmodel nadal będzie znajdować się pomiędzy Twoim modelem a Twoim widokiem.
CodeWarrior,
2
Pamiętaj też, że MVVM to wzorzec projektowy, a tym samym musisz narzucić własne zasady dotyczące jego implementacji. Ponadto będą sytuacje, w których jedynym sposobem wykonania czegoś będzie poza modelem, ViewModel lub częścią XAML widoku. Nie jest grzechem wkładanie czegoś do Codebehind. Jest po prostu bardziej zgodne ze wzorcem MVVM, aby umieścić go w ViewModel, jeśli to możliwe.
CodeWarrior
3
Osobiście nie mam nic przeciwko umieszczaniu właściwości typu Visibility w moich ViewModels. Wiem, że to heretyk z mojej strony, ale dla mnie to daje widokowi większą elastyczność, a nie mniej. Jeśli widok nie chce go używać, nie musi, a jeśli to zrobi, eliminuje ból związany z koniecznością korzystania z konwerterów lub wyzwalaczy stylu. Tak, to wiąże mój ViewModel z technologią prezentacji (na przykład WPF vs. ASP.Net MVC), ale rzadko muszę mieszać te technologie i refaktoryzację, jeśli kiedykolwiek to zrobię , nie przeraża mnie to bardzo.
Jacob Proffitt,
1
BooleanToVisibilityConverter nie jest obecnie dostępny dla interfejsów użytkownika systemu Windows Phone, jednak ta odpowiedź zapewniła implementację stackoverflow.com/a/20344739/595473
CosworthTC
97

Istnieje trzeci sposób, który nie wymaga konwertera ani zmiany modelu widoku: użyj stylu:

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

Preferuję tę technikę, ponieważ używam jej w wielu przypadkach, w których to, co wiążę, nie jest logiczne - np. Wyświetlanie elementu tylko wtedy, gdy DataContextnie jest null, lub implementowanie wyświetlaczy wielostanowych, w których pojawiają się różne układy w oparciu o ustawienie wyliczenia w modelu widoku.

Robert Rossney
źródło
5
Generalnie czuję, że konwertery to hack i nie lubię ich. Myślę, że jest to kwestia mojego zrzędliwego osobistego gustu, a nie trzeźwa ocena zalet i wad z inżynieryjnego punktu widzenia, ale unikam ich.
Robert Rossney
1
Nie mogę powiedzieć, że często ich używam. Zwykle są trochę wybredni (sic?). Po twoim poście przypomniałem sobie, że w poprzednich projektach użyłem całkiem sporo stylów / wyzwalaczy ...
CodeWarrior
Miałem, TextBlockktóremu TextWrapping="Wrap"dano. Teraz ta właściwość zawijania nie jest w nim ustawiona.
amit jha
10

Dwukierunkowa konwersja w języku C # z wartości logicznej na widoczność

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
źródło
7
Jak już wspomniano, jest już wbudowany w WPF. Nie musisz tworzyć własnego.
But
4

Ogólnie rzecz biorąc, można to zrobić na dwa sposoby, za pomocą klasy konwertera lub właściwości w modelu Viewmodel, która zasadniczo konwertuje wartość za Ciebie.

Zwykle stosuję podejście dotyczące własności, jeśli jest to jednorazowa konwersja. Jeśli chcesz go ponownie użyć, użyj konwertera. Poniżej przykład konwertera:

<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

Metoda właściwości ViewModel po prostu sprawdzałaby wartość właściwości logicznej i na jej podstawie zwracałaby widoczność. Pamiętaj, aby zaimplementować INotifyPropertyChanged i wywołać ją we właściwościach Boolean i Visibility, aby poprawnie zaktualizować.

CodeWarrior
źródło
12
WPF ma już wbudowany BooleanToVisibilityConverter .
CodeNaked
Nie zdawałem sobie z tego sprawy. Ten był właściwie czymś innym, co zredagowałem, aby dopasować się do tego scenariusza. Tym lepiej, jeśli jest jeden fabrycznie zbudowany.
CodeWarrior,
3

Można to osiągnąć w bardzo prosty sposób 1. Zapisz to w widoku.

<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. Poniżej znajduje się właściwość logiczna, która przechowuje wartość prawda / fałsz. Poniżej znajduje się fragment kodu. W moim przykładzie ta właściwość jest w klasie UserNote.

    public bool _isHide = false;
    
    public bool IsHide
    {
    
    get { return _isHide; }
    
    set
        {
            _isHide = value;
                OnPropertyChanged("IsHide");
        }
    } 
  2. W ten sposób właściwość IsHide pobiera wartość.

    userNote.IsHide = userNote.IsNoteDeleted;
Joy Fernandes
źródło
2

Z uwagi:

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

W widoku Model:

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

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

Będziesz musiał mieć wydarzenie zmiany właściwości

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

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

W ten sposób używają modelu widoku modelu

Ale ponieważ chcesz, aby był powiązany z wartością logiczną, będziesz potrzebować konwertera. Innym sposobem jest ustawienie wartości boolowskiej na zewnątrz i po kliknięciu tego przycisku ustaw właściwość property_advancedFormat na żądaną widoczność.

Kevin
źródło
private Visibility _advancedFormat = Visibility.visibleTo działa dobrze na UWPdzięki.
rubStackOverflow
1

Od Windows 10 15063 wzwyż

Od wersji 15063 systemu Windows 10 dostępna jest nowa funkcja o nazwie „Niejawna konwersja widoczności”, która natywnie wiąże widoczność z wartością bool - nie ma już potrzeby korzystania z konwertera.

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

Mój kod (który zakłada, że ​​używany jest MVVM, a także szablon 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(); }
}
Varus Septimus
źródło