Animating a built-in button

Enrich your web page or Silverlight business application with Silverlight animations

(For more resources related to this topic, see here.)

Note that there might be more states to a fully functioning button, for example, there is also a Disabled state, but whether a button is disabled or not usually does not depend on mouse movements or positions, it does not have to be animated, and we do not describe it here. Our purpose in this section is not to create a fully functioning button, but rather to demonstrate some generic concepts for re-styling a control and providing custom animations for it.

Let's create a Silverlight project containing a single page with the button in its center. The following is the resulting XAML code of the main page:

<UserControl x:Class="AnimatingButtonStates.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Grid x:Name="LayoutRoot" Background="White"> <Button Width="100" Height="25" Background="LightGray" Content="Press Me" /> </Grid> </UserControl>

We want to re-style this button completely, modifying it shape, border, colors, and creating custom animations for the transition between states.

Now, we'll create a very simple custom template for the button by changing the button code to the following code:

<!-- Here we provide custom button template --> <Button> <Button.Template> <ControlTemplate TargetType="Button"> <Grid x:Name="TopLevelButtonGrid"> <!--Button Border--> <Border x:Name="ButtonBorder" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="5" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> </Border> <!-- button content is placed here--> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </ControlTemplate> </Button.Template> <Button>

If we run the code, as it is, we shall be able to see the button and its Press Me content, but the button will not react visually to mouse over or press events. That is because once we replace the button's template we will have to provide our own solution to the visual changes for different button states.

Now, let's discuss how we want the button to look in the different states and how we want it to handle the transitions between states.

When the mouse is over the button, we want a blue border to appear. The animation to achieve this can be fast or even instantaneous.

When the button is pressed, we want it to scale down significantly and we want the button to scale up and down several times, each time with lower amplitude before achieving a steady pressed state.

Note that the control template developers and designers usually try to avoid changing colors within animations (they are considered to be more complex and less intuitive); instead, they try to achieve color-changing effects by changing opacities of several template parts. So to change the border to blue on mouse over, let's create another border element MouseOverBorder with blue BorderBrush, and non-zero BorderThickness within the control template. At normal state, its opacity property will be 0, and it will be completely transparent. When the state of the button changes to MouseOver, the opacity of this border will change to 1.

After we add the MouseOverBorder element together with the visual state manager functionality, the resulting template code will look as follows:

<Button> <Button.Template> <ControlTemplate TargetType="Button"> <Grid x:Name="TopLevelButtonGrid"> <VisualStateManager.VisualStateGroups> <VisualStateGroup> <VisualStateGroup.Transitions> <!-- duration for the MouseOver animation is set here to 0.2 seconds --> <VisualTransition To="MouseOver" GeneratedDuration="0:0:0.2" /> </VisualStateGroup.Transitions> <VisualState x:Name="Normal" /> <VisualState x:Name="MouseOver"> <VisualState.Storyboard> <Storyboard> <!--animation performed when the button gets into "MouseOver" State--> <DoubleAnimation Storyboard. TargetName="MouseOverBorder" Storyboard.TargetProperty="Opacity" To="1" /> </Storyboard> </VisualState.Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <!--Button Border--> <Border x:Name="ButtonBorder" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="5" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> </Border> <!--MouseOverBorder has opacity 0 normally. Only when the mouse moves over the button, the opacity is changed to 1--> <Border x:Name="MouseOverBorder" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="5" BorderBrush="Blue" BorderThickness="2" Opacity="0"> </Border> <!-- button content is placed here--> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </ControlTemplate> </Button.Template> </Button>

Now, if we start the application, we'll see that the border of the button becomes blue, if the mouse pointer is placed over it, and returns to its usual color when the mouse pointer is moved away from the button, as shown in the following screenshot:

The next step is to animate the pressed state.

To achieve this, we add a ScaleTransform object to the top-level grid of the button's template:

<ControlTemplate TargetType="Button"> <Grid x:Name="TopLevelButtonGrid" RenderTransformOrigin="0.5,0.5"> <Grid.RenderTransform> <!-- scale transform is used to shrink the button when it is pressed --> <ScaleTransform x:Name="OnPressedScaleTransform" ScaleX="1" ScaleY="1" /> </Grid.RenderTransform> ...

The purpose of the ScaleTransform object is to shrink the button once it is pressed. Originally, its ScaleX and ScaleY parameters are set to 1, while the animation that starts when the button is pressed changes them to 0.5.

This animation is defined within VisualState defined as Pressed:

<VisualStateGroup> ... <VisualState x:Name="Pressed"> <VisualState.Storyboard> <Storyboard> <!-- animation performed when the button gets into "Pressed" State will scale down the button by a factor of 0.5 in both dimensions --> <DoubleAnimation Storyboard.TargetProperty="ScaleX" Storyboard.TargetName="TheScaleTransform" To="0.5" /> <DoubleAnimation Storyboard.TargetProperty="ScaleY" Storyboard.TargetName="TheScaleTransform" To="0.5" /> </Storyboard> </VisualState.Storyboard> </VisualState> ... </VisualStateGroup>

VisualState defines the animation storyboard to be triggered once the button switches to the Pressed state.

We can also add VisualStateTransition to the VisualStateGroup element's Transition property:

<VisualStateManager.VisualStateGroups> <VisualStateGroup> <VisualStateGroup.Transitions> ... <VisualTransition To="Pressed" GeneratedDuration="0:0:0.5"> <VisualTransition.GeneratedEasingFunction> <!-- elastic ease will provide a few attenuating bounces before the pressed button reaches a steady state --> <ElasticEase /> </VisualTransition.GeneratedEasingFunction> </VisualTransition> </VisualStateGroup.Transitions> ... </VisuateStateGroup> </VisualStateManager.VisualStateGroups>

VisualTransition elements allow us to modify the animation behavior depending on what the original and final states of the transition are. It has properties such as From and To for the purpose of specifying the original and final states. In our case, we set only its To property to Pressed, which means that it applies to transit from any state to the Pressed state. VisualTransition sets the duration of the animation to 0.5 second and adds the ElasticEase easing function to it, which results in the button size bouncing effect.

Once we started talking about easing functions, we can explain in detail how they work, and give examples of other easing functions.

Easing functions provide a way to modify Silverlight (and WPF) animations. A good article describing easing functions can be found at http://tinyurl.com/arbitrarypathanimations. The easing formula presented in this article is:

v = (V2 – V1)/T * f(t/T) + V1

Here v is the resulting animation value, t is the time parameter, T is the time period in question (either time between two frames in an animation with frames or time between the To and From values in the case of a simple animation), V2 and V1 are the animation values at the end and beginning of the animation correspondingly at the absence of easing, and f is the easing function. In the previous formula, we assumed a linear animation (not a spline one).

There are a bunch of built-in easing functions that come together with the Silverlight framework, for example, BackEase, BounceEase, CircleEase, and so on. For a comprehensive list of built-in easing functions, please check the following website: http://tinyurl.com/silverlighteasing.. Most easing functions have parameters described on this website. As an exercise you can change the easing function in the previous VisualTransition XAML code, modify its parameters, and observe the changes in button animation.

Summary

Here we have just learned how to animate a built-in button using Silverlight.

Resources for Article :


Further resources on this subject:


Books to Consider

comments powered by Disqus