Pitfall with using triggers in Style and ControlTemplate

Using triggers, including datatriggers, in Style and ControlTemplate can be sometimes tricky. Check out the following two code snippets.

<Grid Background=”Red”>
<Grid.Style>
<Style TargetType=”{x:Type Grid}”>
<Style.Triggers>
<Trigger Property=”IsMouseOver” Value=”True”>
<Setter Property=”Background” Value=”Green”/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>

<Button Content=”Test” Foreground=”Red”>
<Button.Template>
<ControlTemplate TargetType=”{x:Type Button}”>
<Grid>
<ContentPresenter/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property=”IsMouseOver” Value=”True”>
<Setter Property=”Foreground” Value=”Green”/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>

Neither of them will work as expected, although they look fine. The reason here is that the property on the FrameworkElement overwrites the setters in Style or ControlTemplate, Therefore, any change caused by setters will not actually bring any change.  The following is the order in which WPF assign a value to a property.

1. The default value (as set by the FrameworkPropertyMetadata object)
2. The inherited value (if the FrameworkPropertyMetadata.Inherits flag is set and a value has been applied to an element somewhere up the containment hierarchy)
3. The value from a theme style (as discussed in Chapter 15)
4. The value from a project style (as discussed in Chapter 12)
5. The local value (in other words, a value you’ve set directly on this object using code or XAML)

To fix the above samples, the solution is quite as easy as the following

<Grid>
<Grid.Style>
<Style TargetType=”{x:Type Grid}”>
<Setter Property=”Background” Value=”Red”/>
<Style.Triggers>
<Trigger Property=”IsMouseOver” Value=”True”>
<Setter Property=”Background” Value=”Green”/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>

As you can see, instead directly assign a value to the property, use setter in Style, whoese value can be overrwritten when the trigger executes its setter.

Another solution is to use setters on higher level, with TargetName property targetting the element. See following.

<Button>
<Button.Template>
<ControlTemplate TargetType=”{x:Type Button}”>
<Grid>
<TextBlock x:Name=”textblock” Text=”Test” Foreground=”Red”/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property=”IsMouseOver” Value=”True”>
<Setter Property=”Foreground” TargetName=”textblock” Value=”Green”/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>

~ by Martin on December 12, 2008.

Leave a Reply