Friday, September 23, 2011

Begin StoryBoard Animation Within A DataTemplate In Silverlight

Animations in Silverlight are a great way to add a dynamic feel to the aesthetics of your Silverlight page or control. Within Silverlight, using a DataTemplate to define a control’s properties and look when a control will be repeatedly used or displayed is the perfect solution. However if you add an Animation to the DataTemplate, trying to set it in motion from the code behind is not as straight forward as it initially seems.

Let's say you have a simple animation and you want it to run when the 'MouseEnter' event fires. In VB.NET, the traditional thinking is to go into this event that is exposed by the created DataTemplate and call 'MyAnimationStoryboard.Begin()'. Guess what though, the app will build, run, hit the event upon having the mouse enter, but the animation will not begin. No exception is thrown, it is just nothing happens.

It turns out that the StoryBoard is only locally known to the object containing it within the DataTemplate, so we must 1st access that control's resources where the storyboard exists, and then we will be able to begin the animation.

So here is a DataTemplate with a Grid control and a StoryBoard. The code is being kept simple because the solution for this is in the code behind.
<DataTemplate x:Key="MyTemplate">
<Grid Width="100" Height="100"
Opacity="0.75"
MouseEnter="MyGrid_MouseEnter" >
<Grid.Resources>
<Storyboard x:Name="MyTemplateAnimate">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[4].(GradientStop.Offset)"
Storyboard.TargetName="path">
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="0.296"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0.384"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0.475"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="0.529"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>

<Path x:Name="path" Data="M0,0L50,0L25,50L0,0L0,50L0,0">
<Path.Fill>
<LinearGradientBrush EndPoint="-0.419,0.662"
MappingMode="RelativeToBoundingBox"
StartPoint="1.051,-0.137">
<GradientStop Color="#FF18250A" Offset="1"/>
<GradientStop Color="#FF18250A"/>
<GradientStop Color="#FF345016" Offset="0.725"/>
<GradientStop Color="#FF345016" Offset="0.275"/>
<GradientStop Color="#FF779F4C" Offset="0.5"/>
</LinearGradientBrush>
</Path.Fill>
</Path>

<TextBlock x:Name="TextBlock1">
</TextBlock>
</Grid>
</DataTemplate>
The DataTemplate does expose the needed 'MouseEnter' event in the code behind but the problem is the StoryBoard is in the child 'Grid' control. Therefore we have (2) options: use the VisualTreeHelper class to find the right child control, or simple define a 'MouseEnter' event on the actual Grid control. I went for the latter option as it is the easiest.

Let's add the event we declared on the Grid in the XMAL named 'MyGrid_MouseEnter' to the code behind. We need to cast the sender which is the Grid itself, and then find the StoryBoard object within the Grid's resources. Once we have acquired and casted the exact StoryBoard, then we can call the .Begin() method.
Private Sub MyGrid_MouseEnter(sender As Object, e As System.Windows.Input.MouseEventArgs)
'Cast the sender to an object of type Grid, so we can find the StoryBoard
Dim MyTemplateGrid As Grid = DirectCast(sender, Grid)
If MyTemplateGrid IsNot Nothing Then
'Find the StoryBoard by name and then begin its animation sequence.
Dim StyBrd As Storyboard = TryCast(MyTemplateGrid.Resources("MyTemplateAnimate"), Storyboard)
StyBrd.Begin()
End If
End Sub
That's it, run the Silverlight app, and the StoryBoard within the DataTemplate will now run. If the controls in this example were not exactly what you have, the principal is you need to drill down to find the containing parent object of the StoryBoard to then get access to the storyboard. If you need, you can use the VisualTreeHelper to drill down to the proper child control. If you need a sample of using this class please refer to the following link:
http://forums.silverlight.net/t/99891.aspx

No comments:

Post a Comment