Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Tutorial - Styles, Triggers & Animation

0.00/5 (No votes)
28 Dec 2010 4.2K  
In this article, I have specified how you could use Style, Triggers and animation in your WPF application to make your application more attractive, interactive and user friendly.

Table of Contents

Introduction

Perhaps the most interesting and most important feature for any WPF application is Styling. Styling means defining styles for controls, and store in reusable ResourceDictionaries and hence forth, it could be used later on by calling its name. Styles in WPF could be well compared with CSS styles. They are both similar in most of the cases, while the former extends the feature allowing most of the features which WPF have. In this article I will discuss mostly how you could use Style in your WPF application to enhance the Rich experience of your UI.

Before we go on further with styles, lets jot down what we have already discussed so far.

So basically if you have already gone through the articles in the series, you must have already know most of the things on how you could apply your styles, how could you define style objects etc. In this article I will discuss how you could write better styles for your application.

Style

WPF exposes a property Style for every Control. If you look into the object Hierarchy, the Style is basically a property which exposes an object of Style in FrameworkElement. So each object can associate it and define custom setters to manipulate the basic look and feel of a control.

WPFClassHierarchyStyle.JPG

    Clearly, the above diagram shows the association of Style in FrameworkElement and from the object hierarchy every control somehow inherits from FrameworkElement and hence style will be available to it. Style is also a WPF object which is inherited form DispatcherObject which helps in setting different properties of your UI Element.

    How Style differs from Theme ?

    Before we move further into Styles lets talk about Themes. Theme is totally different from Styles. Themes are defined at OS level, or more precisely a Theme can take part of delivering styles all over the Desktop while Styles are restricted to the contextual area of a WPF window. WPF are capable of retrieving the color scheme which is defined in OS level. Say for instance, if you do not define style for your application, the elements in the screen will automatically get styles from external environment. Say for instance, in XP if you change the theme to something else you would see that the buttons, TextBox on your WPF window will change its color instantly. You can even set the Theme which the application would use programmatically [^] from your code.

    What about Templates ?

    Every control defines a ControlTemplate. A ControlTemplate defines the overall structure of the control. As I have already told you, say for instance you have a Button. Button is a control that is made up of more than one control. It would have a ContentPresenter which writes the Text over the control, it would have a Rectangle which keeps the boundary of the Button etc. So Template is a special property associated with a Control which specifies how the control will look like structurally. We can easily define our Template and change the overall structure of a control.

    Templates are basically of 2 types :

    1. ControlTemplate
    2. DataTemplate 

    ControlTemplate defines the structure of the Control. It means say for instance, you define the ControlTemplate for a ComboBox. So from ControlTemplate you can easily change the ToggleButton associated with the ComboBox which opens the DropDown, you can change the structure of the TextBox, the Popup etc. So ControlTemplate allows you to change the overall structure of the Control.

    Each control is made up of Data. Say for instance a ItemsControl contains a number of Data Element which builds the items inside the Popup. The DataTemplate could be associated with ItemsTemplate and will build up the Data Block for the ComboBox.

templates.JPG

    So, you should always remember, ControlTemplate defines the whole Control while the DataTemplate defines each individual Data Element.

    How to define Style?

    Normally a style is an unique object which is used to style WPF controls. Each WPF element contains a number of Dependency Properties. A dependency property defines the basic behavior and look of the control in UI. Styles maintains a collection of Setters which enumerates a DependencyProperty with its value.

    Thus you can say a style is a collection of DependencyProperty settings which when applied on a Target will change the behavior of it.

    Let us suppose you are going to style a TextBox.

textbox_style.JPG
    <TextBox Text="This is a TextBox without Styles" 
                HorizontalAlignment="Center" 
                VerticalAlignment="Center" 
                CharacterCasing="Lower" 
                FlowDirection="RightToLeft"
                FontSize="20" 
                FontWeight="UltraBlack"
                Width="400"
                Height="40">
        <TextBox.Background>
            <LinearGradientBrush>
                <GradientStop Color="Cyan" Offset="0.0"/>
                <GradientStop Color="Yellow" Offset="0.5"/>
                <GradientStop Color="Red" Offset="1.0"/>
            </LinearGradientBrush>
        </TextBox.Background>
        <TextBox.Foreground>
            <SolidColorBrush Color="Black"/>
        </TextBox.Foreground>
        <TextBox.Effect>
            <DropShadowEffect BlurRadius="40" Color="Maroon"
               Direction="50" Opacity="0.5"/>
        </TextBox.Effect>
    </TextBox>

    So I have just designed a TextBox in the above code. The XAML looks straight forward, where I have configured different properties of the TextBox control to create my stylish TextBox. But looking at the code, you might wonder how difficult it would be if you need to redo the same thing again and again for every TextBox you define in your application. This is what the problem is. So WPF comes with an alternative with style. A style is an object that holds this behaviors into a collection of Setters. So lets redefine the same with Styles.

textbox_style2.JPG
    <TextBox>
        <TextBox.Style>
            <Style TargetType="{x:Type TextBox}">
                <Setter Property="Text" Value="This is a TextBox with Styles"/>
                <Setter Property="HorizontalAlignment" Value="Center"/>
                <Setter Property="VerticalAlignment" Value="Center"/>
                <Setter Property="CharacterCasing" Value="Lower"/>
                <Setter Property="FlowDirection" Value="RightToLeft"/>
                <Setter Property="FontSize" Value="20"/>
                <Setter Property="FontWeight" Value="UltraBlack"/>
                <Setter Property="Width" Value="400"/>
                <Setter Property="Height" Value="40"/>
                <Setter Property="Background">
                    <Setter.Value>
                            <LinearGradientBrush>
                            <GradientStop Color="Cyan" Offset="0.0"/>
                            <GradientStop Color="Yellow" Offset="0.5"/>
                            <GradientStop Color="Red" Offset="1.0"/>
                        </LinearGradientBrush>
                    </Setter.Value>
                </Setter>
                <Setter Property="Foreground">
                    <Setter.Value>
                        <SolidColorBrush Color="Black"/>
                    </Setter.Value>
                </Setter>
                <Setter Property="Effect" >
                    <Setter.Value>
                        <DropShadowEffect BlurRadius="40" 
                         Color="Maroon" Direction="50" Opacity="0.5"/>
                    </Setter.Value>
                </Setter>
            </Style>
        </TextBox.Style>
    </TextBox>

    So you can see, I have defined the Style inside the TextBox and the textbox looks almost the same. The Setters allows you to enumerate all the properties for the TextBox and produced a style inside it whose TargetType is set to {x:Type Button}

    Now how this style can be made reusable for many controls ? Yes, this might be your first question that arose in your mind. Yes, if you have read my previous articles, you should already know the use of ResourceDictionaries. So in our case I will shift the style to Resource section for the Window and reuse the code just by calling the Resource key from the Textbox.

    <Grid>
        <Grid.Resources>
            <ResourceDictionary>
                <Style TargetType="{x:Type TextBox}" x:Key="MyTextBoxStyle">
                    <Setter Property="Text" Value="This is a TextBox with Styles"/>
                    <Setter Property="HorizontalAlignment" Value="Center"/>
                    <Setter Property="VerticalAlignment" Value="Center"/>
                    <Setter Property="CharacterCasing" Value="Lower"/>
                    <Setter Property="FlowDirection" Value="RightToLeft"/>
                    <Setter Property="FontSize" Value="20"/>
                    <Setter Property="FontWeight" Value="UltraBlack"/>
                    <Setter Property="Width" Value="400"/>
                    <Setter Property="Height" Value="40"/>
                    <Setter Property="Margin" Value="0,20,0,10" />
                    <Setter Property="Background">
                        <Setter.Value>
                            <LinearGradientBrush>
                                <GradientStop Color="Cyan" Offset="0.0"/>
                                <GradientStop Color="Yellow" Offset="0.5"/>
                                <GradientStop Color="Red" Offset="1.0"/>
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Foreground">
                        <Setter.Value>
                            <SolidColorBrush Color="Black"/>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Effect" >
                        <Setter.Value>
                            <DropShadowEffect BlurRadius="40" 
                                 Color="Maroon" Direction="50" Opacity="0.5"/>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ResourceDictionary>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBox Style="{StaticResource MyTextBoxStyle}" Grid.Row="0" />
        <TextBox Style="{StaticResource MyTextBoxStyle}" Grid.Row="1" 
                    Text="The Style is modified here"
                    FlowDirection="LeftToRight"/>
    </Grid>
textbox_style3.JPG

    So here I have shifted the Style into Resource section and used MyTextBoxStyle key to refer for each TextBox i defined. Notably, the style of both the textboxes remains same, while you can see I have also overridden certain settings in the control itself and it works the same. I have modified the Text of the 2nd TextBox to "The Style is modified here" and also made the FlowDirection to LeftToRight.

    Another important thing, that you should always keep into mind, that if you do not define the Key element for the Style in Resource section, it will automatically be applied to all the TextBox you define.

    <Style TargetType="{x:Type TextBox}">
    </Style>

    Say the style you define does not contain any Key. So all the TextBoxes will automatically apply the style when appeared. You can eventually use

    <TextBox Style="{x:Null}"/>

    to revert the style.

    Members of Style

    style_snap.JPG

    The styling of WPF controls is made up with the help of a class called Style. The style object exposes few properties which help you to define various behavior. Lets look into the properties:

    • Resources : It holds the reference for the ResourceDictionary where the Style is defined.
    • Setters : It is a collection which holds all the DependencyProperty configuration for the whole control.
    • TargetType : TargetType defines the type of the control for which the Style can be applied. So based on the TargetType the Style setters are defined to. So if you define a style for TextBox you cannot use Content as property Setter.
    • BasedOn : This is used to allow Style inheritance. You can use an existing style key to inherit all the properties to a new Style.
    • Triggers : A collection of Setters which would be applied based on certain conditions.

    Using those properties you can define your own styles.

    What about Explicit and Implicit Styles ?

    WPF controls can have two type of styles associated with it. A control can have a style defined in the application and applied to its Style property. If your control is using a Style to define its look and feel or basically your control has set an object of Style into its Style property, then it is using an Explicit Style.

    On the other hand, if your control takes the style from external environment (Theme) and the Style property is set to Null, then your control is using Implicit Style. Basically any WPF control automatically defines a DefaultStyle for it, so that you can set only the portion of the control which you need to change.

    Say for instance, you have a Button. If you want to have its Text to be colored Red, you just need to change the Foreground of the Button. You need not to define the whole style. If there is no Default Style defined for Buttons, you need to define all the properties individually to make it appear. Thus the default color of the Text is Black if not defined otherwise.

    Triggers

    Triggers are a set of styles that work on a particular condition. You can think Trigger as a part of Style which will be set only when the Condition defined for the Trigger is met.

    There are few types of Triggers :

    1. Property Trigger : Will be set only when the DependencyProperty of a certain object has been set to a Value.
    2. Data Trigger : Will work for any normal Properties using on Binding.
    3. Event Trigger : Will work only when some event is triggered from the control.

    Now to demonstrate let us look into the code below :

    <Style TargetType="{x:Type TextBox}" x:Key="MyTextBoxStyle">
        <Setter Property="Text" Value="This is a TextBox with Styles"/>
        <Setter Property="HorizontalAlignment" Value="Center"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="CharacterCasing" Value="Lower"/>
        <Setter Property="FlowDirection" Value="RightToLeft"/>
        <Setter Property="FontSize" Value="20"/>
        <Setter Property="FontWeight" Value="UltraBlack"/>
        <Setter Property="Width" Value="400"/>
        <Setter Property="Height" Value="40"/>
        <Setter Property="Margin" Value="0,20,0,10" />
        <Setter Property="Background">
            <Setter.Value>
                <LinearGradientBrush>
                    <GradientStop Color="Cyan" Offset="0.0"/>
                    <GradientStop Color="Yellow" Offset="0.5"/>
                    <GradientStop Color="Red" Offset="1.0"/>
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="Foreground">
            <Setter.Value>
                <SolidColorBrush Color="Black"/>
            </Setter.Value>
        </Setter>
        <Setter Property="Effect" >
            <Setter.Value>
                <DropShadowEffect BlurRadius="40" Color="Maroon" Direction="50"
                             Opacity="0.5"/>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsFocused" Value="True">
                <Setter Property="Effect">
                    <Setter.Value>
                        <DropShadowEffect BlurRadius="40" Color="Red" 
                              Direction="50" Opacity="0.9"/>
                    </Setter.Value>
                </Setter>
            </Trigger>
            <MultiTrigger>
                <MultiTrigger.Conditions>
                    <Condition Property="IsFocused" Value="True"/>
                    <Condition Property="IsMouseOver" Value="True"/>
                </MultiTrigger.Conditions>
                <Setter Property="Effect">
                    <Setter.Value>
                        <DropShadowEffect BlurRadius="40" Color="Violet" 
                                Direction="50" Opacity="0.9"/>
                    </Setter.Value>
                </Setter>
                <Setter Property="Foreground" Value="White" />
                <Setter Property="Background" Value="Maroon" />
            </MultiTrigger>
        </Style.Triggers>
    </Style>

    Here you can see I have used Property Trigger to change the DropShadowEffect of TextBox when it is focussed. Every WPF control exposes few properties to work with Property Triggers, which will be set to true based on the control appearance changes. You can use these properties like IsFocused, IsMouseDown etc to work around with Property Triggers.

    On the second occasion, I have defined a MultiTrigger. MultiTrigger allows you to mention Condition, so that when all the conditions of the MultiTrigger is met, the Property Setters for the object is applied.

styletrigger.JPG

    So you can see when you hover your mouse over the TextBox and your textbox has its focus in it, only then you see the TextBox to appear in Maroon background and Violet DropShadow effect.

    Animation Basics

    Another interesting thing that you might think very interesting is the support of Animation for WPF. Basically, by the word Animation, we generally think of large Texture graphics in 3D space, which would probably be created in 3DS MAX studio or MAC etc. But believe me there is nothing to worry about this in case of WPF. WPF simplifies the concept Animation to be the change of a property over time.

    Say for instance, say you want your textbox to change its color over time, you would write a simple color animation to do this or say you want to change the Opacity of a Border element during time, you need DoubleAnimation to do this. Animation is cool if you are clear about how it works.

    Type of Animation

    I must say, don't make yourself more confused by seeing the types of Animation. Animation is actually categorized in the same way as you categorize variables. Say for instance :

    1. DoubleAnimation : This will animate a Double Value from one value to another. So if you want to change the Width of a TextBox over time you need DoubleAnimation.
    2. ColorAnimation : Same as the above if the type of Changing element is Color, you need ColorAnimation.
    3. SingleAnimation, RectAnimation, PointAnimation, Int32Animaition, ThicknessAnimation etc each of them bears the same meaning.

    So basically the basis of Animation types is based on the type of the property for which you want your animation to work on.

    Animation can also be categorized into two basic ways :

    1. Animation Without KeyFrames : These are animation that only needs two values, From and To. It gives you a smooth animation based on the Timeline.DesiredFramerate property for the animation.
    2. Animation With KeyFrames : Allows you to specify a KeyFrame collection which lets you define the KeyFrame value on a specified time. So that you can adjust your own animation based on specific time intervals.

    Let us take a look at a few examples to make you understand animation feature of WPF:

    <Window.Triggers>
            <EventTrigger RoutedEvent="Loaded">
                <BeginStoryboard>
                    <Storyboard RepeatBehavior="Forever">
                        <DoubleAnimation Storyboard.TargetProperty="Width" 
          From="300" To="200" AutoReverse="True" Duration="0:0:5" ></DoubleAnimation>
                        <DoubleAnimation Storyboard.TargetProperty="Height" 
          From="300" To="200" AutoReverse="True" Duration="0:0:5"></DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Window.Triggers>

    In the above code, I have defined an EventTrigger which lets you have a DoubleAnimation(as Width is double value) on Width of the Window. We use Loaded Event to start a StoryBoard.

    What is a StoryBoard ?

    A StoryBoard can be defined as a Container for TimeLines or a collection of animation timelines for which the object specified in Target will animate. We use StoryBoard to specify Animation within it.

    Few important properties of StoryBoard :

    • RepeatBehaviour : Specifies the number of times for which the StoryBoard repeat the animation.
    • Target : Specifies the Target for which the storyboard will be applied to.
    • TargetName : Defines the target and reference it by its name attribute.
    • TargetProperty : Specifies the property for which the animation will be applied for.
    • AccelerationRatio / DecelerationRatio : Defines the acceleration or deceleration for the animation.
    • AutoReverse : Defines whether the StoryBoard will be reversed automatically. This is really cool concept, which allows you to get the reverse of the storyboard timeline automatically generated by the WPF.

    Animation can also be applied from code.

    DoubleAnimation myDoubleAnimation = new DoubleAnimation();
    myDoubleAnimation.From = 1.0;
    myDoubleAnimation.To = 0.0;
    myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(5));

    In the above code I have declared a DoubleAnimation which starts From 1.0 and moves to 0.0 in 5 seconds.

    Animation with KeyFrames

    Animation can be defined either using KeyFrames or without KeyFrames. KeyFrame allows you to define an intermediate frame so that the animation occurs for each individual frame intervals. There are three types of interpolation for an AnimationwithKeyFrames.

    1. Linear
    2. Discrete
    3. Spline

    Linear

    Lets create an animation using KeyFrames :

    <Border Background="Violet" 
                  HorizontalAlignment="Stretch"
                  VerticalAlignment="Stretch" >
                <Border.Triggers>
                    <EventTrigger RoutedEvent="Border.MouseLeftButtonDown">
                        <EventTrigger.Actions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimationUsingKeyFrames 
                                          Storyboard.TargetName="transformObj"
                                          Storyboard.TargetProperty="X"
                                          Duration="0:0:15">
                                        <LinearDoubleKeyFrame Value="500"
                    KeyTime="0:0:3" />
                                        <LinearDoubleKeyFrame Value="50" 
                    KeyTime="0:0:7" />
                                        <LinearDoubleKeyFrame Value="300" 
                    KeyTime="0:0:13" />
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger.Actions>
                    </EventTrigger>
                </Border.Triggers>
                <Border.RenderTransform>
                    <TranslateTransform x:Name="transformObj" X="0" Y="0" />
                </Border.RenderTransform>
            </Border>

    Here the animation is applied as LinearDoubleKeyFrame, which means the animation would be smooth while we define each KeyFrame value based on KeyTime. Here we change the Translation of the Border based on different KeyTime specified such that on 3rd second, the Rectangle will move to 500, at 7th second it will be at 50 and at 13th second it will be at 300. The animation is LinearDouble so the animation is smooth and steady.

    Discrete

    If I change the animation to DiscreteAnimation it will place the object only at the KeyTime specified

    <DoubleAnimationUsingKeyFrames 
            Storyboard.TargetName="transformObj"
            Storyboard.TargetProperty="X"
            Duration="0:0:15">
        <DiscreteDoubleKeyFrame Value="500" KeyTime="0:0:3" />
        <DiscreteDoubleKeyFrame Value="50" KeyTime="0:0:7" />
        <DiscreteDoubleKeyFrame Value="300" KeyTime="0:0:13" />
    </DoubleAnimationUsingKeyFrames>

    Thus changing the LinearDouble with DiscreteDouble makes it change its position all of a sudden based on the KeyTime specified for the animation.

    Spline

    SplineAnimation is used to define more realistic animation behavior for your control. It lets you control acceleration and deceleration of the animation. With KeySpline you can define the the cubic bazier curve using Spline Key frame. Lets look at the example

    <DoubleAnimationUsingKeyFrames 
            Storyboard.TargetName="transformObj"
            Storyboard.TargetProperty="X"
            Duration="0:0:15">
        <SplineDoubleKeyFrame Value="500" KeyTime="0:0:3" KeySpline="0.0,0.1 0.1,0.1" />
        <SplineDoubleKeyFrame Value="50" KeyTime="0:0:7" KeySpline="0.0,0.1 0.1,0.1"/>
        <SplineDoubleKeyFrame Value="300" KeyTime="0:0:13" KeySpline="0.0,0.1 0.1,0.1"/>
    </DoubleAnimationUsingKeyFrames>

    Thus you can see KeySpline allows you to define a smooth animation that starts with acceleration and with highest speed in the middle and ultimately decelerates back again.

    You can also use EasingFunction to specify the custom formulas for the animation. For further information try : KeyFrameAnimation [^]

    Conclusion

    I hope you like my article. I am also happy that I have covered almost most of the topics that you need to know in WPF. There are still a few things left behind, which I would cover in my next article. Please put your valuable feedback for the article.

    Thank you for reading.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here