Share via


Información general sobre objetos Storyboard

En este tema se muestra cómo utilizar objetos Storyboard para organizar y aplicar animaciones. Se describe cómo manipular interactivamente los objetos Storyboard y la sintaxis de establecimiento indirecto de propiedades de destino.

Requisitos previos

Para entender este tema, debe estar familiarizado con los distintos tipos de animaciones y sus características básicas. Para obtener una introducción a la animación, consulte Información general sobre animaciones. También debe saber utilizar las propiedades asociadas. Para obtener más información sobre propiedades asociadas, consulte Información general sobre propiedades asociadas.

¿Qué es un guión gráfico?

Las animaciones no son el único tipo útil de escala de tiempo. Se proporcionan otras clases de escalas de tiempo para ayudarle a organizar conjuntos de escalas de tiempo y para aplicar las escalas de tiempo a las propiedades. Las escalas de tiempo contenedoras se derivan de la clase TimelineGroup, e incluyen ParallelTimeline y Storyboard.

Un objeto Storyboard es un tipo de escala de tiempo contenedora que proporciona información de destino para las escalas de tiempo que contiene. Un guión gráfico puede contener cualquier tipo de Timeline, incluso otras escalas de tiempo contenedoras y animaciones. Los objetos Storyboard permiten combinar escalas de tiempo que afectan a diversos objetos y propiedades en un solo árbol de escalas de tiempo, lo que facilita la organización y el control de comportamientos de control de tiempo complejos. Por ejemplo, supongamos que desea un botón que haga estas tres cosas.

  • Aumentar de tamaño y cambiar de color cuando el usuario selecciona el botón.

  • Disminuir de tamaño y volver a su tamaño original cuando se hace clic en él.

  • Disminuir de tamaño y desvanecerse con un fundido al 50% de opacidad al deshabilitarse.

En este caso, tenemos varios conjuntos de animaciones que se aplican al mismo objeto, y hay que reproducirlas en distintos momentos, según cuál sea el estado del botón. Los objetos Storyboard permiten organizar las animaciones y aplicarlas en grupos a uno o más objetos.

¿Dónde se puede utilizar un guión gráfico?

Un Storyboard se utiliza para animar propiedades de dependencia de clases que se pueden animar (para obtener más información sobre qué hace que una clase pueda animar, vea Información general sobre animaciones). Sin embargo, al ser la creación de guiones gráficos una característica del marco de trabajo, el objeto debe pertenecer al NameScope de FrameworkElement o de FrameworkContentElement.

Por ejemplo, puede utilizar Storyboard para hacer lo siguiente:

Sin embargo, no se puede utilizar un Storyboard para animar un SolidColorBrush que no ha registrado su nombre con un FrameworkElement o FrameworkContentElement, o que no se ha utilizado para establecer una propiedad de un FrameworkElement o FrameworkContentElement.

Cómo aplicar animaciones mediante un guión gráfico

Si desea utilizar Storyboard para organizar y aplicar animaciones, agregue las animaciones como escalas de tiempo secundarias de Storyboard. La clase Storyboard proporciona las propiedades asociadas Storyboard.TargetName y Storyboard.TargetProperty. Se establecen estas propiedades de una animación para especificar su objeto y propiedad de destino.

Para aplicar animaciones a sus destinos, se comienza el Storyboard utilizando una acción desencadenante o un método. En XAML, se usa un objeto BeginStoryboard con un EventTriggerTrigger o DataTrigger. En el código, puede utilizar también el método Begin.

En la tabla siguiente se muestran los distintos espacios en que se admite cada técnica de comienzo de Storyboard: por instancia, estilo, plantilla de control y plantilla de datos. " Por instancia" se refiere a la técnica de aplicar directamente una animación o un guión gráfico a las instancias de un objeto, en lugar de un estilo, una plantilla de control o una plantilla de datos.

El guión gráfico se comienza utilizando…

Por instancia

Estilo

Plantilla de control

Plantilla de datos

Ejemplo

BeginStoryboard y EventTrigger

Cómo: Animar una propiedad utilizando un guión gráfico

BeginStoryboard y un Trigger de propiedad

No

Cómo: Activar una animación al cambiar el valor de una propiedad

BeginStoryboard y DataTrigger

No

Cómo: Activar una animación cuando se cambian datos

Método Begin

No

No

No

Cómo: Animar una propiedad utilizando un guión gráfico

En el ejemplo siguiente se utiliza Storyboard para animar el Width de un elemento Rectangle y Color de SolidColorBrush utilizado para pintar 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;
        } 
    }
}

En las secciones siguientes se describen las propiedades asociadas TargetName y TargetProperty con mayor detalle.

Establecer como destino elementos de marco, elementos de contenido de marco y elementos inmovilizables

En la sección anterior se menciona que, para que una animación encuentre su destino, debe conocer el nombre del mismo y la propiedad que debe animar. Especificar la propiedad que se va a animar es sencillo: basta con establecer Storyboard.TargetProperty en el nombre de la propiedad que se va a animar. Para especificar el nombre del objeto cuya la propiedad se desea animar se establece la propiedad Storyboard.TargetName de la animación.

Para que la propiedad TargetName funcione, el objeto de destino debe tener nombre. Asignar un nombre a un FrameworkElement o a un FrameworkContentElement en XAML es distinto que asignar un nombre a un objeto Freezable.

Los elementos de marco son las clases que heredan de la clase FrameworkElement. Ejemplos de elementos de marco son Window, DockPanel, Button y Rectangle. En esencia, todas las ventanas, los paneles y los controles son elementos. Los elementos de contenido de marco son las clases que heredan de la clase FrameworkContentElement. Ejemplos de elementos de contenido de marco son FlowDocument y Paragraph. Si no está seguro de si un tipo es un elemento de marco o un elemento de contenido de marco, compruebe si tiene una propiedad Name. Si la tiene, probablemente sea un elemento de marco o un elemento de contenido de marco. Para estar seguro, compruebe la sección de jerarquía de herenciade su página de tipo.

Para habilitar el establecimiento de destinos de un elemento de marco o un elemento de contenido de marco en XAML, se establece su propiedad Name. En el código también es preciso usar el método RegisterName para registrar el nombre del elemento en el elemento para el que se ha creado un NameScope.

En el ejemplo siguiente, tomado del ejemplo anterior, se asigna el nombre MyRectangle a un Rectangle, un tipo 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);

Una vez que tenga un nombre, puede animar una propiedad de ese elemento.

<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));

Los tipos Freezable son las clases que heredan de la clase Freezable. Ejemplos de elementos inmovilizables (objetos Freezable) incluyen SolidColorBrush, RotateTransform y GradientStop.

Para habilitar el establecimiento de destinos de un objeto Freezable por parte de una animación en XAML, se emplea x:Name (Directiva) para asignarle un nombre. En el código, también se usa el método RegisterName para registrar su nombre en el elemento para el que se ha creado un NameScope.

En el ejemplo siguiente se asigna un nombre a un objeto 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);

A continuación, ya se puede establecer el objeto como destino de una animación.

<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)); 

Los objetos Storyboard utilizan ámbitos de nombres para resolver la propiedad TargetName. Para obtener más información ámbitos de nombres en WPF, consulte Ámbitos de nombres XAML de WPF. Si se omite la propiedad TargetName, la animación establece como destino el elemento en el que se define o, en el caso de los estilos, el elemento al que se aplica el estilo.

A veces, no se puede asignar un nombre a un objeto Freezable. Por ejemplo, si un objeto Freezable se declara como recurso o se utiliza para establecer el valor de una propiedad de un estilo, no se le puede asignar un nombre. Al no tener nombre, no se puede establecer como destino directamente, pero sí de manera indirecta. En las secciones siguientes se describe cómo utilizar el establecimiento indirecto de destinos.

Establecimiento indirecto de destinos

Existen ocasiones en que una animación no puede establecer directamente un objeto Freezable como destino, como cuando el objeto Freezable se declara como recurso o se utiliza para establecer el valor de una propiedad en un estilo. En estos casos, aunque no se puede establecer como destino directamente, el objeto Freezable se puede animar. En lugar de establecer la propiedad TargetName con el nombre de Freezable, se establece en el nombre del elemento al que "pertenece" el objeto Freezable. Por ejemplo, SolidColorBrush utilizado para establecer la propiedad Fill de un elemento de rectángulo pertenece a ese rectángulo. Para animar el pincel, se establece la propiedad TargetProperty de la animación con una cadena de propiedades que se inicia con la propiedad del elemento de marco o elemento de contenido de marco para cuyo establecimiento se ha utilizado el objeto Freezable y que finaliza con la propiedad de Freezable que se desea animar.

<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);

Tenga en cuenta que, si se inmoviliza Freezable, se crea un clon y se anima ese clon. Cuando esto sucede, la propiedad HasAnimatedProperties del objeto original continúa devolviendo false, porque en realidad el objeto original no se anima. Para obtener más información sobre la clonación, consulte Información general sobre objetos Freezable.

Además, tenga en cuenta que, al utilizar el establecimiento indirecto de propiedades de destino, es posible establecer como destino objetos que no existen. Por ejemplo, podría suponer que la propiedad Background de un botón determinado se ha establecido mediante un pincel SolidColorBrush e intentar animar el color, aunque en realidad se ha utilizado un pincel LinearGradientBrush establecer el fondo del botón. En estos casos, no se inicia ninguna excepción; la animación no tiene ningún efecto visible porque LinearGradientBrush no reacciona a los cambios de la propiedad Color.

En las secciones siguientes se describe con mayor detalle la sintaxis de establecimiento indirecto de propiedades de destino.

Establecer de manera indirecta una propiedad de destino de un objeto inmovilizable en XAML

Para establecer como destino una propiedad de un objeto inmovilizable en XAML, se utiliza la sintaxis siguiente.

ElementPropertyName.FreezablePropertyName

Where

En el código siguiente se muestra cómo animar el Color de un SolidColorBrush que se utiliza para establecer el

Fill de un elemento de rectángulo.

<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>

En ocasiones, es preciso establecer como destino un objeto inmovilizable contenido en una colección o matriz.

Para establecer como destino un objeto inmovilizable contenido en una colección, se utiliza la sintaxis de ruta de acceso siguiente.

ElementPropertyName.Children[CollectionIndex].FreezablePropertyName

Donde CollectionIndex es el índice del objeto en su matriz o colección.

Por ejemplo, supongamos que un rectángulo tiene un recurso TransformGroup aplicado a su propiedad RenderTransform, y que desea animar una de las transformaciones que contiene.

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

En el código siguiente se muestra cómo animar la propiedad Angle de la RotateTransform mostrada en el ejemplo anterior.

<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>  

Establecer de manera indirecta una propiedad de destino de un objeto inmovilizable mediante código

En el código, se crea un objeto PropertyPath. Al crear PropertyPath, se especifican las propiedades Path y PathParameters.

Para crear PathParameters, se crea una matriz de tipo DependencyProperty que contiene una lista de campos de identificadores de propiedades de dependencia. El primer campo de identificador es para la propiedad de FrameworkElement o FrameworkContentElement que se establecen mediante Freezable. El campo de identificador siguiente representa la propiedad de Freezable que se establece como destino. Piense en ello como una cadena de propiedades que conecta el objeto Freezable al objeto FrameworkElement.

A continuación se muestra ejemplo de cadena de propiedades de dependencia que establecen como destino el Color de un SolidColorBrush utilizado para establecer el Fill de un elemento de rectángulo.

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

También es preciso especificar una Path. Una Path es una String que indica a Path cómo debe interpretar sus PathParameters. Utiliza la sintaxis siguiente.

(OwnerPropertyArrayIndex).(FreezablePropertyArrayIndex)

Where

  • OwnerPropertyArrayIndex es el índice de la matriz de DependencyProperty que contiene el identificador de la propiedad del objeto FrameworkElement que se establece mediante Freezable y

  • FreezablePropertyArrayIndex es el índice de la matriz de DependencyProperty que contiene el identificador de la propiedad que se establece como destino.

En el ejemplo siguiente se muestra la Path que acompañaría a los PathParameters definidos en el ejemplo anterior.

            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)";

En el ejemplo siguiente se combina el código de los ejemplos anteriores para animar el Color de un SolidColorBrush utilizado para establecer el Fill de un elemento de rectángulo.


            ' 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);

En ocasiones, es preciso establecer como destino un objeto inmovilizable contenido en una colección o matriz. Por ejemplo, supongamos que un rectángulo tiene un recurso TransformGroup aplicado a su propiedad RenderTransform, y que desea animar una de las transformaciones que contiene.

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

Para establecer como destino un objeto Freezable contenido en una colección, se utiliza la sintaxis de ruta de acceso siguiente.

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

Donde CollectionIndex es el índice del objeto en su matriz o colección.

Para establecer como destino la propiedad Angle de RotateTransform, la segunda transformación de TransformGroup, se utilizan la Path y los PathParameters siguientes.

            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);

En el ejemplo siguiente se muestra el código completo para animar el Angle de una RotateTransform contenida en 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);

Establecimiento indirecto de destinos con un objeto inmovilizable como punto de partida

En las secciones anteriores se ha descrito cómo establecer indirectamente un objeto Freezable como destino comenzando por un FrameworkElement o un FrameworkContentElement y creando una cadena de propiedades a una subpropiedad de Freezable. También se puede utilizar un objeto Freezable como punto de partida y establecer indirectamente como destino una de las subpropiedades de Freezable. Cuando se utiliza un objeto Freezable como punto de partida para el establecimiento indirecto de destinos se aplica una restricción adicional: el objeto Freezable inicial y todos los objetos Freezable entre éste y la subpropiedad establecida indirectamente como destino no pueden estar inmovilizados.

Controlar guiones gráficos de forma interactiva en XAML

Para iniciar un guión gráfico en Extensible Application Markup Language (XAML), use una acción de desencadenador BeginStoryboard. BeginStoryboard distribuye las animaciones a los objetos y las propiedades que se animan y, a continuación, inicia el guión gráfico. (Para obtener información detallada acerca de este proceso, consulte Información general sobre sistemas de temporización y animación.) Si asigna un nombre a BeginStoryboard especificando su propiedad Name, lo convierte en un guión gráfico controlable. A continuación, podrá controlar interactivamente el guión gráfico una vez iniciado. Ésta es una lista de acciones de guión gráfico controlables que se utiliza con desencadenadores de eventos para controlar un guión gráfico.

En el ejemplo siguiente se utilizan acciones para controlar interactivamente un guión gráfico.

<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>

Controlar interactivamente un guión gráfico mediante código

En los ejemplos anteriores se muestra cómo animar mediante acciones desencadenantes. En el código, también puede controlar un guión gráfico mediante los métodos interactivos de la clase Storyboard. Para que un Storyboard sea interactivo en el código, debe utilizar la sobrecarga apropiada del método Begin del guión gráfico y especificar true para que sea controlable. Para obtener más información, consulte Begin(FrameworkElement, Boolean).

En la lista siguiente se muestran los métodos que se pueden utilizar para manipular Storyboard después de haberse iniciado:

La ventaja de utilizar estos métodos es que no es preciso crear objetos Trigger o TriggerAction; únicamente se necesita una referencia al Storyboard controlable que desea manipular.

Nota: todas las acciones interactivas llevadas a cabo sobre un objeto Clock y, por tanto, sobre un objeto Storyboard, se producen en el siguiente paso del motor de control de tiempo, lo que tiene lugar un poco antes de que se produzca la siguiente representación. Por ejemplo, si utiliza el método Seek para saltar a otro punto de una animación, el valor de la propiedad no cambia al instante, más bien, el valor cambia en el paso siguiente del motor de control de tiempo.

En el ejemplo siguiente se muestra cómo aplicar y controlar animaciones mediante los métodos interactivos de la clase 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);

        }         

    }

}

Animar en un estilo

Puede utilizar los objetos Storyboard para definir animaciones en una Style. Animar con un Storyboard en un Style es similar a utilizar un Storyboard en otros objetos, con las tres excepciones siguientes:

  • No se especifica un TargetName; Storyboard siempre establece como destino el elemento al que se aplica el Style. Para establecer como destino objetos Freezable, debe utilizar el establecimiento indirecto de destinos. Para obtener más información sobre el establecimiento indirecto de destinos, consulte la sección Establecimiento indirecto de destinos.

  • No se puede especificar un SourceName para un EventTrigger o un Trigger.

  • No se pueden utilizar referencias de recursos dinámicos ni expresiones de enlace de datos para establecer Storyboard o valores de propiedades de animación. Esto se debe a que todo lo que esté dentro de un Style debe ser seguro para subprocesos y el sistema de control de tiempo debe inmovilizar (Freeze) los objetos Storyboard a fin de hacerlos seguros para subprocesos. No se puede inmovilizar un Storyboard si éste o sus escalas de tiempo secundarias contienen referencias de recursos dinámicos o expresiones de enlace de datos. Para obtener más información sobre la inmovilización y otras características de Freezable, consulte Información general sobre objetos Freezable.

  • En XAML, no se pueden declarar controladores de eventos para Storyboard ni eventos de animación.

Para obtener un ejemplo en el que se muestra cómo definir un guión gráfico en un estilo, consulte el ejemplo Cómo: Animar en un estilo.

Animar en una ControlTemplate

Puede utilizar los objetos Storyboard para definir animaciones en una ControlTemplate. Animar con un Storyboard en una ControlTemplate es similar a utilizar un Storyboard en otros objetos, con las dos excepciones siguientes:

  • TargetName sólo puede hacer referencia a los objetos secundarios de ControlTemplate. Si no se especifica TargetName, la animación establece como destino el elemento al que se aplica ControlTemplate.

  • El SourceName de un EventTrigger o de un Trigger únicamente puede hacer referencia a objetos secundarios de ControlTemplate.

  • No se pueden utilizar referencias de recursos dinámicos ni expresiones de enlace de datos para establecer Storyboard o valores de propiedades de animación. Esto se debe a que todo lo que esté dentro de un ControlTemplate debe ser seguro para subprocesos y el sistema de control de tiempo debe inmovilizar (Freeze) los objetos Storyboard a fin de hacerlos seguros para subprocesos. No se puede inmovilizar un Storyboard si éste o sus escalas de tiempo secundarias contienen referencias de recursos dinámicos o expresiones de enlace de datos. Para obtener más información sobre la inmovilización y otras características de Freezable, consulte Información general sobre objetos Freezable.

  • En XAML, no se pueden declarar controladores de eventos para Storyboard ni eventos de animación.

Para obtener un ejemplo en el que se muestra cómo definir un guión gráfico en una ControlTemplate, consulte el ejemplo Cómo: Animar en un ControlTemplate.

Animar cambia el valor de una propiedad

En los estilos y las plantillas de control, puede utilizar objetos Trigger para iniciar un guión gráfico cuando cambia una propiedad. Para obtener ejemplos, vea Cómo: Activar una animación al cambiar el valor de una propiedad y Cómo: Animar en un ControlTemplate.

Las animaciones aplicadas por objetos Trigger de propiedad se comportan de un modo más complejo que las animaciones de EventTrigger o que aquéllas que se inician mediante métodos de Storyboard. Se "continúan" con animaciones definidas por otros objetos Trigger, pero se crean con EventTrigger y animaciones activadas por métodos.

Vea también

Conceptos

Información general sobre animaciones

Información general sobre técnicas de animación de propiedades

Información general sobre objetos Freezable