2
votes

My attempt (below) fails:

<Canvas x:Key="Lock" ... />
<Canvas x:Key="Unlock" ... />

<Style x:Key="LockButtonStyle" TargetType="{x:Type Button}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=IsReadOnly}" Value="True">
            <DataTrigger.Setters>
                <Setter Property="Content" Value="{StaticResource Lock}" />                           
            </DataTrigger.Setters>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=IsReadOnly}" Value="False">
            <DataTrigger.Setters>
                <Setter Property="Content" Value="{StaticResource Unlock}" />
            </DataTrigger.Setters>
        </DataTrigger>
    </Style.Triggers>
</Style>

...

<Button Content="{StaticResource Lock}" />

I'm trying to get the button to change when the IsReadOnly property on the ViewModel changes (it fires INotifyPropertyChanged.PropertyChanged with "IsReadOnly" as the PropertyName). What am I missing?

.NET 3.5

ANSWER (at least for me - doesn't support the general case):

I just wrote a quick converter for boolean property binding.

[ValueConversion(typeof(bool), typeof(object))]
public class BooleanValueConverter : IValueConverter
{
    public object FalseValue { get; set; }
    public object TrueValue { get; set; }

    #region IValueConverter Members

    public object Convert(object value, Type targetType, 
                          object parameter, CultureInfo culture)
    {
        return (bool)value ? this.TrueValue : this.FalseValue;
    }

    public object ConvertBack(object value, Type targetType, 
                              object parameter, CultureInfo culture)
    {
        return object.Equals(this.TrueValue, value) ? true : false;
    }

    #endregion
}

...

<local:BooleanValueConverter x:Key="LockOrUnlock" 
    TrueValue="{StaticResource Unlock}" 
    FalseValue="{StaticResource Lock}" />

...

<Button Content="{Binding Path=IsReadOnly, 
                             Converter={StaticResource LockOrUnlock}}" />
2

2 Answers

5
votes

If you set the property "Content" of the Button, you cannot change it with a trigger, because the first one takes precedence.
Try to remove the setting of Content and it should work, because triggers will do the right work themselves.

2
votes

You are using C# which means that the ToString() method for booleans return "true" and "false; whereas VB.NET they would be "True" and "False.

Your original code will work if you change your triggers to use the lowercase "true" and "false" instead of uppercase "True" and "False".

Tested with this code...

XAML:

<Window.Resources>
     <local:ViewModel x:Key="TheViewModel" />
    <Canvas x:Key="Lock">
        <TextBlock Text="Lock"/>
    </Canvas>
    <Canvas x:Key="Unlock">
        <TextBlock Text="Unlock"/>
    </Canvas>

    <Style x:Key="LockButtonStyle" TargetType="{x:Type Button}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=IsReadOnly}" Value="true">
                <DataTrigger.Setters>
                    <Setter Property="Content" Value="{StaticResource Lock}" />
                </DataTrigger.Setters>
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=IsReadOnly}" Value="false">
                <DataTrigger.Setters>
                    <Setter Property="Content" Value="{StaticResource Unlock}" />
                </DataTrigger.Setters>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
<Grid DataContext="{StaticResource TheViewModel}">
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
    </Grid.RowDefinitions>
    <Button x:Name="LockButton" Style="{StaticResource LockButtonStyle}" />
    <Button x:Name="ChangeIsReadOnly" Content="Change State" Grid.Row="1" Click="ChangeIsReadOnly_Click"/>
</Grid>

C# for the Window:

public MainWindow()
{
    InitializeComponent();
}
private void ChangeIsReadOnly_Click(object sender, RoutedEventArgs e)
{
    ViewModel theViewModel = (ViewModel)FindResource("TheViewModel");
    theViewModel.IsReadOnly = !theViewModel.IsReadOnly;
}

C# for the ViewModel class:

public class ViewModel : System.ComponentModel.INotifyPropertyChanged
{
    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    private bool isReadOnly;
    public bool IsReadOnly
    {
        get
        {
            return isReadOnly;
        }
        set 
        {
            isReadOnly = value;
            PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("IsReadOnly"));
        }
    }
}