Monday, May 25, 2009

WPF triggers

Different Types of Triggers

A trigger is a collection of Setter objects  that get applied only when a given condition is true. WPF contains three main types of triggers: Trigger (also referred to as a property trigger), DataTrigger, and EventTrigger. It's also possible to respond to multiple simultaneous conditions by using the related MultiTrigger and MultiDataTrigger objects.

 

Style for a Button:

<Style TargetType="{x:Type Button}">
  <
Style.Triggers>
    <
Trigger Property="IsMouseOver" Value="True">
      <
Setter Property="Opacity" Value="0.7" />
      <
Setter Property="TextBlock.FontWeight" Value="Bold" />
   
Trigger>
 
Style.Triggers>
Style>

A data trigger allows you to trigger off of a property on your data item by leveraging a Binding. This is most often used in the context of a DataTemplate, but don't overlook the power of data triggers in other scenarios. For example, a data trigger is very handy for triggering off of a property on another object, as shown in this example:

<DataTemplate x:Key="MyItemTemplate">
  <
Border x:Name="root" BorderThickness="2" CornerRadius="6">
    <
TextBlock Margin="4" Text="{Binding XPath=@First}" />
 
Border>
  <
DataTemplate.Triggers>
    <
DataTrigger Value="True" Binding="{Binding Path=IsSelected,
       
 RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}">
      <
Setter TargetName="root" Property="BorderBrush" Value="Pink" />
   
DataTrigger>
 
DataTemplate.Triggers>
DataTemplate>

 

In this case, we are creating a DataTrigger that acts more like a property trigger by using a FindAncestor binding to get to a source object. Namely, we are triggering off of the IsSelected property on an ancestor ListBoxItem. This will allow us to draw a pink border around our template whenever it is selected. Now suppose our data item is a person and we only want a pink border when decorating a female. If the person is male, we instead want a blue border. We could achieve this by adding a second trigger, as shown in the following template:

<DataTemplate x:Key="MyItemTemplate">
  <
Border x:Name="root" BorderThickness="2" CornerRadius="6">
    <
TextBlock Margin="4" Text="{Binding XPath=@First}" />
 
Border>
  <
DataTemplate.Triggers>
    <
DataTrigger Value="True" Binding="{Binding Path=IsSelected,
       
 RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}">
      <
Setter TargetName="root" Property="BorderBrush" Value="Pink" />
   
DataTrigger>
    <
MultiDataTrigger>
      <
MultiDataTrigger.Conditions>
        <
Condition Binding="{Binding XPath=@Gender}" Value="Male" />
        <
Condition Value="True" Binding="{Binding Path=IsSelected,
            
RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
     
MultiDataTrigger.Conditions>
      <
Setter TargetName="root" Property="BorderBrush" Value="Blue" />
   
MultiDataTrigger>
 
DataTemplate.Triggers>
DataTemplate>

 

There is one more type of trigger called an EventTrigger. An event trigger, as the name implies, can be used to start an action in response to an event. More specifically, an event trigger executes in response to a routedevent. Here's a kaxample of a Button that uses an event trigger to begin an opacity animation when the Button is first loaded:

 

<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <
Button Opacity="0" Content="My Button">
    <
Button.Triggers>
      <
EventTrigger RoutedEvent="FrameworkElement.Loaded">
        <
BeginStoryboard Name="MyBeginAction">
          <
Storyboard>
            <
DoubleAnimation Storyboard.TargetProperty="Opacity"
               
 Duration="0:0:1" BeginTime="0:0:0.25" To="1.0" />
         
Storyboard>
       
BeginStoryboard>
     
EventTrigger>
   
Button.Triggers>
 
Button>
Canvas>

In this example, the event trigger is added directly to the Triggers collection of the framework element (the Button), itself. Again, the FrameworkElement.Triggers collection can only contain event triggers. If you wish to use property or data triggers, they must be within the Triggers collection of a style or template.

Note that you cannot trigger off of a standard CLR event because the mechanism for supporting event triggers depends on the WPF event routing engine, which intelligently routes events to only the elements that have registered to receive them.

With a property or data trigger, the condition that causes the trigger to be applied is based on state. As such, the condition will be true for a period of time. As long as the condition evaluates to true, the setters are applied to their target properties. When the condition later becomes false, the setters are no longer applied and the target properties fall back to their untriggered values.

There is no such concept of state with an event trigger. An event is fleeting. The event fires and then it's gone. Since the condition that invokes an event trigger is instantaneous, an EventTrigger does not contain a collection of setters.  Rather, it contains actions which execute in response to the event. These actions allow you to control (start, stop, pause, resume, etc) the execution of storyboards. The example above uses a BeginStoryboard action in response to the FrameworkElement.Loaded event.

You may also want to set properties based on the firing of an event. Even though you don't have setters, you can still use an EventTrigger to set property values. You achieve this by leveraging an object animation with a DiscreteObjectKeyFrame.