Sep 9, 2013

WPF - Triggers

2 comments
In previous article, we learned Styles, Triggers are similar to styles, the difference is Styles are applied to controls unconditionally whereas Triggers as based on one or more conditions.

There are four types of Triggers,
  1. Property triggers are invoked when a dependency property changes. 
  2. Event triggers are invoked when a routed event is raised. 
  3. Data triggers are invoked when a regular .NET property changes. 
  4. Multi triggers (and MultiData triggers) represent a logical AND relationship between triggers.
1. Property triggers: A property trigger executes a collection of Setters when a specific dependency property changes to a specific value. When the dependency property no longer has the specified value, the Setters are undone with no additional code required.

For example, if we want to change the background of a TextBox, when it is moused-over we can simply do this,
<Grid>
    <Grid.Resources>
        <!-- Define a named style... -->
        <Style x:Key="TextBoxBaseStyle">
            <!-- with a triggers collection... -->
            <Style.Triggers>
                <!-- and a property trigger – when the textbox is moused-over... -->
                <Trigger Property="TextBox.IsMouseOver" Value="True">
                    <!-- set the background undefinedcolor) property  to LightGray -->
                    <Setter Property="TextBox.Background" Value="LightGray" />
                </Trigger>
                <!-- Note that we don’t have to define a property trigger for when 
                IsMouseOver is false, this is automatically handled by the framework -->
            </Style.Triggers>
            <!-- These Setters are not triggered by a dependency property --> 
            <Setter Property="Control.Height" Value="30" />
            <Setter Property="Control.Width" Value="60" />
        </Style>
    </Grid.Resources>

    <!-- Apply the style to this textbox -->
    <TextBox Style="{StaticResource TextBoxBaseStyle}" />
</Grid>

2. Event triggers: An event trigger executes a collection of Setters when a routed event is raised. Starting an animation is the most common action for an event trigger.
<Grid>
    <Grid.Resources>
        <!-- Define a named style... -->
        <Style x:Key="TextBoxBaseStyle">
            <!-- with a triggers collection -->
            <Style.Triggers>
                <!-- Define an event trigger that fires on mouseenter... -->
                <EventTrigger RoutedEvent="TextBox.MouseEnter">
                    <BeginStoryboard Storyboard="{StaticResource BeginAnimation}" />
                </EventTrigger>
                <!-- and another event trigger which fires on mouseleave -->
                <EventTrigger RoutedEvent="TextBox.MouseLeave">
                    <BeginStoryboard Storyboard="{StaticResource EndAnimation}" />
                </EventTrigger>
            </Style.Triggers>
            <!-- These Setters are not triggered by an event –>
            <Setter Property="Control.Height" Value="30" />
            <Setter Property="Control.Width" Value="60" />
        </Style>
        <Storyboard x:Key="BeginAnimation">
            <!-- In this animation we'll change the background's color property -->
            <ColorAnimation 
                Storyboard.TargetProperty="undefinedTextBox.Background).undefinedSolidColorBrush.Color)" 
                To="LightGray" Duration="0:0:0.1"/>
        </Storyboard>
        <Storyboard x:Key="EndAnimation">
            <!-- In this animation we'll change the background's color property -->
            <ColorAnimation 
                Storyboard.TargetProperty="undefinedTextBox.Background).undefinedSolidColorBrush.Color)" 
                To="White" Duration="0:0:0.1"/>
        </Storyboard>
    </Grid.Resources>

    <!-- Apply the style to this textbox -->
    <TextBox Style="{StaticResource TextBoxBaseStyle}" />
</Grid>

3. Data Triggers: Similar to Property triggers except that any .NET property can invoke these. Note that the Setters are still restricted to setting dependency properties.
<Grid>
    <Grid.Resources>
        <!-- Define a named style... -->
        <Style x:Key="TextBoxBaseStyle">
            <!-- with a triggers collection... -->
            <Style.Triggers>
                <!-- Define a data trigger that fires when the Role property is set to Admin -->
                <DataTrigger Binding="{Binding Path=Role}" Value="Admin">
                    <Setter Property="TextBox.Foreground" Value="Red" />
                </DataTrigger>
            </Style.Triggers>
            <Setter Property="Control.Height" Value="30" />
            <Setter Property="Control.Width" Value="60" />
        </Style>
    </Grid.Resources>

    <!-- Apply the style to this textbox -->
    <TextBox x:Name="MyTextBox" Style="{StaticResource TextBoxBaseStyle}" />
</Grid>

AND in the code behind,
public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
 
        // When the role is Admin the foreground text will be red colored.
        this.MyTextBox.DataContext = new Person { Role = "Admin" };

        // Otherwise the foreground text color is default (black)
        //this.MyTextBox.DataContext = new Person { Role = "User" };
    }
}

public class Person
{
    public string Role { get; set; }
}

4. Multi triggers (and MultiData triggers): Since Style.Triggers can contain multiple triggers, if we have the exact same Setters then a logical OR relationship forms between them.

This means that, “If IsMouseOver is true OR if IsFocused is true – set Background color to Red”. Correspondingly, a logical AND relationship is expressed using a Multi trigger (or MultiData trigger).
<Style.Triggers>
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition Property="TextBox.IsMouseOver" Value="True" />
            <!-- AND -->
            <Condition Property="TextBox.IsFocused" Value="True" />
        </MultiTrigger.Conditions>
        <Setter Property="TextBox.Background" Value="Red" />
     </MultiTrigger>
</Style.Triggers>

2 comments :