Vue d'ensemble des storyboards

Cette rubrique indique comment utiliser des objets Storyboard pour organiser et appliquer des animations. Elle décrit comment manipuler des objets Storyboard interactivement et décrit la syntaxe de ciblage de propriété indirecte.

Composants requis

Pour comprendre cette rubrique, vous devez être familiarisé avec les différents types d'animation et leurs fonctionnalités de base. Pour une présentation des fonctionnalités d'animation, consultez Vue d'ensemble de l'animation. Vous devez également savoir comment utiliser des propriétés attachées. Pour plus d'informations sur le fonctionnement des propriétés attachées, consultez Vue d'ensemble des propriétés jointes.

Qu'est-ce qu'une table de montage séquentiel ?

Les animations ne sont pas le seul type utile de chronologie. D'autres classes de chronologie sont fournies pour vous aider à organiser des jeux de chronologies, et appliquer des chronologies aux propriétés. Les chronologies de conteneur dérivent de la classe TimelineGroup et incluent ParallelTimeline et Storyboard.

Un Storyboard est un type de chronologie de conteneur qui fournit des informations de ciblage pour les animations qu'il contient. Un storyboard peut contenir n'importe quel type de Timeline, y compris les chronologies et animations d'un autre conteneur. Les objets Storyboard vous permettent de combiner les chronologies qui affectent divers objets et propriétés en une arborescence de chronologie unique, ce qui facilite l'organisation et le contrôle des comportements de minutage complexes. Par exemple, supposez que vous souhaitiez un bouton qui fasse ces trois choses.

  • Grandit et change de couleur lorsque l'utilisateur sélectionne le bouton.

  • Réduit et reprend sa taille d'origine en cas de clic.

  • Réduit et s'atténue à 50 pour cent d'opacité lorsqu'il est désactivé.

Dans ce cas, vous avez plusieurs jeux d'animations qui s'appliquent au même objet, que vous souhaitez jouer à des moments différents, en fonction de l'état du bouton. Les objets Storyboard vous permettent d'organiser les animations et de les appliquer dans des groupes à un ou plusieurs objets.

Où pouvez-vous utiliser une table de montage séquentiel ?

Un Storyboard peut être utilisé pour animer des propriétés de dépendance de classes pouvant être animées (pour plus d'informations sur ce qui permet d'animer une classe, consultez Vue d'ensemble de l'animation). Toutefois, du fait que les tables de montage séquentiel sont une fonctionnalité de niveau infrastructure, l'objet doit appartenir au NameScope d'un FrameworkElement ou d'un FrameworkContentElement.

Par exemple, vous pourriez utiliser un Storyboard pour effectuer les opérations suivantes :

Toutefois, vous n'avez pas pu utiliser de Storyboard pour animer un SolidColorBrush qui n'a pas enregistré son nom avec un FrameworkElement ou FrameworkContentElement, ou qui n'a pas été utilisé pour définir une propriété d'un FrameworkElement ou FrameworkContentElement.

Comment appliquer des animations avec une table de montage séquentiel

Pour utiliser un Storyboard pour organiser et appliquer des animations, vous ajoutez les animations comme chronologies enfants du Storyboard. La classe Storyboard fournit les propriétés attachées Storyboard.TargetName et Storyboard.TargetProperty. Vous définissez ces propriétés sur une animation pour spécifier son objet et propriété cibles.

Pour appliquer des animations à leurs cibles, vous commencez le Storyboard à l'aide d'une action de déclencheur ou d'une méthode. En XAML, vous utilisez un objet BeginStoryboard avec un EventTrigger, Trigger ou DataTrigger. Dans le code, vous pouvez également utiliser la méthode Begin.

Le tableau suivant indique les différents endroits où chaque technique de démarrage de Storyboard est prise en charge : par instance, style, modèle de contrôle et modèle de données. " « Par instance » fait référence à la technique d'application d'une animation ou d'un storyboard directement aux instances d'un objet, plutôt que dans un style, un modèle de contrôle ou un modèle de données.

La table de montage séquentiel commence à être utilisée…

Par instance

Style

Modèle de contrôle

Modèle de données

Exemple

BeginStoryboard et un EventTrigger

Oui

Oui

Oui

Oui

Comment : animer une propriété à l'aide d'un storyboard

BeginStoryboard et une propriété Trigger

Non

Oui

Oui

Oui

Comment : déclencher une animation en cas de modification d'une valeur de propriété

BeginStoryboard et un DataTrigger

Non

Oui

Oui

Oui

Comment : déclencher une animation en cas de modification de données

Méthode Begin

Oui

Non

Non

Non

Comment : animer une propriété à l'aide d'un storyboard

L'exemple suivant utilise un Storyboard pour animer le Width d'un élément Rectangle et le Color d'un SolidColorBrush utilisé pour peindre ce Rectangle.

<!-- This example shows how to animate with a storyboard.-->
<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="Microsoft.Samples.Animation.StoryboardsExample" 
  WindowTitle="Storyboards Example">
  <StackPanel Margin="20">

    <Rectangle Name="MyRectangle"
      Width="100"
      Height="100">
      <Rectangle.Fill>
        <SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
      </Rectangle.Fill>
      <Rectangle.Triggers>
        <EventTrigger RoutedEvent="Rectangle.MouseEnter">
          <BeginStoryboard>
            <Storyboard>
              <DoubleAnimation 
                Storyboard.TargetName="MyRectangle"
                Storyboard.TargetProperty="Width"
                From="100" To="200" Duration="0:0:1" />

              <ColorAnimation 
                Storyboard.TargetName="MySolidColorBrush"
                Storyboard.TargetProperty="Color"
                From="Blue" To="Red" Duration="0:0:1" />  
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger>
      </Rectangle.Triggers>
    </Rectangle> 
  </StackPanel>
</Page>

Imports System
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Data
Imports System.Windows.Shapes
Imports System.Windows.Input


Namespace Microsoft.Samples.Animation
    Public Class StoryboardsExample
        Inherits Page
        Public Sub New()
            Me.WindowTitle = "Storyboards Example"
            Dim myStackPanel As New StackPanel()
            myStackPanel.Margin = New Thickness(20)

            Dim myRectangle As New Rectangle()
            myRectangle.Name = "MyRectangle"

            ' Create a name scope for the page.
            NameScope.SetNameScope(Me, New NameScope())

            Me.RegisterName(myRectangle.Name, myRectangle)
            myRectangle.Width = 100
            myRectangle.Height = 100
            Dim mySolidColorBrush As New SolidColorBrush(Colors.Blue)
            Me.RegisterName("MySolidColorBrush", mySolidColorBrush)
            myRectangle.Fill = mySolidColorBrush

            Dim myDoubleAnimation As New DoubleAnimation()
            myDoubleAnimation.From = 100
            myDoubleAnimation.To = 200
            myDoubleAnimation.Duration = New Duration(TimeSpan.FromSeconds(1))
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name)
            Storyboard.SetTargetProperty(myDoubleAnimation, New PropertyPath(Rectangle.WidthProperty))

            Dim myColorAnimation As New ColorAnimation()
            myColorAnimation.From = Colors.Blue
            myColorAnimation.To = Colors.Red
            myColorAnimation.Duration = New Duration(TimeSpan.FromSeconds(1))
            Storyboard.SetTargetName(myColorAnimation, "MySolidColorBrush")
            Storyboard.SetTargetProperty(myColorAnimation, New PropertyPath(SolidColorBrush.ColorProperty))
            Dim myStoryboard As New Storyboard()
            myStoryboard.Children.Add(myDoubleAnimation)
            myStoryboard.Children.Add(myColorAnimation)

            AddHandler myRectangle.MouseEnter, Sub(sender As Object, e As MouseEventArgs) myStoryboard.Begin(Me)

            myStackPanel.Children.Add(myRectangle)
            Me.Content = myStackPanel
        End Sub
    End Class
End Namespace
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Data;
using System.Windows.Shapes;
using System.Windows.Input;


namespace Microsoft.Samples.Animation
{
    public class StoryboardsExample : Page
    {      
        public StoryboardsExample()
        {
            this.WindowTitle = "Storyboards Example";
            StackPanel myStackPanel = new StackPanel();
            myStackPanel.Margin = new Thickness(20);

            Rectangle myRectangle = new Rectangle();
            myRectangle.Name = "MyRectangle";

            // Create a name scope for the page.
            NameScope.SetNameScope(this, new NameScope());            

            this.RegisterName(myRectangle.Name, myRectangle);
            myRectangle.Width = 100;
            myRectangle.Height = 100;
            SolidColorBrush mySolidColorBrush = new SolidColorBrush(Colors.Blue);
            this.RegisterName("MySolidColorBrush", mySolidColorBrush);
            myRectangle.Fill = mySolidColorBrush;

            DoubleAnimation myDoubleAnimation = new DoubleAnimation();
            myDoubleAnimation.From = 100;
            myDoubleAnimation.To = 200;
            myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
            Storyboard.SetTargetProperty(myDoubleAnimation, 
                new PropertyPath(Rectangle.WidthProperty));

            ColorAnimation myColorAnimation = new ColorAnimation();
            myColorAnimation.From = Colors.Blue;
            myColorAnimation.To = Colors.Red;
            myColorAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
            Storyboard.SetTargetName(myColorAnimation, "MySolidColorBrush");
            Storyboard.SetTargetProperty(myColorAnimation, 
                new PropertyPath(SolidColorBrush.ColorProperty)); 
            Storyboard myStoryboard = new Storyboard();
            myStoryboard.Children.Add(myDoubleAnimation);
            myStoryboard.Children.Add(myColorAnimation);

            myRectangle.MouseEnter += delegate(object sender, MouseEventArgs e)
            {
                myStoryboard.Begin(this);
            };

            myStackPanel.Children.Add(myRectangle);
            this.Content = myStackPanel;
        } 
    }
}

Les sections suivantes décrivent plus en détail les propriétés attachées TargetName et TargetProperty.

Ciblage d'éléments d'infrastructure, éléments du contenu de l'infrastructure et Freezables

La section précédente a mentionné que pour qu'une animation trouve sa cible, elle doit connaître le nom de la cible et la propriété à animer. La spécification de la propriété à animer est très simple : il suffit de définir Storyboard.TargetProperty avec le nom de la propriété à animer. Vous indiquez le nom de l'objet dont vous souhaitez animer la propriété en définissant la propriété Storyboard.TargetName sur l'animation.

Pour que la propriété TargetName fonctionne, l'objet ciblé doit avoir un nom. L'affectation d'un nom à un FrameworkElement ou un FrameworkContentElement en XAML diffère de l'affectation d'un nom à un objet Freezable.

Les éléments d'infrastructure sont les classes qui héritent de la classe FrameworkElement. Window, DockPanel, Button, et Rectangle sont des exemples d'éléments d'infrastructure. En général, toutes les fenêtres, volets et contrôles sont des éléments. Les éléments du contenu de l'infrastructure sont les classes qui héritent de la classe FrameworkContentElement. FlowDocument et Paragraph sont des exemples d'éléments du contenu de l'infrastructure. Si vous n'êtes pas sûr si un type est un élément d'infrastructure ou un élément du contenu de l'infrastructure, vérifiez s'il dispose d'une propriété Name. Si c'est le cas, il s'agit probablement d'un élément d'infrastructure ou d'un élément du contenu de l'infrastructure. Pour être sûr, vérifiez la section Hiérarchied'héritage de sa page type.

Pour activer le ciblage d'un élément d'infrastructure ou d'un élément du contenu de l'infrastructure en XAML, définissez sa propriété Name. Dans le code, vous devez également utiliser la méthode RegisterName pour enregistrer le nom de l'élément avec l'élément pour lequel vous avez créé un NameScope.

L'exemple suivant, pris de l'exemple précédent, attribue le nom MyRectangle à un Rectangle, un type de FrameworkElement.

<Rectangle Name="MyRectangle"
  Width="100"
  Height="100">
            Dim myRectangle As New Rectangle()
            myRectangle.Name = "MyRectangle"

            ' Create a name scope for the page.
            NameScope.SetNameScope(Me, New NameScope())

            Me.RegisterName(myRectangle.Name, myRectangle)
Rectangle myRectangle = new Rectangle();
myRectangle.Name = "MyRectangle";

// Create a name scope for the page.
NameScope.SetNameScope(this, new NameScope());            

this.RegisterName(myRectangle.Name, myRectangle);

Une fois que l'élément est nommé, vous pouvez animer une propriété de cet élément.

<DoubleAnimation 
  Storyboard.TargetName="MyRectangle"
  Storyboard.TargetProperty="Width"
  From="100" To="200" Duration="0:0:1" />
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name)
            Storyboard.SetTargetProperty(myDoubleAnimation, New PropertyPath(Rectangle.WidthProperty))
Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
Storyboard.SetTargetProperty(myDoubleAnimation, 
    new PropertyPath(Rectangle.WidthProperty));

Les types Freezable sont les classes qui héritent de la classe Freezable. SolidColorBrush, RotateTransform, et GradientStop sont des exemples de Freezable.

Pour activer le ciblage d'un Freezable par une animation en XAML, vous utilisez le x:Name, directive pour lui attribuer un nom. Dans le code, vous utilisez la méthode RegisterName pour enregistrer son nom avec l'élément pour lequel vous avez créé un NameScope.

L'exemple suivant attribue un nom à un objet Freezable.

<SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
            Dim mySolidColorBrush As New SolidColorBrush(Colors.Blue)
            Me.RegisterName("MySolidColorBrush", mySolidColorBrush)
SolidColorBrush mySolidColorBrush = new SolidColorBrush(Colors.Blue);
this.RegisterName("MySolidColorBrush", mySolidColorBrush);

L'objet peut ensuite être ciblé par une animation.

<ColorAnimation 
  Storyboard.TargetName="MySolidColorBrush"
  Storyboard.TargetProperty="Color"
  From="Blue" To="Red" Duration="0:0:1" />  
            Storyboard.SetTargetName(myColorAnimation, "MySolidColorBrush")
            Storyboard.SetTargetProperty(myColorAnimation, New PropertyPath(SolidColorBrush.ColorProperty))
Storyboard.SetTargetName(myColorAnimation, "MySolidColorBrush");
Storyboard.SetTargetProperty(myColorAnimation, 
    new PropertyPath(SolidColorBrush.ColorProperty)); 

Les objets Storyboard utilisent des portées de nom pour résoudre la propriété TargetName. Pour plus d'informations sur les portées de nom WPF, consultez Portées de nom XAML WPF. Si la propriété TargetName est omise, l'animation cible l'élément sur lequel elle est définie, ou, dans le cas de styles, l'élément auquel est appliqué un style.

Il arrive parfois qu'un nom ne puisse pas être attribué à un objet Freezable. Par exemple, si un Freezable est déclaré comme une ressource ou utilisé pour définir une valeur de propriété dans un style, aucun nom ne peut lui être donné. Du fait qu'il n'a pas de nom, il ne peut pas être ciblé directement, mais peut être ciblé indirectement. Les sections suivantes décrivent comment utiliser le ciblage indirect.

Ciblage indirect

Il arrive qu'un Freezable ne puisse pas être ciblé directement par une animation, comme quand le Freezable est déclaré comme une ressource ou utilisé définir une valeur de propriété dans un style. Dans ces cas, bien que vous ne puissiez pas le cibler directement, vous pouvez encore animer l'objet Freezable. Au lieu de définir la propriété TargetName avec le nom du Freezable, vous lui donnez le nom de l'élément auquel "appartient" le Freezable. Par exemple, un SolidColorBrush utilisé pour définir le Fill d'un élément rectangle appartient à ce rectangle. Pour animer le pinceau, vous devrez définir la propriété TargetProperty de l'animation avec une chaîne de propriétés qui démarre à la propriété de l'élément d'infrastructure ou de l'élément du contenu de l'infrastructure le Freezable utilisé pour la définition et se termine avec la propriété Freezable à animer.

<ColorAnimation 
  Storyboard.TargetName="Rectangle01"
  Storyboard.TargetProperty="Fill.Color"
  From="Blue" To="AliceBlue" Duration="0:0:1" />
            Dim propertyChain() As DependencyProperty = {Rectangle.FillProperty, SolidColorBrush.ColorProperty}
            Dim thePath As String = "(0).(1)"
            Dim myPropertyPath As New PropertyPath(thePath, propertyChain)
            Storyboard.SetTargetProperty(myColorAnimation, myPropertyPath)
DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};
string thePath = "(0).(1)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myColorAnimation, myPropertyPath);

Notez que si le Freezable est figé, un clone sera créé, et que ce clone sera animé. Lorsque cela se produit, la propriété HasAnimatedProperties de l'objet d'origine continue à retourner false, car l'objet d'origine n'est pas animé réellement. Pour plus d'informations sur les clones, consultez Vue d'ensemble des objets Freezable.

Notez également que lors de l'utilisation du ciblage indirect de propriété, il est possible de cibler des objets qui n'existent pas. Par exemple, vous pouvez supposer que l'Background d'un bouton particulier a été défini avec un SolidColorBrush et essayer d'animer sa couleur, alors qu'en fait, un LinearGradientBrush a été utilisé pour définir l'arrière-plan du bouton. Dans ces cas, aucune exception n'est renvoyée ; l'animation ne parvient pas à avoir un effet visible car LinearGradientBrush ne réagit pas aux modifications apportées à la propriété Color.

Les sections suivantes décrivent plus en détail la syntaxe de ciblage indirect de propriété.

Ciblage indirect d'une propriété d'un Freezable en XAML

Pour cibler une propriété d'un freezable en XAML, utilisez la syntaxe suivante.

ElementPropertyName.FreezablePropertyName

Where

Le code suivant montre comment animer le Color d'un SolidColorBrush utilisé pour définir le

Fill d'un élément rectangle.

<Rectangle
  Name="Rectangle01"
  Height="100"
  Width="100"
  Fill="{StaticResource MySolidColorBrushResource}">
  <Rectangle.Triggers>
    <EventTrigger RoutedEvent="Rectangle.MouseEnter">
      <BeginStoryboard>
        <Storyboard>
          <ColorAnimation 
            Storyboard.TargetName="Rectangle01"
            Storyboard.TargetProperty="Fill.Color"
            From="Blue" To="AliceBlue" Duration="0:0:1" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Rectangle.Triggers>
</Rectangle>

Vous devez parfois cibler un freezable contenu dans une collection ou un tableau.

Pour cibler un freezable contenu dans une collection, vous utilisez la syntaxe Path suivante.

ElementPropertyName.Children[CollectionIndex].FreezablePropertyName

CollectionIndex est l'index de l'objet dans son tableau ou sa collection.

Par exemple, supposez qu'un rectangle a une ressource TransformGroup appliquée à sa propriété RenderTransform, et que vous souhaitez animer l'une des transformations qu'il contient.

<TransformGroup x:Key="MyTransformGroupResource"
  x:Shared="False">
  <ScaleTransform />
  <RotateTransform />
</TransformGroup>

Le code suivant indique comment animer la propriété Angle de la RotateTransform affichée dans l'exemple précédent.

<Rectangle
  Name="Rectangle02"
  Height="100"
  Width="100"
  Fill="Blue"
  RenderTransform="{StaticResource MyTransformGroupResource}">
  <Rectangle.Triggers>
    <EventTrigger RoutedEvent="Rectangle.MouseEnter">
      <BeginStoryboard>
        <Storyboard>
          <DoubleAnimation 
            Storyboard.TargetName="Rectangle02"
            Storyboard.TargetProperty="RenderTransform.Children[1].Angle"
            From="0" To="360" Duration="0:0:1" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Rectangle.Triggers>
</Rectangle>  

Ciblage indirect d'une propriété d'un Freezable dans le code

Dans le code, vous créez un objet PropertyPath. Lorsque vous créez le PropertyPath, vous spécifiez un Path et des PathParameters.

Pour créer PathParameters, vous créez un tableau de type DependencyProperty qui contient une liste de champs d'identificateur de propriété de dépendance. Le premier champ d'identificateur est pour la propriété de l'FrameworkElement ou FrameworkContentElement que le Freezable utilise pour définir. Le champ d'identificateur suivant représente la propriété du Freezable à cibler. Considérez-le comme une chaîne de propriétés qui connecte le Freezable à l'objet FrameworkElement.

Les éléments suivants sont un exemple d'une chaîne de propriété de dépendance qui cible le Color d'un SolidColorBrush utilisé pour définir le Fill d'un élément rectangle.

            Dim propertyChain() As DependencyProperty = {Rectangle.FillProperty, SolidColorBrush.ColorProperty}
DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};

Vous devez également spécifier un Path. Un Path est une String qui explique au Path comment interpréter ses PathParameters. Il utilise la syntaxe suivante.

(OwnerPropertyArrayIndex).(FreezablePropertyArrayIndex)

Where

  • OwnerPropertyArrayIndex est l'index du tableau DependencyProperty qui contient l'identificateur de la propriété de l'objet FrameworkElement que le Freezable utilise pour définir, et

  • FreezablePropertyArrayIndex est l'index du tableau DependencyProperty qui contient l'identificateur de propriété à cibler.

L'exemple suivant montre le Path qui devrait accompagner les PathParameters définie dans l'exemple précédent.

            Dim propertyChain() As DependencyProperty = {Rectangle.FillProperty, SolidColorBrush.ColorProperty}
            Dim thePath As String = "(0).(1)"
DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};
string thePath = "(0).(1)";

L'exemple suivant combine le code des exemples précédents pour animer le Color d'un SolidColorBrush utilisé pour définir le Fill d'un élément rectangle.


            ' Create a name scope for the page.
            NameScope.SetNameScope(Me, New NameScope())

            Dim rectangle01 As New Rectangle()
            rectangle01.Name = "Rectangle01"
            Me.RegisterName(rectangle01.Name, rectangle01)
            rectangle01.Width = 100
            rectangle01.Height = 100
            rectangle01.Fill = CType(Me.Resources("MySolidColorBrushResource"), SolidColorBrush)

            Dim myColorAnimation As New ColorAnimation()
            myColorAnimation.From = Colors.Blue
            myColorAnimation.To = Colors.AliceBlue
            myColorAnimation.Duration = New Duration(TimeSpan.FromSeconds(1))
            Storyboard.SetTargetName(myColorAnimation, rectangle01.Name)

            Dim propertyChain() As DependencyProperty = {Rectangle.FillProperty, SolidColorBrush.ColorProperty}
            Dim thePath As String = "(0).(1)"
            Dim myPropertyPath As New PropertyPath(thePath, propertyChain)
            Storyboard.SetTargetProperty(myColorAnimation, myPropertyPath)

            Dim myStoryboard As New Storyboard()
            myStoryboard.Children.Add(myColorAnimation)
            Dim myBeginStoryboard As New BeginStoryboard()
            myBeginStoryboard.Storyboard = myStoryboard
            Dim myMouseEnterTrigger As New EventTrigger()
            myMouseEnterTrigger.RoutedEvent = Rectangle.MouseEnterEvent
            myMouseEnterTrigger.Actions.Add(myBeginStoryboard)
            rectangle01.Triggers.Add(myMouseEnterTrigger)

// Create a name scope for the page.
NameScope.SetNameScope(this, new NameScope()); 

Rectangle rectangle01 = new Rectangle();
rectangle01.Name = "Rectangle01";   
this.RegisterName(rectangle01.Name, rectangle01);
rectangle01.Width = 100;
rectangle01.Height = 100;
rectangle01.Fill = 
    (SolidColorBrush)this.Resources["MySolidColorBrushResource"];

ColorAnimation myColorAnimation = new ColorAnimation();
myColorAnimation.From = Colors.Blue;
myColorAnimation.To = Colors.AliceBlue;
myColorAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
Storyboard.SetTargetName(myColorAnimation, rectangle01.Name);

DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};
string thePath = "(0).(1)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myColorAnimation, myPropertyPath);

Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myColorAnimation);
BeginStoryboard myBeginStoryboard = new BeginStoryboard();
myBeginStoryboard.Storyboard = myStoryboard;
EventTrigger myMouseEnterTrigger = new EventTrigger();
myMouseEnterTrigger.RoutedEvent = Rectangle.MouseEnterEvent;
myMouseEnterTrigger.Actions.Add(myBeginStoryboard);
rectangle01.Triggers.Add(myMouseEnterTrigger);

Vous devez parfois cibler un freezable contenu dans une collection ou un tableau. Par exemple, supposez qu'un rectangle a une ressource TransformGroup appliquée à sa propriété RenderTransform, et que vous souhaitez animer l'une des transformations qu'il contient.

<TransformGroup x:Key="MyTransformGroupResource"
  x:Shared="False">
  <ScaleTransform />
  <RotateTransform />
</TransformGroup>  

Pour cibler un Freezable contenu dans une collection, vous utilisez la syntaxe Path suivante.

(OwnerPropertyArrayIndex).( CollectionChildrenPropertyArrayIndex) [CollectionIndex ].()

CollectionIndex est l'index de l'objet dans son tableau ou sa collection.

Pour cibler la propriété Angle de la deuxième RotateTransform transformation dans le TransformGroup, vous utiliseriez le Path suivant et PathParameters.

            Dim propertyChain() As DependencyProperty = { Rectangle.RenderTransformProperty, TransformGroup.ChildrenProperty, RotateTransform.AngleProperty }
            Dim thePath As String = "(0).(1)[1].(2)"
            Dim myPropertyPath As New PropertyPath(thePath, propertyChain)
            Storyboard.SetTargetProperty(myDoubleAnimation, myPropertyPath)
DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {
            Rectangle.RenderTransformProperty, 
            TransformGroup.ChildrenProperty,
            RotateTransform.AngleProperty
        };
string thePath = "(0).(1)[1].(2)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myDoubleAnimation, myPropertyPath);

L'exemple suivant affiche le code complet pour animer l'Angle d'une RotateTransform a contenue dans un TransformGroup.

            Dim rectangle02 As New Rectangle()
            rectangle02.Name = "Rectangle02"
            Me.RegisterName(rectangle02.Name, rectangle02)
            rectangle02.Width = 100
            rectangle02.Height = 100
            rectangle02.Fill = Brushes.Blue
            rectangle02.RenderTransform = CType(Me.Resources("MyTransformGroupResource"), TransformGroup)

            Dim myDoubleAnimation As New DoubleAnimation()
            myDoubleAnimation.From = 0
            myDoubleAnimation.To = 360
            myDoubleAnimation.Duration = New Duration(TimeSpan.FromSeconds(1))
            Storyboard.SetTargetName(myDoubleAnimation, rectangle02.Name)

            Dim propertyChain() As DependencyProperty = { Rectangle.RenderTransformProperty, TransformGroup.ChildrenProperty, RotateTransform.AngleProperty }
            Dim thePath As String = "(0).(1)[1].(2)"
            Dim myPropertyPath As New PropertyPath(thePath, propertyChain)
            Storyboard.SetTargetProperty(myDoubleAnimation, myPropertyPath)

            Dim myStoryboard As New Storyboard()
            myStoryboard.Children.Add(myDoubleAnimation)
            Dim myBeginStoryboard As New BeginStoryboard()
            myBeginStoryboard.Storyboard = myStoryboard
            Dim myMouseEnterTrigger As New EventTrigger()
            myMouseEnterTrigger.RoutedEvent = Rectangle.MouseEnterEvent
            myMouseEnterTrigger.Actions.Add(myBeginStoryboard)
            rectangle02.Triggers.Add(myMouseEnterTrigger)
Rectangle rectangle02 = new Rectangle();
rectangle02.Name = "Rectangle02";
this.RegisterName(rectangle02.Name, rectangle02);
rectangle02.Width = 100;
rectangle02.Height = 100;
rectangle02.Fill = Brushes.Blue;
rectangle02.RenderTransform = 
    (TransformGroup)this.Resources["MyTransformGroupResource"];

DoubleAnimation myDoubleAnimation = new DoubleAnimation();
myDoubleAnimation.From = 0;
myDoubleAnimation.To = 360;
myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
Storyboard.SetTargetName(myDoubleAnimation, rectangle02.Name);

DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {
            Rectangle.RenderTransformProperty, 
            TransformGroup.ChildrenProperty,
            RotateTransform.AngleProperty
        };
string thePath = "(0).(1)[1].(2)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myDoubleAnimation, myPropertyPath);

Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myDoubleAnimation);
BeginStoryboard myBeginStoryboard = new BeginStoryboard();
myBeginStoryboard.Storyboard = myStoryboard;
EventTrigger myMouseEnterTrigger = new EventTrigger();
myMouseEnterTrigger.RoutedEvent = Rectangle.MouseEnterEvent;
myMouseEnterTrigger.Actions.Add(myBeginStoryboard);
rectangle02.Triggers.Add(myMouseEnterTrigger);

Ciblage indirect avec un Freezable comme point de départ

Les sections précédentes ont décrit comment cibler indirectement un Freezable en démarrant avec un FrameworkElement ou FrameworkContentElement et en créant une chaîne de propriété à une sous-propriété Freezable. Vous pouvez également utiliser un Freezable comme point de départ et cibler indirectement l'une de ses sous-propriétés Freezable. Une restriction supplémentaire s'applique lors de l'utilisation d'un Freezable comme point de départ pour le ciblage indirect : le Freezable initial et chaque Freezable entre lui et la sous-propriété indirectement ciblée ne doivent pas être figés.

Contrôle interactif d'une table de montage séquentiel en XAML

Pour démarrer un storyboard en Extensible Application Markup Language (XAML), vous utilisez une action de déclencheur BeginStoryboard. BeginStoryboard distribue les animations aux objets et propriétés qu'elles animent et démarre le storyboard. (Pour plus de détails sur ce processus, consultez Vue d'ensemble de l'animation et du système de minutage). Si vous donnez un nom à BeginStoryboard en spécifiant sa propriété Name, vous en faites une table de montage séquentiel contrôlable. Vous pouvez ensuite contrôler interactivement la table de montage séquentiel après son démarrage. Les éléments suivants sont une liste d'actions de table de montage séquentiel vérifiables que vous utilisez avec les déclencheurs d'événements pour contrôler une table de montage séquentiel.

Dans l'exemple suivant, des actions de table de montage séquentiel contrôlables sont utilisées pour contrôler interactivement une table de montage séquentiel.

<Page
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="Microsoft.SDK.Animation.ControllableStoryboardExample"
  WindowTitle="Fading Rectangle Example">
  <StackPanel Margin="10">

    <Rectangle
      Name="MyRectangle"
      Width="100" 
      Height="100"
      Fill="Blue">
    </Rectangle>

    <Button Name="BeginButton">Begin</Button>
    <Button Name="PauseButton">Pause</Button>
    <Button Name="ResumeButton">Resume</Button>
    <Button Name="SkipToFillButton">Skip To Fill</Button>
    <Button Name="StopButton">Stop</Button>

    <StackPanel.Triggers>
      <EventTrigger RoutedEvent="Button.Click" SourceName="BeginButton">
        <BeginStoryboard Name="MyBeginStoryboard">
          <Storyboard>
            <DoubleAnimation
              Storyboard.TargetName="MyRectangle" 
              Storyboard.TargetProperty="(Rectangle.Opacity)"
              From="1.0" To="0.0" Duration="0:0:5" />
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="PauseButton">
        <PauseStoryboard BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="ResumeButton">
        <ResumeStoryboard BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="SkipToFillButton">
        <SkipStoryboardToFill BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="StopButton">
        <StopStoryboard BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
    </StackPanel.Triggers>
  </StackPanel>
</Page>

Contrôler interactivement une table de montage séquentiel en utilisant du code

Les exemples précédents ont indiqué comment animer l'utilisation d'actions de déclencheur. Dans le code, vous pouvez également contrôler une table de montage séquentiel à l'aide des méthodes interactives de la classe Storyboard. Pour qu'un Storyboard soit rendu interactif dans le code, vous devez utiliser la surcharge appropriée de la méthode Begin de la table de montage séquentiel et spécifier la valeur true pour qu'elle soit contrôlable. Pour plus d'informations, consultez la page Begin(FrameworkElement, Boolean).

La liste suivante affiche les méthodes qui peuvent être utilisées pour manipuler un Storyboard après son démarrage :

L'avantage d'utiliser ces méthodes est que vous n'avez pas besoin de créer d'objets Trigger ou TriggerAction ; vous avez juste besoin d'une référence au Storyboard contrôlable que vous souhaitez manipuler.

Remarque : toutes les actions interactives effectuées sur un Clock, et par conséquent également sur un Storyboard, se produiront sur le battement suivant du moteur de minutage, ce qui se produira peu avant le rendu suivant. Par exemple, si vous utilisez la méthode Seek pour accéder à un autre point dans une animation, la valeur de propriété ne change pas instantanément, mais plutôt sur le battement suivant du moteur de minutage.

L'exemple suivant indique comment appliquer et contrôler des animations à l'aide des méthodes interactives de la classe Storyboard.


Imports Microsoft.VisualBasic
Imports System
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Shapes
Imports System.Windows.Media
Imports System.Windows.Media.Animation

Namespace SDKSample

    Public Class ControllableStoryboardExample
        Inherits Page
        Private myStoryboard As Storyboard

        Public Sub New()

            ' Create a name scope for the page.

            NameScope.SetNameScope(Me, New NameScope())

            Me.WindowTitle = "Controllable Storyboard Example"
            Dim myStackPanel As New StackPanel()
            myStackPanel.Margin = New Thickness(10)

            ' Create a rectangle.
            Dim myRectangle As New Rectangle()
            myRectangle.Name = "myRectangle"

            ' Assign the rectangle a name by 
            ' registering it with the page, so that
            ' it can be targeted by storyboard
            ' animations.
            Me.RegisterName(myRectangle.Name, myRectangle)
            myRectangle.Width = 100
            myRectangle.Height = 100
            myRectangle.Fill = Brushes.Blue
            myStackPanel.Children.Add(myRectangle)

            '
            ' Create an animation and a storyboard to animate the
            ' rectangle.
            '
            Dim myDoubleAnimation As New DoubleAnimation()
            myDoubleAnimation.From = 1.0
            myDoubleAnimation.To = 0.0
            myDoubleAnimation.Duration = New Duration(TimeSpan.FromMilliseconds(5000))
            myDoubleAnimation.AutoReverse = True

            ' Create the storyboard.
            myStoryboard = New Storyboard()
            myStoryboard.Children.Add(myDoubleAnimation)
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name)
            Storyboard.SetTargetProperty(myDoubleAnimation, New PropertyPath(Rectangle.OpacityProperty))

            '
            ' Create some buttons to control the storyboard
            ' and a panel to contain them.
            '
            Dim buttonPanel As New StackPanel()
            buttonPanel.Orientation = Orientation.Horizontal
            Dim beginButton As New Button()
            beginButton.Content = "Begin"
            AddHandler beginButton.Click, AddressOf beginButton_Clicked
            buttonPanel.Children.Add(beginButton)
            Dim pauseButton As New Button()
            pauseButton.Content = "Pause"
            AddHandler pauseButton.Click, AddressOf pauseButton_Clicked
            buttonPanel.Children.Add(pauseButton)
            Dim resumeButton As New Button()
            resumeButton.Content = "Resume"
            AddHandler resumeButton.Click, AddressOf resumeButton_Clicked
            buttonPanel.Children.Add(resumeButton)
            Dim skipToFillButton As New Button()
            skipToFillButton.Content = "Skip to Fill"
            AddHandler skipToFillButton.Click, AddressOf skipToFillButton_Clicked
            buttonPanel.Children.Add(skipToFillButton)
            Dim setSpeedRatioButton As New Button()
            setSpeedRatioButton.Content = "Triple Speed"
            AddHandler setSpeedRatioButton.Click, AddressOf setSpeedRatioButton_Clicked
            buttonPanel.Children.Add(setSpeedRatioButton)
            Dim stopButton As New Button()
            stopButton.Content = "Stop"
            AddHandler stopButton.Click, AddressOf stopButton_Clicked
            buttonPanel.Children.Add(stopButton)
            myStackPanel.Children.Add(buttonPanel)
            Me.Content = myStackPanel


        End Sub

        ' Begins the storyboard.
        Private Sub beginButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            ' Specifying "true" as the second Begin parameter
            ' makes this storyboard controllable.
            myStoryboard.Begin(Me, True)

        End Sub

        ' Pauses the storyboard.
        Private Sub pauseButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.Pause(Me)

        End Sub

        ' Resumes the storyboard.
        Private Sub resumeButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.Resume(Me)

        End Sub

        ' Advances the storyboard to its fill period.
        Private Sub skipToFillButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.SkipToFill(Me)

        End Sub

        ' Updates the storyboard's speed.
        Private Sub setSpeedRatioButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            ' Makes the storyboard progress three times as fast as normal.
            myStoryboard.SetSpeedRatio(Me, 3)

        End Sub

        ' Stops the storyboard.
        Private Sub stopButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.Stop(Me)

        End Sub

    End Class

End Namespace
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace SDKSample
{

    public class ControllableStoryboardExample : Page
    {
        private Storyboard myStoryboard;

        public ControllableStoryboardExample()
        {

            // Create a name scope for the page.

            NameScope.SetNameScope(this, new NameScope()); 

            this.WindowTitle = "Controllable Storyboard Example";
            StackPanel myStackPanel = new StackPanel();
            myStackPanel.Margin = new Thickness(10);

            // Create a rectangle.
            Rectangle myRectangle = new Rectangle();
            myRectangle.Name = "myRectangle";

            // Assign the rectangle a name by 
            // registering it with the page, so that
            // it can be targeted by storyboard
            // animations.
            this.RegisterName(myRectangle.Name, myRectangle);
            myRectangle.Width = 100;
            myRectangle.Height = 100;
            myRectangle.Fill = Brushes.Blue;
            myStackPanel.Children.Add(myRectangle);

            //
            // Create an animation and a storyboard to animate the
            // rectangle.
            //
            DoubleAnimation myDoubleAnimation = new DoubleAnimation();
            myDoubleAnimation.From = 1.0;
            myDoubleAnimation.To = 0.0;
            myDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(5000));
            myDoubleAnimation.AutoReverse = true;

            // Create the storyboard.
            myStoryboard = new Storyboard();
            myStoryboard.Children.Add(myDoubleAnimation);
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
            Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(Rectangle.OpacityProperty));

            //
            // Create some buttons to control the storyboard
            // and a panel to contain them.
            //
            StackPanel buttonPanel = new StackPanel();
            buttonPanel.Orientation = Orientation.Horizontal;
            Button beginButton = new Button();
            beginButton.Content = "Begin";
            beginButton.Click += new RoutedEventHandler(beginButton_Clicked);
            buttonPanel.Children.Add(beginButton);
            Button pauseButton = new Button();
            pauseButton.Content = "Pause";
            pauseButton.Click += new RoutedEventHandler(pauseButton_Clicked);
            buttonPanel.Children.Add(pauseButton);
            Button resumeButton = new Button();
            resumeButton.Content = "Resume";
            resumeButton.Click += new RoutedEventHandler(resumeButton_Clicked);
            buttonPanel.Children.Add(resumeButton);
            Button skipToFillButton = new Button();
            skipToFillButton.Content = "Skip to Fill";
            skipToFillButton.Click += new RoutedEventHandler(skipToFillButton_Clicked);
            buttonPanel.Children.Add(skipToFillButton);
            Button setSpeedRatioButton = new Button();
            setSpeedRatioButton.Content = "Triple Speed";
            setSpeedRatioButton.Click += new RoutedEventHandler(setSpeedRatioButton_Clicked);
            buttonPanel.Children.Add(setSpeedRatioButton);
            Button stopButton = new Button();
            stopButton.Content = "Stop";
            stopButton.Click += new RoutedEventHandler(stopButton_Clicked);
            buttonPanel.Children.Add(stopButton);
            myStackPanel.Children.Add(buttonPanel);
            this.Content = myStackPanel;        


        }

        // Begins the storyboard.
        private void beginButton_Clicked(object sender, RoutedEventArgs args)
        {
            // Specifying "true" as the second Begin parameter
            // makes this storyboard controllable.
            myStoryboard.Begin(this, true);

        }

        // Pauses the storyboard.
        private void pauseButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.Pause(this);

        }

        // Resumes the storyboard.
        private void resumeButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.Resume(this);

        }

        // Advances the storyboard to its fill period.
        private void skipToFillButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.SkipToFill(this);

        }

        // Updates the storyboard's speed.
        private void setSpeedRatioButton_Clicked(object sender, RoutedEventArgs args)
        {
            // Makes the storyboard progress three times as fast as normal.
            myStoryboard.SetSpeedRatio(this, 3);

        }

        // Stops the storyboard.
        private void stopButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.Stop(this);

        }         

    }

}

Animer dans un style

Vous pouvez utiliser des objets Storyboard pour définir des animations dans un Style. Animer avec un Storyboard dans un Style est semblable à l'utilisation d'un Storyboard ailleurs, avec les trois exceptions suivantes :

  • Vous ne spécifiez pas de TargetName ; le Storyboard cible toujours l'élément auquel est appliqué le Style. Pour cibler des objets Freezable, vous devez utiliser le ciblage indirect. Pour plus d'informations sur le ciblage indirect, consultez la section Ciblage indirect.

  • Vous ne pouvez pas spécifier de SourceName pour un EventTrigger ou un Trigger.

  • Vous ne pouvez pas utiliser de références à une ressource dynamique ou d'expressions de liaison de données pour définir Storyboard ou des valeurs de propriété d'animation. La raison est que tout ce qui se trouve à l'intérieur d'un Style doit être thread-safe, et que le système d'horloge doit Freezeles objets Storyboard pour les rendre thread-safe. Un Storyboard ne peut pas être figé si lui ou ses chronologies enfants contiennent des références à une ressource dynamique ou des expressions de liaison de données. Pour plus d'informations sur le blocage et d'autres fonctionnalités Freezable, consultez Vue d'ensemble des objets Freezable.

  • En XAML, vous ne pouvez pas déclarer de gestionnaires d'événements pour Storyboard ou les événements d'animation.

Pour obtenir un exemple qui indique comment définir une table de montage séquentiel dans un style, consultez l'exemple Comment : animer dans un style.

Animer dans un modèle de contrôle

Vous pouvez utiliser des objets Storyboard pour définir des animations dans un ControlTemplate. Animer avec un Storyboard dans un ControlTemplate est semblable à l'utilisation d'un Storyboard ailleurs, avec les deux exceptions suivantes :

  • Le TargetName peut faire référence uniquement aux objets enfants du ControlTemplate. Si TargetName n'est pas spécifié, l'animation cible l'élément auquel est appliqué le ControlTemplate.

  • Le SourceName pour un EventTrigger ou un Trigger peut faire référence uniquement aux objets enfants du ControlTemplate.

  • Vous ne pouvez pas utiliser de références à une ressource dynamique ou d'expressions de liaison de données pour définir Storyboard ou des valeurs de propriété d'animation. La raison est que tout ce qui se trouve à l'intérieur d'un ControlTemplate doit être thread-safe, et que le système d'horloge doit Freezeles objets Storyboard pour les rendre thread-safe. Un Storyboard ne peut pas être figé si lui ou ses chronologies enfants contiennent des références à une ressource dynamique ou des expressions de liaison de données. Pour plus d'informations sur le blocage et d'autres fonctionnalités Freezable, consultez Vue d'ensemble des objets Freezable.

  • En XAML, vous ne pouvez pas déclarer de gestionnaires d'événements pour Storyboard ou les événements d'animation.

Pour obtenir un exemple qui indique comment définir une table de montage séquentiel dans un ControlTemplate, consultez l'exemple Comment : effectuer une animation dans un ControlTemplate.

Animer lorsqu'une valeur de propriété est modifiée

Dans les styles et les modèles de contrôle, vous pouvez utiliser des objets déclencheurs pour démarrer une table de montage séquentiel lorsqu'une propriété change. Pour obtenir des exemples, consultez Comment : déclencher une animation en cas de modification d'une valeur de propriété et Comment : effectuer une animation dans un ControlTemplate.

Les animations appliquées par les objets de propriété Trigger se comportent d'une façon plus complexe que les animations EventTrigger ou les animations démarrées à l'aide des méthodes Storyboard. Elles "procèdent au transfert" avec les animations définies par d'autres objets Trigger, mais composent avec des EventTrigger et des animations déclenchées par méthode.

Voir aussi

Concepts

Vue d'ensemble de l'animation

Vue d'ensemble des techniques d'animation de propriétés

Vue d'ensemble des objets Freezable