28
votes

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"/>
2
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.Mike Strobel
@MikeStrobel, surely the default access modifier is internal and not private.Sheridan
Yep can't belive I missed that! It wasn't publicmHelpMe
@Sheridan For top-level declarations, the default is internal; for class members, including nested type declarations, it is private.Mike Strobel

2 Answers

43
votes

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.

7
votes

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.