Binding a Button's visibility to a bool value in ViewModel
Asked Answered
T

7

153

How do I bind the visibility of a button to a bool value in my ViewModel?

<Button Height="50" Width="50" Style="{StaticResource MyButtonStyle}"
    Command="{Binding SmallDisp}" CommandParameter="{Binding}" Cursor="Hand"
    Visibility="{Binding Path=AdvancedFormat}" />
Thibaud answered 9/8, 2011 at 18:15 Comment(1)
Take a look at CalcBindingBlindage
S
254

Assuming AdvancedFormat is a bool, you need to declare and use a 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}}"/>

Note the added Converter={StaticResource BoolToVis}.

This is a very common pattern when working with MVVM. In theory you could do the conversion yourself on the ViewModel property (i.e. just make the property itself of type Visibility) though I would prefer not to do that, since now you are messing with the separation of concerns. An item's visbility should really be up to the View.

Shelving answered 9/8, 2011 at 18:22 Comment(8)
Is it ok to use this method if I'm using an MVVM style ?Thibaud
@Thibaud Sure. The ViewModel returns only a boolean, indicating a condition. If your View happens to interpret that boolean as whether or not to show something, that's up to the View. Note that another View can still interpret it differently.Shelving
Yes, as this is just a helper class that massages a value. The viewmodel will still sit between your model and your view.Minda
Also, keep in mind that MVVM is a design pattern, and thus you have to enforce your own rules regarding its implementation. Additionally there will be times when the only way to accomplish something will be outside of the Model, the ViewModel or the XAML portion of the View. It is not a sin to put something in the Codebehind. It is just more in line with the MVVM pattern to put it in the ViewModel if possible.Minda
Personally, I don't mind putting a property of type Visibility in my ViewModels. I know that's heretic of me, but to me, this gives a View more flexibility, not less. If a View doesn't want to use it, it doesn't have to and if one does, it cuts out the pain of having to play with converters or style triggers. Yes, this ties my ViewModel to a presentation technology (WPF vs. ASP.Net MVC, for example) a bit, but I seldom need to mix those technologies and refactoring if I ever do doesn't scare me, much.Euphonize
For me, , Converter={StaticResource VisibilityOfBool} worked over , Converter={StaticResource BoolToVis}Serai
BooleanToVisibilityConverter isn't currently available to Windows Phone UIs, however this answer provided an implementation https://mcmap.net/q/159902/-how-to-implement-a-booltovisibilityconverterLienlienhard
Please see the next answer: in the latest update, we don't need to use a converter.Elboa
F
108

There's a third way that doesn't require a converter or a change to your view model: use a 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>

I tend to prefer this technique because I use it in a lot of cases where what I'm binding to is not boolean - e.g. displaying an element only if its DataContext is not null, or implementing multi-state displays where different layouts appear based on the setting of an enum in the view model.

Frowzy answered 9/8, 2011 at 18:34 Comment(4)
Generally, I feel like converters are a hack and I don't like them. I think this is a matter of my cranky personal taste rather than a sober assessment of the pros and cons from an engineering standpoint, but I avoid them.Frowzy
I can't say that I use them that often either. They tend to be kind of finicky (sic?). After your post, I remembered that I used quite a few styles/triggers in previous projects...Minda
I had a TextBlock to which TextWrapping="Wrap" was given. Now that wrapping property is not set in it.Mcclenaghan
+1, this is very useful, I can now avoid having a custom code for any appearance related conversion, such as background color based on some value in dataRaveaux
G
10

2 way conversion in c# from boolean to visibility

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;
        }
    }
}
Gerrald answered 2/1, 2014 at 18:23 Comment(1)
As has already been mentioned, there is one already built into WPF. You don't need to make your own.Prosper
F
7

Since Windows 10 15063 upwards

Since Windows 10 build 15063, there is a new feature called "Implicit Visibility conversion" that binds Visibility to bool value natively - There is no need anymore to use a converter.

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

My code (which supposes that MVVM is used, and Template 10 as well):

<!-- 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(); }
}
Formulaic answered 29/10, 2019 at 20:8 Comment(3)
This should be the new best answer. We should stop using the converters.Elboa
The question asks for a WPF answer. Compiled Binding, aka {x:Bind}, is not currently supported for WPF. Here is the relevant issue, with links to a user-made x:Bind implementation: https://github.com/dotnet/wpf/issues/130Tetrarch
On WinUI 3 I could do: Visibility="{Binding IsVisible}Epigene
M
4

Generally there are two ways to do it, a converter class or a property in the Viewmodel that essentially converts the value for you.

I tend to use the property approach if it is a one off conversion. If you want to reuse it, use the converter. Below, find an example of the converter:

<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

A ViewModel property method would just check the boolean property value, and return a visibility based on that. Be sure to implement INotifyPropertyChanged and call it on both the Boolean and Visibility properties to updated properly.

Minda answered 9/8, 2011 at 18:25 Comment(2)
WPF already has a BooleanToVisibilityConverter built in.Imidazole
I had not realized that. This one was actually something else that I edited to fit this scenario. So much the better if there is one prebuilt.Minda
C
3

This can be achieved in a very simple way 1. Write this in the view.

<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;
    
Crescantia answered 23/9, 2015 at 13:38 Comment(0)
D
2

In View:

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

In view Model:

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

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

You will need to have a property changed event

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

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

This is how they use Model-view-viewmodel

But since you want it binded to a boolean, You will need some converter. Another way is to set a boolean outside and when that button is clicked then set the property_advancedFormat to your desired visibility.

Davison answered 9/8, 2011 at 20:56 Comment(1)
private Visibility _advancedFormat = Visibility.visible This works fine on UWP thanks.Improvisatory

© 2022 - 2024 — McMap. All rights reserved.