devielu
devielu

Reputation: 198

Binding Data to Button Style with DataTrigger

I'm building a menu with WPF but I want to simplify the code creating a new style. At the moment, the menu is working great:

<Button Name="btnMenu1" Grid.Row="0" Content="Button One" Click="BtnMenu1_Click">
    <Button.Style>
        <Style TargetType="Button">
            <Style.Triggers>
                <DataTrigger Binding="{Binding [0].Selected}" Value="True">
                    <Setter Property="Foreground" Value="Yellow" />
                </DataTrigger>
                <DataTrigger Binding="{Binding [0].Selected}" Value="False">
                    <Setter Property="Foreground" Value="Blue" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>
<Button Name="btnMenu2" Grid.Row="1" Content="Button Two" Click="BtnMenu2_Click">
    <Button.Style>
        <Style TargetType="Button">
            <Style.Triggers>
                <DataTrigger Binding="{Binding [1].Selected}" Value="True">
                    <Setter Property="Foreground" Value="Yellow" />
                </DataTrigger>
                <DataTrigger Binding="{Binding [1].Selected}" Value="False">
                    <Setter Property="Foreground" Value="Blue" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

Imagine that I need to add 10 or 15 buttons to the menu, can you imagine the amount of code? My idea is to simplify the code:

<Button Name="btnMenu1" Grid.Row="0" Style="{StaticResource StyleButtonMenu}" Content="Button One" Click="BtnMenu1_Click"/>
<Button Name="btnMenu2" Grid.Row="1" Style="{StaticResource StyleButtonMenu}" Content="Button Two" Click="BtnMenu2_Click"/>
<Button Name="btnMenu3" Grid.Row="2" Style="{StaticResource StyleButtonMenu}" Content="Button Three" Click="BtnMenu3_Click"/>
<Button Name="btnMenu4" Grid.Row="3" Style="{StaticResource StyleButtonMenu}" Content="Button Four" Click="BtnMenu4_Click"/>
<Button Name="btnMenu5" Grid.Row="4" Style="{StaticResource StyleButtonMenu}" Content="Button Five" Click="BtnMenu5_Click"/>

And the code style could be something like this:

<Style x:Key="StyleButtonMenu" TargetType="Button">
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="Cursor" Value="Hand" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding Selected}" Value="True">
            <Setter Property="Foreground" Value="Yellow"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Selected}" Value="False">
            <Setter Property="Foreground" Value="Blue"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

My problem here is: How can I work with Bindings in DataTrigger to treat the property Selected? I have a List with properties and when some property change, I need to update UI. As I said at the top, the code works great, I just want to create a generic style and treat bindings in DataTrigger.

Upvotes: 0

Views: 1099

Answers (1)

thatguy
thatguy

Reputation: 22079

For your pretty static example, your could bind the Tag in your style using a DataTrigger.

<Style x:Key="StyleButtonMenu" TargetType="Button">
   <Setter Property="BorderThickness" Value="0" />
   <Setter Property="Cursor" Value="Hand" />
   <Setter Property="Foreground" Value="Blue"/>
   <Style.Triggers>
      <DataTrigger Binding="{Binding Tag.Selected, RelativeSource={RelativeSource Self}}" Value="True">
         <Setter Property="Foreground" Value="Yellow"/>
      </DataTrigger>
   </Style.Triggers>
</Style>

Then, you could bind the concrete data context to the Tag of each button.

<Button Name="btnMenu1" Grid.Row="0" Tag="{Binding [0]}" Style="{StaticResource StyleButtonMenu}" Content="Button One" Click="BtnMenu1_Click"/>
<Button Name="btnMenu2" Grid.Row="1" Tag="{Binding [1]}" Style="{StaticResource StyleButtonMenu}" Content="Button Two" Click="BtnMenu2_Click"/>
<Button Name="btnMenu3" Grid.Row="2" Tag="{Binding [2]}" Style="{StaticResource StyleButtonMenu}" Content="Button Three" Click="BtnMenu3_Click"/>
<Button Name="btnMenu4" Grid.Row="3" Tag="{Binding [3]}" Style="{StaticResource StyleButtonMenu}" Content="Button Four" Click="BtnMenu4_Click"/>
<Button Name="btnMenu5" Grid.Row="4" Tag="{Binding [4]}" Style="{StaticResource StyleButtonMenu}" Content="Button Five" Click="BtnMenu5_Click"/>

However, there is a better way. Whenever you try to display a collection of data items, consider using an ItemsControl or any derived type that is appropriate for your use-case. Here, you could use an ItemsControl with a data template, since you do not need selection or anything special.

<Style x:Key="MenuButtonStyle" TargetType="Button">
   <Setter Property="BorderThickness" Value="0" />
   <Setter Property="Cursor" Value="Hand" />
   <Setter Property="Foreground" Value="Blue"/>
   <Style.Triggers>
      <DataTrigger Binding="{Binding Selected}" Value="True">
         <Setter Property="Foreground" Value="Yellow"/>
      </DataTrigger>
   </Style.Triggers>
</Style>

<DataTemplate x:Key="MenuItemTemplate">
   <Button Style="{StaticResource MenuButtonStyle}"
           Content="{Binding Name}"
           Command="{Binding DoSomething}"
           CommandParameter="{Binding}">
   </Button>
</DataTemplate>

This data template assumes that your menu item data types contain a Name, DoSomething and the Selected property from your question. Depending on your requirements, the DoSomething command might also be definded on the parent data context. This command would handle the button click.

Instead of a Grid with hard-coded buttons, you can now bind the items collection (here MenuItems) to an ItemsControl with the aforementioned data template for your items.

<ItemsControl ItemsSource="{Binding MenuItems}"
              ItemTemplate="{StaticResource MenuItemTemplate}"/>

Upvotes: 1

Related Questions