Información general sobre objetos Storyboard

Este tema proporciona información sobre cómo usar objetos Storyboard para organizar y aplicar animaciones. Describe cómo manipular interactivamente objetos Storyboard así como la sintaxis de establecimiento indirecto del destino de propiedades.

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 las propiedades adjuntas, consulte Información general sobre propiedades asociadas.

Qué es un guion 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 de contenedor se derivan de la clase TimelineGroup e incluyen ParallelTimeline y Storyboard.

Un Storyboard es un tipo de escala de tiempo de contenedor que proporciona información de establecimiento de destino para las escalas de tiempo que contiene. Un guion gráfico puede contener cualquier tipo de Timeline, incluso otras escalas de tiempo de contenedor 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 guion gráfico

Un Storyboard se puede utilizar para animar propiedades de dependencia de clases que se pueden animar (para obtener más información sobre qué hace que una clase se pueda animar, consulte Información general sobre animaciones). Sin embargo, como los guiones gráficos son una característica de nivel de marco, el objeto debe pertenecer al NameScope de un FrameworkElement o un FrameworkContentElement.

Por ejemplo, puede ejecutar un Storyboard para hacer lo siguiente:

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

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

Para usar un Storyboard para organizar y aplicar animaciones, debe agregar las animaciones como escalas de tiempo secundarias en el Storyboard. La clase Storyboard proporciona las propiedades adjuntas 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, inicie el Storyboard mediante una acción desencadenadora o un método. En XAML se usa un objeto BeginStoryboard con un EventTrigger, Trigger o DataTrigger. En el código también puede usar el método Begin.

En la tabla siguiente se muestran las distintas ubicaciones en las 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 guión gráfico a las instancias de un objeto, en lugar de en 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 Animar una propiedad mediante un guión gráfico
BeginStoryboard y una propiedad Trigger No Activar una animación al cambiar el valor de una propiedad
BeginStoryboard y una propiedad MultiTrigger No Ejemplo de clase MultiTrigger
BeginStoryboard y un DataTrigger No Cómo: Activar una animación cuando se cambian datos
BeginStoryboard y un MultiDataTrigger No Ejemplo de clase MultiDataTrigger
Método Begin No N.º No Animar una propiedad mediante un guión gráfico

En el ejemplo siguiente se usa un objeto Storyboard para animar el Width de un elemento Rectangle y el Color de un SolidColorBrush utilizado para pintar ese Rectangle.

<!-- This example shows how to animate with a storyboard.-->
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://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>
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 de TargetName y TargetProperty con más 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. La propiedad que se va a animar se especifica de forma directa: simplemente, establezca TargetProperty con el nombre de la propiedad que va a animar. Especifique el nombre del objeto cuya propiedad quiere animar mediante el establecimiento de la propiedad Storyboard.TargetName en la animación.

Precaución

Aunque puede usar la propiedad Target para enlazar directamente a un objeto como alternativa a TargetName, no se puede serializar. No hay ninguna garantía de que se pueda hacer referencia correctamente al objeto Target en XAML.

Para que funcione la propiedad TargetName, el objeto de destino debe tener un nombre. Asignar un nombre a un FrameworkElement o 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. Algunos ejemplos de elementos de marco incluyen 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. Entre los ejemplos de elementos de contenido del marco se incluyen 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 herencia de su página de tipo.

Para habilitar el establecimiento del destino de un elemento de marco o un elemento de contenido de marco en XAML, debe establecer su propiedad Name. En el código también deberá usar el método RegisterName para registrar el nombre del elemento con el elemento para el que ha creado un NameScope.

En el ejemplo siguiente, tomado del ejemplo anterior, se asigna al nombre MyRectangle un Rectangle, un tipo de FrameworkElement.

<Rectangle Name="MyRectangle"
  Width="100"
  Height="100">
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));

Los tipos Freezable son las clases que heredan de la clase Freezable. Algunos ejemplos de Freezable incluyen SolidColorBrush, RotateTransform y GradientStop.

Para habilitar el establecimiento del destino de un Freezable mediante una animación en XAML, use x:Name Directive para asignarle un nombre. En el código use un método RegisterName para registrar su nombre con el elemento para el que ha creado un NameScope.

En el ejemplo siguiente se asigna un nombre a un objeto Freezable.

<SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
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));

Los objetos Storyboard usan ámbitos de nombre para resolver la propiedad TargetName. Para obtener más información sobre los ámbitos de nombres de 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 un nombre no se puede asignar a un objeto Freezable. Por ejemplo, si un Freezable se declara como un recurso o se usa para establecer un valor de una propiedad en un estilo, no se le puede dar 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

En ocasiones una animación no puede establecer directamente como destino un Freezable, como cuando el 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 le asigna el nombre del elemento al que «pertenece» el Freezable. Por ejemplo, un SolidColorBrush utilizado para establecer el Fill de un elemento rectángulo pertenece a ese rectángulo. Para animar el pincel, se establece la propiedad de la animación TargetProperty 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 Freezable que se desea animar.

<ColorAnimation 
  Storyboard.TargetName="Rectangle01"
  Storyboard.TargetProperty="Fill.Color"
  From="Blue" To="AliceBlue" Duration="0:0:1" />
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 creará un clon y ese clon se animará. Cuando esto sucede, la propiedad del objeto original HasAnimatedProperties continúa devolviendo false, porque en realidad el objeto original no está animado. 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, puede suponer que el Background de un botón determinado se ha establecido con un SolidColorBrush e intentar animar su color, cuando en realidad se ha usado un LinearGradientBrush para establecer el fondo del botón. En esos casos no se inicia una excepción; se produce un error cuando la animación intenta provocar un efecto visual, ya que 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, utilice la siguiente sintaxis.

Sintaxis de las propiedades
ElementPropertyName . FreezablePropertyName

Where

  • ElementPropertyName es la propiedad del FrameworkElement que se ha usado para establecer el Freezable, y

  • FreezablePropertyName es la propiedad del Freezable que se va a animar.

En el código siguiente se muestra cómo animar el Color de un SolidColorBrush utilizado 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.

Sintaxis de path
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 quiere animar una de las transformaciones que contiene.

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

El código siguiente muestra cómo animar la propiedad Angle del RotateTransform que se muestra 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 código se crea un objeto PropertyPath. Cuando se crea PropertyPath, se especifican un 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 del identificador es para la propiedad del FrameworkElement o FrameworkContentElement que se ha usado para establecer el Freezable. El siguiente campo de identificador representa la propiedad del Freezable establecida como destino. Se puede ver como una cadena de propiedades que conecta el Freezable al objeto FrameworkElement.

A continuación se muestra un ejemplo de cadena de propiedades de dependencia cuyo destino es el Color de un SolidColorBrush que se ha usado para establecer el Fill de un elemento de rectángulo.

DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};

También necesita especificar un Path. Un Path es un String que indica al Path cómo interpretar su PathParameters. Se usa la siguiente sintaxis:

Sintaxis de ruta de acceso de propiedad
( OwnerPropertyArrayIndex ).( FreezablePropertyArrayIndex )

Where

  • OwnerPropertyArrayIndex es el índice de la matriz DependencyProperty que contiene el identificador de la propiedad del objeto FrameworkElement que se ha usado para establecer el Freezable, y

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

En el ejemplo siguiente se muestra el Path que acompañaría al PathParameters definido en el ejemplo anterior.

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

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


// 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 quiere animar una de las transformaciones que contiene.

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

Para establecer como destino un Freezable en una colección, debe usar la siguiente sintaxis de ruta de acceso.

Sintaxis de path
( OwnerPropertyArrayIndex ).( CollectionChildrenPropertyArrayIndex ) [ CollectionIndex ].( FreezablePropertyArrayIndex )

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

Para establecer como destino la propiedad Angle del RotateTransform, la segunda transformación del TransformGroup, se usarían los siguientes Path y PathParameters.

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 un RotateTransform contenido en un TransformGroup.

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 como destino un Freezable, empezando con un FrameworkElement o FrameworkContentElement y creando una cadena de propiedades a una subpropiedad Freezable. También puede usar un Freezable como punto inicial y establecer indirectamente como destino una de sus subpropiedades Freezable. Se aplica una restricción adicional al usar Freezable como punto de partida para el establecimiento indirecto de destinos: el Freezable inicial y todos los Freezable entre él y la subpropiedad establecida indirectamente como destino no pueden estar inmovilizados.

Controlar guiones gráficos de forma interactiva en XAML

Para iniciar un guion 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 e inicia el guion gráfico. (Para obtener más información sobre 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 guion gráfico controlable. A continuación, podrá controlar interactivamente el guión gráfico una vez iniciado. Esta 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="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://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 de desencadenador. En el código también puede controlar un guion gráfico mediante métodos interactivos de la clase Storyboard. Para hacer que un Storyboard sea interactivo con código, debe usar la sobrecarga adecuada en el método Begin del guion gráfico y especificar true para hacerlo controlable. Vea la página Begin(FrameworkElement, Boolean) para obtener más información.

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

La ventaja de usar estos métodos es que no es necesario crear objetos Trigger ni TriggerAction; sólo necesita una referencia al controlable Storyboard que quiere manipular.

Nota:

Todas las acciones interactivas realizadas en un Clock, y por lo tanto también en un Storyboard, se producirán en el siguiente tic del motor de sincronización, que ocurrirá poco antes de la siguiente representación. Por ejemplo, si utiliza el método Seek para saltar a otro punto de la animación, el valor de propiedad no cambia al instante, sino que lo hace 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.

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

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

Animar en un estilo

Puede usar objetos Storyboard para definir animaciones en un Style. La animación con un Storyboard en un Style es similar al uso de un Storyboard en cualquier otro lugar, con las tres excepciones siguientes:

  • No se especifica un TargetName; el Storyboard siempre establece como destino el elemento al que se aplica Style. Para establecer como destino objetos Freezable, debe usar 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 ni valores de propiedades de animación. El motivo es que todo lo que está dentro de un Style debe ser seguro para los hilos, y el sistema de sincronización debe Freeze los objetos Storyboard para que sean seguros para los hilos. No se puede inmovilizar un Storyboard si este 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 sobre cómo definir un guion gráfico en un estilo, consulte el ejemplo Animar en un estilo.

Animar en un ControlTemplate

Puede usar objetos Storyboard para definir animaciones en un ControlTemplate. La animación con un Storyboard en un ControlTemplate es similar al uso de un Storyboard en cualquier otra ubicación, aunque con las dos excepciones siguientes:

  • El TargetName solo puede hacer referencia a objetos secundarios del ControlTemplate. Si TargetName no está especificado, la animación establece como destino el elemento al que se aplica el ControlTemplate.

  • El SourceName para un EventTrigger o un Trigger solo puede hacer referencia a objetos secundarios del ControlTemplate.

  • No se pueden utilizar referencias de recursos dinámicos ni expresiones de enlace de datos para establecer Storyboard ni valores de propiedades de animación. El motivo es que todo lo que está dentro de un ControlTemplate debe ser seguro para los hilos, y el sistema de sincronización debe Freeze los objetos Storyboard para que sean seguros para los hilos. No se puede inmovilizar un Storyboard si este 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 guion gráfico en un ControlTemplate, consulte el ejemplo Animar en un ControlTemplate.

Animar cuando cambia el valor de una propiedad

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

Las animaciones aplicadas por los objetos Trigger de propiedad se comportan de manera más compleja que las animaciones EventTrigger o las animaciones que se inician mediante métodos Storyboard. "Entregan" con animaciones definidas por otros objetos Trigger, pero componen con EventTrigger y animaciones desencadenadas por métodos.

Vea también