Thursday, September 24, 2009

WPF Styles and Animations

I have been working with WPF lately and I’m really impressed with the style capabilities. From a high level our goal when using styles is to be able to set some standard properties of various controls one time in one place and have them affect many or all of the controls of that type in our window or application.

Let’s start with a really basic window and a hand type a few controls.



Code Snippet



  1. <Window x:Class="Lesson1Styles.Window1"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. Title="Window1" Height="300" Width="300">
  5. <Grid>
  6. <Label Margin="0,25,0,0" VerticalAlignment="Top" >
  7. Style Driven Label
  8. </Label>
  9. <TextBox Margin="0,100,0,0" VerticalAlignment="Top">
  10. Style Driven TextBox
  11. </TextBox>
  12. <Button Margin="0,175,0,0" VerticalAlignment="Top" >
  13. Style Driven Button
  14. </Button>
  15. </Grid>
  16. </Window>




One of the reasons for hand typing the controls is that I chose to only set the margin and alignment of the controls and I didn’t want the designer to add any height and width properties. If we run the application now here is what we see.

image

Now let’s create a style for each of our controls by adding a Window.Resources element.



Code Snippet



  1. <Window.Resources>
  2. <Style TargetType="TextBox">
  3. <Setter Property="Background" Value="Yellow"/>
  4. <Setter Property="Foreground" Value="Purple"/>
  5. <Setter Property="BorderBrush" Value="Purple"/>
  6. <Setter Property="Width" Value="135"/>
  7. <Setter Property="Height" Value="30"/>
  8. </Style>
  9. <Style TargetType="Label">
  10. <Setter Property="Foreground" Value="Purple"/>
  11. <Setter Property="Background" Value="Yellow"/>
  12. <Setter Property="Width" Value="135"/>
  13. <Setter Property="Height" Value="33"/>
  14. </Style>
  15. <Style TargetType="Button">
  16. <Setter Property="Background" Value="Yellow"/>
  17. <Setter Property="Foreground" Value="Purple"/>
  18. <Setter Property="Width" Value="135"/>
  19. <Setter Property="Height" Value="30"/>
  20. </Style>
  21. </Window.Resources>




Our new window now looks like this:

image

Note - I did not have to set a style property on any of the UI Elements. This is because I set the TargetType property in each style and the runtime knew to apply each style to their corresponding controls. Any label, button, or textbox you add to the window will inherit these styles unless you explicitly override the style or individual properties of the style.

It would be highly unusual (ok…ugly) for us to want our labels, buttons, and textboxes to have the same size and color scheme but if we did, it would be nice to not have to duplicate these attributes for each control.

With style inheritance we can do just that. We declare a base style with a TargetType of Control and move all of the common properties into this style. The following refactored code will render the window exactly as we see above.



Code Snippet



  1. <Window.Resources>
  2. <Style x:Key="baseStyle" TargetType="Control">
  3. <Setter Property="Background" Value="Yellow"/>
  4. <Setter Property="Foreground" Value="Purple"/>
  5. <Setter Property="BorderBrush" Value="Purple"/>
  6. <Setter Property="Width" Value="135"/>
  7. <Setter Property="Height" Value="30"/>
  8. </Style>
  9. <Style TargetType="TextBox" BasedOn="{StaticResource baseStyle}">
  10. </Style>
  11. <Style TargetType="Label" BasedOn="{StaticResource baseStyle}">
  12. </Style>
  13. <Style TargetType="Button" BasedOn="{StaticResource baseStyle}">
  14. </Style>
  15. </Window.Resources>




Let’s look at how we can override a portion of the style. If we want our textbox style to have a different height and width than the base style we can simply duplicate the height and width property setter in the style for the textbox and set the value to whatever we want. For example, if we change the textbox style as follows:



Code Snippet



  1. <Style TargetType="TextBox" BasedOn="{StaticResource baseStyle}">
  2. <Setter Property="Width" Value="60"/>
  3. <Setter Property="Height" Value="60"/>
  4. </Style>




Our window looks like this:

image

We can even override these properties at the control level. Let’s add a Height property to our textbox control.



Code Snippet



  1. <TextBox Margin="0,100,0,0" VerticalAlignment="Top" Width="200">
  2. Style Driven TextBox
  3. </TextBox>




Now when we run, the textbox width is based on the property we set in the control – we override the style defined for the Button which in turn overrides the style we have set for all Controls.

image

To close things out let’s take a look at adding triggers to a style. If we want all labels to change their background color when the mouse is over the label we can add a Dependency Property Trigger to the style.



Code Snippet



  1. <Style TargetType="Label" BasedOn="{StaticResource baseStyle}">
  2. <Style.Triggers>
  3. <Trigger Property="IsMouseOver" Value="True">
  4. <Setter Property="Background" Value="Gray"/>
  5. </Trigger>
  6. </Style.Triggers>
  7. </Style>




If we want to animate the textbox when the mouse is over the control we can add a Dependency Property Trigger with a StoryBoard and Animation defined. This animation changes the Font Size, height, width, and background color of the textbox.



Code Snippet



  1. <Style TargetType="TextBox" BasedOn="{StaticResource baseStyle}">
  2. <Setter Property="Width" Value="60"/>
  3. <Setter Property="Height" Value="60"/>
  4. <Style.Triggers>
  5. <Trigger Property="IsMouseOver" Value="True">
  6. <Trigger.EnterActions>
  7. <BeginStoryboard Name="story1">
  8. <Storyboard Duration="0:0:5">
  9. <DoubleAnimation Storyboard.TargetProperty="FontSize" To="20"/>
  10. <ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="Gray"/>
  11. <DoubleAnimation Storyboard.TargetProperty="Height" To="50"/>
  12. <DoubleAnimation Storyboard.TargetProperty="Width" To="270"/>
  13. </Storyboard>
  14. </BeginStoryboard>
  15. </Trigger.EnterActions>
  16. <Trigger.ExitActions>
  17. <StopStoryboard BeginStoryboardName="story1"/>
  18. </Trigger.ExitActions>
  19. </Trigger>
  20. </Style.Triggers>
  21. </Style>




Finally we’ll add a Routed Event trigger to the button just to demonstrate the different trigger options.



Code Snippet



  1. <Style TargetType="Button" BasedOn="{StaticResource baseStyle}">
  2. <Style.Triggers>
  3. <EventTrigger RoutedEvent="Mouse.MouseEnter" >
  4. <BeginStoryboard Name="story3">
  5. <Storyboard Duration="0:0:2">
  6. <DoubleAnimation
  7. Duration="0:0:0.2"
  8. Storyboard.TargetProperty="Height"
  9. To="90" />
  10. </Storyboard>
  11. </BeginStoryboard>
  12. </EventTrigger>
  13. <EventTrigger RoutedEvent="Mouse.MouseLeave" >
  14. <BeginStoryboard Name="story34">
  15. <Storyboard Duration="0:0:2">
  16. <DoubleAnimation
  17. Duration="0:0:0.2"
  18. Storyboard.TargetProperty="Height" />
  19. </Storyboard>
  20. </BeginStoryboard>
  21. </EventTrigger>
  22. </Style.Triggers>
  23. </Style>




We could do all of the styling and animation in code if we want but the good thing about doing this declaratively is if we are not happy with our Minnesota Viking look and feel, we can have a UI designer use a tool like Expression Blend to make things look nice without requiring that they understand code or XAML.

Try adding some other textboxes, labels, and buttons to see what style properties are inherited.

Here is the XAML in it’s entirety:



Code Snippet



  1. <Window x:Class="Lesson1Styles.Window1"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. Title="Window1" Height="300" Width="300">
  5. <Window.Resources>
  6. <Style x:Key="baseStyle" TargetType="Control">
  7. <Setter Property="Background" Value="Yellow"/>
  8. <Setter Property="Foreground" Value="Purple"/>
  9. <Setter Property="BorderBrush" Value="Purple"/>
  10. <Setter Property="Width" Value="135"/>
  11. <Setter Property="Height" Value="30"/>
  12. </Style>
  13. <Style TargetType="TextBox" BasedOn="{StaticResource baseStyle}">
  14. <Setter Property="Width" Value="60"/>
  15. <Setter Property="Height" Value="60"/>
  16. <Style.Triggers>
  17. <Trigger Property="IsMouseOver" Value="True">
  18. <Trigger.EnterActions>
  19. <BeginStoryboard Name="story1">
  20. <Storyboard Duration="0:0:5">
  21. <DoubleAnimation Storyboard.TargetProperty="FontSize" To="20"/>
  22. <ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="Gray"/>
  23. <DoubleAnimation Storyboard.TargetProperty="Height" To="50"/>
  24. <DoubleAnimation Storyboard.TargetProperty="Width" To="270"/>
  25. </Storyboard>
  26. </BeginStoryboard>
  27. </Trigger.EnterActions>
  28. <Trigger.ExitActions>
  29. <StopStoryboard BeginStoryboardName="story1"/>
  30. </Trigger.ExitActions>
  31. </Trigger>
  32. </Style.Triggers>
  33. </Style>
  34. <Style TargetType="Label" BasedOn="{StaticResource baseStyle}">
  35. <Style.Triggers>
  36. <Trigger Property="IsMouseOver" Value="True">
  37. <Setter Property="Background" Value="Gray"/>
  38. </Trigger>
  39. </Style.Triggers>
  40. </Style>
  41. <Style TargetType="Button" BasedOn="{StaticResource baseStyle}">
  42. <Style.Triggers>
  43. <EventTrigger RoutedEvent="Mouse.MouseEnter" >
  44. <BeginStoryboard Name="story3">
  45. <Storyboard Duration="0:0:2">
  46. <DoubleAnimation
  47. Duration="0:0:0.2"
  48. Storyboard.TargetProperty="Height"
  49. To="90" />
  50. </Storyboard>
  51. </BeginStoryboard>
  52. </EventTrigger>
  53. <EventTrigger RoutedEvent="Mouse.MouseLeave" >
  54. <BeginStoryboard Name="story34">
  55. <Storyboard Duration="0:0:2">
  56. <DoubleAnimation
  57. Duration="0:0:0.2"
  58. Storyboard.TargetProperty="Height" />
  59. </Storyboard>
  60. </BeginStoryboard>
  61. </EventTrigger>
  62. </Style.Triggers>
  63. </Style>
  64. </Window.Resources>
  65. <Grid>
  66. <Label Margin="0,25,0,0" VerticalAlignment="Top" >
  67. Style Driven Label
  68. </Label>
  69. <TextBox Margin="0,100,0,0" VerticalAlignment="Top" Width="200">
  70. Style Driven TextBox
  71. </TextBox>
  72. <Button Margin="0,175,0,0" VerticalAlignment="Top" >
  73. Style Driven Button
  74. </Button>
  75. </Grid>
  76. </Window>




1 comment:

  1. Nice tutorial at you can find themes/styles for WPF and Silverlight, check it out

    ReplyDelete