WPF MVVM hiding button using BooleanToVisibilityConverter
Asked Answered
H

2

33

In my WPF application I am trying to change the visibility of a button depending on the options chosen by a user. On load I want one of the buttons to not be visible. I am using the in built value converter BooleanToVisibilityConverter. However it's not working as the button is appearing at load time. I have changed the property to both true and false, makes no difference. Below is my code, I can't see what I'm missing?

The property in my View Model

 bool ButtCancel
    {
        get { return _buttCancel; }
        set
        {
            _buttCancel = value;
            OnPropertyChanged("ButtCancel");
        }
    }

In my app.xaml

 <Application.Resources>       
    <BooleanToVisibilityConverter x:Key="BoolToVis"/>

In my MainWindow.xaml

 <Button Grid.Column="2" 
      Command="{Binding CommandButtProgressCancel}" 
      Content="Cancel" 
      Visibility="{Binding ButtCancel, Converter={StaticResource BoolToVis}}"
      IsEnabled="{Binding ButtCancelEnabled}" 
      Height="50" Width="120" 
      HorizontalAlignment="Center" 
      VerticalAlignment="Center" Margin="0,0,50,20"/>
Hub answered 13/12, 2013 at 15:31 Comment(4)
Is your ButtCancel property public? It has no access modifier in your code excerpt, which would make it private and thus invisible to the binding engine. Also, you should not be binding the IsEnabled property; let the button use your command's CanExecute callback to determine its own state.Argonaut
@MikeStrobel, surely the default access modifier is internal and not private.Military
Yep can't belive I missed that! It wasn't publicHub
@Military For top-level declarations, the default is internal; for class members, including nested type declarations, it is private.Argonaut
D
52

For starters mate, if you're using a Command, then you don't need to bind IsEnabled, the command implementation should decide this.

Secondly, the binding of a ViewModel to a View tends to happen at a bit of a later stage, so it's best to also set a default value for the binding, like so

Visibility="{Binding ButtCancel, Converter={StaticResource BoolToVis}, FallbackValue=Hidden}"

Third, as Mike pointed out, ensure that your property is public, since the ViewModel and the View are two separate classes.

Drais answered 13/12, 2013 at 15:44 Comment(2)
An internal property won't work either. The binding engine has no knowledge of where a binding was declared, so all non-public members are treated the same (effectively private).Argonaut
Oh, I wasn't aware of that, thanks Mike, I'll adjust my answer.Drais
I
7

Instead of using a converter, you can just use a DataTrigger.

<Button Grid.Column="2" Command="{Binding CommandButtProgressCancel}" Content="Cancel" 
        Visibility="{Binding ButtCancel, Converter={StaticResource BoolToVis}}" 
        IsEnabled="{Binding ButtCancelEnabled}" Height="50" Width="120"
        HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,50,20">
    <Button.Style>
        <Style TargetType={X:Type Button}>
            <!-- This would be the default visibility -->
            <Setter Property="Visibility" Value="Visible" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding ButtCancel, UpdateSourceTrigger=PropertyChanged}" Value="True">
                    <Setter Property="Visibility" Value="Hidden" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

Update your ViewModel's properties to public

public bool ButtCancel
{
    get { return _buttCancel; }
    set
    {
        _buttCancel = value;
        OnPropertyChanged("ButtCancel");
    }
}

And make sure the DataContext of your MainWindow is set to the ViewModel.

Intemperate answered 13/12, 2013 at 15:56 Comment(2)
triggers aren't the best if you are trying to use a MVVM pattern (as the OP says).Kurtz
I've noticed that triggers are slower than using a binding and a converter. I would not recommend them. My button would lag when I changed the background color using a trigger.Ryley

© 2022 - 2024 — McMap. All rights reserved.