
Dynamically Changing TargetName
The most common scenario for dynamically changing the Storyboard..::.TargetName property is when you want to apply the same animation to more than one object. This is especially useful when you have a large number of objects that have similar animations applied to them. For example, you might be displaying rows of images and want to use an animation to highlight the image that currently has the mouse pointer over it. It is inconvenient and messy to create separate Storyboard objects for each image. It is better to reuse the same Storyboard.
The following example has a number of rectangles that fade out and back into sight when you click them. All of these rectangles use the same Storyboard, because the DoubleAnimation that animates the Opacity changes its TargetName to whichever rectangle is clicked.
Run this sample
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Storyboard x:Name="myStoryboard">
<DoubleAnimation x:Name="myDoubleAnimation"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:2"
AutoReverse="True" />
</Storyboard>
</StackPanel.Resources>
<Rectangle
x:Name="MyAnimatedRectangle1"
Margin="3" Width="100" Height="100" Fill="Blue"
MouseLeftButtonDown="Start_Animation" />
<Rectangle
x:Name="MyAnimatedRectangle2"
Margin="3" Width="100" Height="100" Fill="Blue"
MouseLeftButtonDown="Start_Animation" />
<Rectangle
x:Name="MyAnimatedRectangle3"
Margin="3" Width="100" Height="100" Fill="Blue"
MouseLeftButtonDown="Start_Animation" />
<Rectangle
x:Name="MyAnimatedRectangle4"
Margin="3" Width="100" Height="100" Fill="Blue"
MouseLeftButtonDown="Start_Animation" />
</StackPanel>
Private Sub Start_Animation(ByVal sender As Object, ByVal e As MouseEventArgs)
' If the Storyboard is running and you try to change
' properties of its animation objects programmatically,
' an error will occur.
myStoryboard.Stop()
' Get a reference to the rectangle that was clicked.
Dim myRect As Rectangle = CType(sender, Rectangle)
' Change the TargetName of the animation to the name of the
' rectangle that was clicked.
myDoubleAnimation.SetValue(Storyboard.TargetNameProperty, myRect.Name)
' Begin the animation.
myStoryboard.Begin()
End Sub
private void Start_Animation(object sender, MouseEventArgs e)
{
// If the Storyboard is running and you try to change
// properties of its animation objects programmatically,
// an error will occur.
myStoryboard.Stop();
// Get a reference to the rectangle that was clicked.
Rectangle myRect = (Rectangle)sender;
// Change the TargetName of the animation to the name of the
// rectangle that was clicked.
myDoubleAnimation.SetValue(Storyboard.TargetNameProperty, myRect.Name);
// Begin the animation.
myStoryboard.Begin();
}
In the preceding code, notice that you must stop the Storyboard before you dynamically change the properties of its animation objects; otherwise, an error will occur. In this example, it might not be desirable to stop an animation on one rectangle so that the animation can start on another rectangle. Perhaps you want both animations to run at the same time. However, you cannot use the same animation object to run two separate animations at the same time, because there is only one TargetName. This does not mean that you are back to creating a separate Storyboard for every object. Instead, you need one Storyboard for each animation that you want to run concurrently (synchronously). The following example is similar to the previous one, except that it contains three Storyboard objects instead of one. When you click a rectangle, the event handler looks for a Storyboard that is not currently in use and uses that one to create the animation.
Run this sample
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Storyboard x:Name="myStoryboard1" Completed="Storyboard_Completed">
<DoubleAnimation x:Name="myDoubleAnimation1"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:2" AutoReverse="True" />
</Storyboard>
<Storyboard x:Name="myStoryboard2" Completed="Storyboard_Completed">
<DoubleAnimation x:Name="myDoubleAnimation2"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:2"
AutoReverse="True" />
</Storyboard>
<Storyboard x:Name="myStoryboard3" Completed="Storyboard_Completed">
<DoubleAnimation x:Name="myDoubleAnimation3"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:2"
AutoReverse="True" />
</Storyboard>
</StackPanel.Resources>
<Rectangle x:Name="MyAnimatedRectangle1"
Margin="3" Width="100" Height="100" Fill="Blue"
MouseLeftButtonDown="Start_Animation" />
<Rectangle x:Name="MyAnimatedRectangle2"
Margin="3" Width="100" Height="100" Fill="Blue"
MouseLeftButtonDown="Start_Animation" />
<Rectangle x:Name="MyAnimatedRectangle3"
Margin="3" Width="100" Height="100" Fill="Blue"
MouseLeftButtonDown="Start_Animation" />
<Rectangle x:Name="MyAnimatedRectangle4"
Margin="3" Width="100" Height="100" Fill="Blue"
MouseLeftButtonDown="Start_Animation" />
</StackPanel>
Private storyboard1Active As Boolean = False
Private storyboard2Active As Boolean = False
Private storyboard3Active As Boolean = False
Private Sub Start_Animation(ByVal sender As Object, ByVal e As MouseEventArgs)
' Get a reference to the rectangle that was clicked.
Dim myRect As Rectangle = CType(sender, Rectangle)
If Not storyboard1Active Then
myStoryboard1.Stop()
myDoubleAnimation1.SetValue(Storyboard.TargetNameProperty, myRect.Name)
myStoryboard1.Begin()
storyboard1Active = True
ElseIf Not storyboard2Active Then
myStoryboard2.Stop()
myDoubleAnimation2.SetValue(Storyboard.TargetNameProperty, myRect.Name)
myStoryboard2.Begin()
storyboard2Active = True
ElseIf Not storyboard3Active Then
myStoryboard3.Stop()
myDoubleAnimation3.SetValue(Storyboard.TargetNameProperty, myRect.Name)
myStoryboard3.Begin()
storyboard3Active = True
End If
End Sub
Private Sub Storyboard_Completed(ByVal sender As Object, ByVal e As EventArgs)
Dim myStoryboard As Storyboard = CType(sender, Storyboard)
Select Case (myStoryboard.GetValue(NameProperty).ToString)
Case "myStoryboard1"
storyboard1Active = False
Case "myStoryboard2"
storyboard2Active = False
Case "myStoryboard3"
storyboard3Active = False
End Select
End Sub
bool storyboard1Active = false;
bool storyboard2Active = false;
bool storyboard3Active = false;
private void Start_Animation(object sender, MouseEventArgs e)
{
// Get a reference to the rectangle that was clicked.
Rectangle myRect = (Rectangle)sender;
if (!storyboard1Active)
{
myStoryboard1.Stop();
myDoubleAnimation1.SetValue(Storyboard.TargetNameProperty, myRect.Name);
myStoryboard1.Begin();
storyboard1Active = true;
}
else if (!storyboard2Active)
{
myStoryboard2.Stop();
myDoubleAnimation2.SetValue(Storyboard.TargetNameProperty, myRect.Name);
myStoryboard2.Begin();
storyboard2Active = true;
}
else if (!storyboard3Active)
{
myStoryboard3.Stop();
myDoubleAnimation3.SetValue(Storyboard.TargetNameProperty, myRect.Name);
myStoryboard3.Begin();
storyboard3Active = true;
}
}
private void Storyboard_Completed(object sender, EventArgs e)
{
Storyboard myStoryboard = sender as Storyboard;
switch (myStoryboard.GetValue(NameProperty).ToString())
{
case "myStoryboard1": storyboard1Active = false; break;
case "myStoryboard2": storyboard2Active = false; break;
case "myStoryboard3": storyboard3Active = false; break;
}
}
In the preceding example, only three animations can run at the same time (equal to the number of Storyboard objects). This is fine if you do not anticipate a need for more concurrent animations, which would require more Storyboard objects. If you expect a lot of independent animations to be running at the same time, you might want to create your Storyboard objects dynamically. For an example of creating storyboard objects in code, see the next section.