Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing style from inside a DataTrigger

Tags:

wpf

xaml

As often happens with WPF, I'm probably going about things the wrong way, but I have the following situation:

I would like to apply a style depending on a DataTrigger, but you can't change Style from inside a Style:

<Button>
   <Button.Style>
      <Style BasedOn="SomeStyle">
         <Style.Triggers>
            <DataTrigger ...>
               <Setter Property="Style" Value="OtherStyle" /> <---- NO CAN DO

Logical really, but what I want to avoid is duplication of the same setters, just because my trigger conditions change:

<Button>
   <Button.Style>
      <Style BasedOn="SomeStyle">
         <Style.Triggers>
            <DataTrigger Binding="{Binding X}" Value="Condition1">
               <Setter Property="A" Value="1" /> 
               <Setter Property="B" Value="1" /> 
               <etc...>
(...)

<Button>
   <Button.Style>
      <Style BasedOn="SomeStyle">
         <Style.Triggers>
            <DataTrigger Binding="{Binding X}" Value="Condition2">
               <Setter Property="A" Value="1" /> 
               <Setter Property="B" Value="1" /> 
               <etc...>

Is there something else inside which I could put the DataTrigger, thus allowing me to change the style from inside it? Or another way of: avoiding duplicating style info?

like image 651
Benjol Avatar asked Sep 10 '14 06:09

Benjol


1 Answers

Here is an example of how you may change the style of an element based on the data trigger:

<StackPanel>
    <Control Focusable="False">
        <Control.Template>
            <ControlTemplate>
                <!--resources-->
                <ControlTemplate.Resources>
                    <Style TargetType="Button" x:Key="primary">
                        <Setter Property="Content" Value="Primary style"/>
                    </Style>
                    <Style TargetType="Button" x:Key="secondary">
                        <Setter Property="Content" Value="Secondary style"/>
                    </Style>
                </ControlTemplate.Resources>
                <!--content-->
                <Button Style="{StaticResource primary}" x:Name="button"/>
                <!--triggers-->
                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource Self}}" Value="true">
                        <Setter TargetName="button" Property="Style" Value="{StaticResource secondary}"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding IsKeyboardFocusWithin,RelativeSource={RelativeSource Self}}" Value="true">
                        <Setter TargetName="button" Property="Style" Value="{StaticResource secondary}"/>
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Control.Template>
    </Control>
    <Button Content="A normal button"/>
</StackPanel>

In this example, Control is a wrapper element which will host the desired element in its control template. It is required to be in the control template if you wish to access it by name. A set of data triggers are defined in the control template's trigger, which will apply the style on the desired element (button) as needed.

I did not find a way to avoid duplicate setters. Perhaps the ability to swap/apply the style may help you achieve the same result.

If you do not wish to go for the control template approach, you may make use of attached properties or an unused Tag property in the element. In this example, we will take advantage of two way binding to achieve the same result.

Example:

<StackPanel>
    <Grid>
        <!--content-->
        <Button Style="{Binding Tag,RelativeSource={RelativeSource FindAncestor,AncestorType=Grid}}"/>
        <Grid.Style>
            <Style TargetType="Grid">
                <!--resources-->
                <Style.Resources>
                    <Style TargetType="Button" x:Key="primary">
                        <Setter Property="Content" Value="Primary style"/>
                    </Style>
                    <Style TargetType="Button" x:Key="secondary">
                        <Setter Property="Content" Value="Secondary style"/>
                    </Style>
                </Style.Resources>
                <Setter Property="Tag" Value="{StaticResource primary}"/>
                <!--triggers-->
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource Self}}" Value="true">
                        <Setter Property="Tag" Value="{StaticResource secondary}"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding IsKeyboardFocusWithin,RelativeSource={RelativeSource Self}}" Value="true">
                        <Setter Property="Tag" Value="{StaticResource secondary}"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Grid.Style>
    </Grid>
    <Button Content="A normal button"/>
</StackPanel>

Try it and see if this helps you to achieve the desired result.

like image 110
pushpraj Avatar answered Sep 28 '22 12:09

pushpraj