Defining storyboards and animations
in XAML is ostensibly easier than defining them in code, and for that
reason you’ll find the vast majority of Silverlight storyboards and
animations in XAML. But there are some issues involving the sharing of resources.
Let’s try to rewrite the ClickAndSpin program to use XAML for the storyboards and animations. The XamlClickAndSpin program has the following content area:
Example 1. Silverlight Project: XamlClickAndSpin File: MainPage.xaml (excerpt)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions>
<Button Name="btn1" Content="Button No. 1" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5 0.5" Click="OnButtonClick"> <Button.RenderTransform> <RotateTransform x:Name="rotate1" /> </Button.RenderTransform> </Button>
<Button Name="btn2" Content="Button No. 2" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5 0.5" Click="OnButtonClick"> <Button.RenderTransform> <RotateTransform x:Name="rotate2" /> </Button.RenderTransform> </Button>
<Button Name="btn3" Content="Button No. 3" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5 0.5" Click="OnButtonClick"> <Button.RenderTransform> <RotateTransform x:Name="rotate3" /> </Button.RenderTransform> </Button> </Grid>
|
This is basically the same as the previous version except that all the Button elements and all the RotateTransform objects have been given names for easy reference.
The storyboards and animations are defined in the Resources collection of the page:
Example 2. Silverlight Project: XamlClickAndSpin File: MainPage.xaml (excerpt)
<phone:PhoneApplicationPage.Resources> <Storyboard x:Name="storyboard1"> <DoubleAnimation Storyboard.TargetName="rotate1" Storyboard.TargetProperty="Angle" From="0" To="360" Duration="0:0:0.5" /> </Storyboard>
<Storyboard x:Name="storyboard2"> <DoubleAnimation Storyboard.TargetName="rotate2" Storyboard.TargetProperty="Angle" From="0" To="360" Duration="0:0:0.5" /> </Storyboard>
<Storyboard x:Name="storyboard3"> <DoubleAnimation Storyboard.TargetName="rotate3" Storyboard.TargetProperty="Angle" From="0" To="360" Duration="0:0:0.5" /> </Storyboard> </phone:PhoneApplicationPage.Resources>
|
Three buttons; three storyboards. Notice the attached properties:
<DoubleAnimation Storyboard.TargetName="rotate1"
Storyboard.TargetProperty="Angle"
From="0" To="360" Duration="0:0:0.5" />
To set the attached properties in code, you make calls to the static methods Storyboard.SetTarget and Storyboard.SetTargetProperty. In XAML you set the attached properties Storyboard.TargetName and Storyboard.TargetProperty. Notice the difference: The markup needs to reference a target object by name whereas code has access to the actual object itself.
Alternatively, the Angle property could be referenced through the Button object:
<DoubleAnimation Storyboard.TargetName="btn1"
Storyboard.TargetProperty="(Button.RenderTransform).(RotateTransform
.Angle)"
From="0" To="360" Duration="0:0:0.5" />
Or:
<DoubleAnimation Storyboard.TargetName="btn1"
Storyboard.TargetProperty="(Button.RenderTransform).Angle"
From="0" To="360" Duration="0:0:0.5" />
With this syntax, the RotateTransform objects do not require names.
Notice how the duration
is defined. At least three numbers are required: hours, minutes, and
seconds separated by colons. The seconds can have a fractional part. You
can also preface hours with a number of days and a period.
I used x:Name rather than x:Key with the Storyboard resources to make them easier to reference in code. The handler for the button’s Click event simply calls Begin on the appropriate object:
Example 3. Silverlight Project: XamlClickAndSpin File: MainPage.xaml.cs (excerpt)
void OnButtonClick(object sender, RoutedEventArgs args) { if (sender == btn1) storyboard1.Begin();
else if (sender == btn2) storyboard2.Begin();
else if (sender == btn3) storyboard3.Begin(); }
|
Simple enough. But I’m
probably not alone in desiring a way to define just one storyboard and
animation in XAML rather than three. It seems like it should be possible
to leave out the Storyboard.TargetName assignment in the XAML and call the Storyboard.SetTarget
method in code once you know what button is involved. But you can’t get
around the fact that resources are shared, and if a particular Storyboard and DoubleAnimation are associated with one Button, they can’t also be used with another Button. With one Storyboard and DoubleAnimation resource you couldn’t have two buttons spinning at the same time.
Even if you could assure yourself that one Button would be stopped before another Button would begin, you still have to make sure that the storyboard is stopped as well, which means you need to call Stop on the Storyboard object. (Besides Begin and Stop methods, the Storyboard class also defines Pause and Resume, but they’re not often used.)