Übersicht über Storyboards

In diesem Artikel erfahren Sie, wie Sie Storyboard-Objekte zum Organisieren und Anwenden von Animationen verwenden. Es beschreibt, wie Sie Storyboard-Objekte interaktiv bearbeiten, und erläutert die indirekte Eigenschaftenadressierungssyntax.

Voraussetzungen

Als Voraussetzung für dieses Thema sollten Sie mit den unterschiedlichen Animationstypen und ihren grundlegenden Funktionen vertraut sein. Eine Einführung zu Animationen finden Sie unter Übersicht über Animationen. Sie sollten auch wissen, wie angefügte Eigenschaften verwendet werden. Weitere Informationen zu angefügten Eigenschaften finden Sie in der Übersicht über angefügte Eigenschaften.

Was ist ein Storyboard?

Animationen sind nicht der einzige nützliche Zeitachsentyp. Andere Zeitachsenklassen werden bereitgestellt,um Ihnen beim Organisieren von Sätzen von Zeitachsen zu helfen und Zeitachsen auf Eigenschaften anzuwenden. Containerzeitachsen sind von der TimelineGroup-Klasse abgeleitet und schließen ParallelTimeline und Storyboard ein.

Storyboard ist ein Typ einer Containerzeitachse, der Adressierungsinformationen für die darin enthaltenen Zeitachsen liefert. Ein Storyboard kann jeden Typ von Timeline enthalten, einschließlich anderer Containerzeitachsen und Animationen. Mit Storyboard-Objekten können Sie Zeitachsen kombinieren, die eine Vielzahl von Objekten und Eigenschaften zu einer einzigen Zeitachsenstruktur kombinieren und dadurch die Organisation und Steuerung komplexer Zeitsteuerungsverhalten vereinfachen. Nehmen wir beispielsweise an, Sie wünschen eine Schaltfläche, die die folgenden drei Aktionen ausführt.

  • Vergrößern und Ändern der Farbe, wenn der Benutzer die Schaltfläche auswählt.

  • Verkleinern und dann wieder die ursprüngliche Größe annehmen, wenn auf sie geklickt wird.

  • Verkleinern und zu 50 % Deckkraft ausgeblendet werden, wenn sie deaktiviert wird.

In diesem Fall sind mehrere Sätze von Animationen vorhanden, die auf das gleiche Objekt angewendet werden und die Sie abhängig vom Zustand der Schaltfläche zu unterschiedlichen Zeitpunkten wiedergeben möchten. Storyboard-Objekte ermöglichen Ihnen, Animationen zu organisieren und in Gruppen auf ein oder mehrere Objekte anzuwenden.

Wo können Sie ein Storyboard verwenden?

Sie können mit einem Storyboard Abhängigkeitseigenschaften von animierbaren Klassen animieren (weitere Informationen dazu, was eine Klasse animierbar macht, finden Sie unter Übersicht über Animationen). Da der Einsatz von Storyboards ein Feature auf Frameworkebene ist, muss das Objekt zum NameScope eines FrameworkElement oder eines FrameworkContentElement gehören.

Sie können beispielsweise mit einem Storyboard Folgendes ausführen:

Sie können jedoch kein Storyboard verwenden, um einen SolidColorBrush zu animieren, der seinen Namen nicht mit einem FrameworkElement oder FrameworkContentElement registriert hat, oder nicht zum Festlegen einer Eigenschaft eines FrameworkElement oder FrameworkContentElement verwendet wurde.

Anwenden von Animationen mit einem Storyboard

Um ein Storyboard zum Organisieren und Anwenden von Animationen zu verwenden, fügen Sie diese als untergeordnete Zeitachsen dem Storyboard hinzu. Die Storyboard-Klasse stellt die Storyboard.TargetName und Storyboard.TargetProperty angefügten Eigenschaften bereit. Sie können diese Eigenschaften für eine Animation einstellen, um deren Zielobjekt und Eigenschaft festzulegen.

Um Animationen auf ihre Ziele anzuwenden, starten Sie das Storyboard mit einer Triggeraktion oder einer Methode. In XAML verwenden Sie ein BeginStoryboard-Objekt mit einem EventTrigger, Trigger oder DataTrigger. Im Code können Sie auch die Begin-Methode nutzen.

Die folgende Tabelle zeigt die unterschiedlichen Stellen, an denen die einzelnen Storyboard-Startmethoden unterstützt werden: pro Instanz, Stil, Steuerelementvorlage und Datenvorlage. „Pro Instanz“ bezieht sich auf das Verfahren, eine Animation oder ein Storyboard direkt auf Instanzen eines Objekts und nicht in einem Stil, einer Steuerelementvorlage oder einer Datenvorlage anzuwenden.

Storyboard wird gestartet mit... Pro Instanz Stil Steuerelementvorlage Datenvorlage Beispiel
BeginStoryboard und einer EventTrigger Ja Ja Ja Ja Animieren einer Eigenschaft unter Verwendung eines Storyboards
BeginStoryboardund Hinzufügen einer Eigenschaft Trigger Nein Ja Ja Ja Auslösen einer Animation, wenn sich eine Eigenschaft ändert
BeginStoryboardund Hinzufügen einer Eigenschaft MultiTrigger Nein Ja Ja Ja Beispiel für multiTrigger-Klasse
BeginStoryboard und eine DataTrigger Nein Ja Ja Ja Vorgehensweise: Auslösen einer Animation bei Änderung eines Eigenschaftswerts
BeginStoryboard und ein MultiDataTrigger Nein Ja Ja Ja Beispiel für MultiDataTrigger-Klasse
Begin-Methode Ja Nr. Nr. Nein Animieren einer Eigenschaft unter Verwendung eines Storyboards

Im folgenden Beispiel wird mit einem Storyboard die Width eines Rectangle-Elements animiert, und die Color eines SolidColorBrush verwendet, um das Rectangle zu malen.

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

In den folgenden Abschnitten werden die angefügten Eigenschaften TargetName und TargetProperty ausführlicher beschrieben.

Frameworkelemente, Frameworkinhaltselemente und Freezable-Objekte

Im vorherigen Abschnitt wurde erwähnt, dass eine Animation den Namen des Ziels und die zu animierende Eigenschaft kennen muss, um ihr Ziel zu finden. Das Angeben der zu animierenden Eigenschaft ist leicht: Legen Sie einfach TargetProperty als den Namen der zu animierenden Eigenschaft fest. Geben Sie den Namen des Objekts, dessen Eigenschaft animiert werden soll, durch Festlegen der Storyboard.TargetName-Eigenschaft auf der Animation an.

Achtung

Sie können zwar die Target-Eigenschaft verwenden, um direkt an ein Objekt als Alternative zu TargetName zu binden. Dies ist allerdings nicht serialisierbar. Es ist nicht garantiert, dass XAML korrekt auf das Target-Objekt verweisen kann.

Damit die TargetName-Eigenschaft funktioniert, muss das Zielobjekt einen Namen haben. Die Zuweisung eines Namens in XAML an ein FrameworkElement oder ein FrameworkContentElement unterscheidet sich von der Zuweisung eines Namens an ein Freezable-Objekt.

Frameworkelemente sind die Klassen, die von der FrameworkElement-Klasse erben. Beispiele für Frameworkelemente sind Window, DockPanel, Button, und Rectangle. Im Wesentlichen sind alle Fenster, Panels und Steuerelemente Elemente. Framework-Inhaltselemente sind diejenigen Klassen, die von der FrameworkContentElement-Klasse erben. Beispiele für Framework-Inhaltselemente sind FlowDocument und Paragraph. Wenn Sie nicht sicher sind, ob ein Typ ein Frameworkelement oder ein Frameworkinhaltselement ist, überprüfen Sie, ob es über eine Name-Eigenschaft verfügt. Wenn dies der Fall ist, handelt es sich aller Wahrscheinlichkeit nach um ein Frameworkelement oder ein Frameworkinhaltselement. Um sicherzugehen, überprüfen Sie den Vererbungshierarchie-Abschnitt seiner Typ-Seite.

Zum Aktivieren des Ziels eines Frameworkelements oder eines Frameworkinhaltselements in XAML legen Sie die Name-Eigenschaft fest. Im Code müssen Sie auch mit der RegisterName-Methode den Namen des Elements bei dem Element registrieren, für das Sie einen NameScope erstellt haben.

Das folgende Beispiel bedient sich beim vorigen Beispiel und weist den Namen MyRectangle, ein Rectangle, einen Typen von FrameworkElement zu.

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

Wenn der Name vergeben wurde, können Sie eine Eigenschaft des Elements animieren.

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

Freezable-Typen sind die Klassen, die von der Freezable-Klasse erben. Beispiele für Freezable sind etwa SolidColorBrush, RotateTransform und GradientStop.

Um das Adressieren eines Freezable durch eine Animation in XAML zu ermöglichen, verwenden Sie die x:Name-Anweisung, um ihm einen Namen zuzuweisen. Im Code registrieren Sie seinen Namen mit der RegisterName-Methode beim Element, für das Sie einen NameScope erstellt haben.

Im folgenden Beispiel wird einem Freezable-Objekt ein Name zugewiesen.

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

Das Objekt kann dann animiert werden.

<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-Objekte verwenden Namensbereiche, um die TargetName-Eigenschaft aufzulösen. Weitere Informationen über WPF-Namescopes finden Sie unter WPF-XAML-Namescopes. Wenn die TargetName-Eigenschaft weggelassen wird, adressiert die Animation das Element, auf dem sie definiert ist, oder, im Fall von Stilen, das den Stil formatierte Element.

Manchmal kann ein Namen nicht einem Freezable-Objekt zugewiesen werden. Beispiel: Wenn ein Freezable-Objekt als Ressource deklariert ist oder verwendet wird, um einen Eigenschaftenwert in einem Stil festzulegen, kann ihm kein Name zugewiesen werden. Da es keinen Namen hat, kann es zwar kein direktes, aber ein indirektes Ziel sein. In den folgenden Abschnitten wird beschrieben, wie indirekte Ziele verwendet werden.

Indirektes Ziel

Manchmal kann ein Freezable-Element nicht das direkte Ziel einer Animation sein, z. B. wenn das Freezable-Objekt als Ressource deklariert oder verwendet wird, um einen Eigenschaftenwert in einem Stil festzulegen. In diesen Fällen können Sie das Freezable-Objekt animieren, obwohl Sie es nicht als direktes Ziel verwenden können. Anstatt die TargetName-Eigenschaft als den Namen des Freezable-Elements festzulegen, geben Sie ihm den Namen des Elements, zu dem das Freezable-Element „gehört“. Beispielsweise gehört ein zum Festlegen des Fill eines Rechteckelements verwendeter SolidColorBrush zu diesem Rechteck. Zum Animieren des Pinsels legen Sie die TargetProperty der Animation mit einer Kette von Eigenschaften fest, die an der Eigenschaft des Frameworkelements oder Frameworkinhaltselements beginnt, mit dem das Freezable-Objekt festgelegt wurde, und mit der zu animierenden Freezable-Eigenschaft endet.

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

Beachten Sie, dass ein Klon erstellt und animiert wird, wenn das Freezable-Objekt fixiert (frozen) ist. Ist dies der Fall, gibt die HasAnimatedProperties-Eigenschaft des Originalobjekts weiterhin false zurück, da das Originalobjekt nicht animiert ist. Weitere Informationen zum Klonen finden Sie unter Übersicht über Freezable-Objekte.

Beachten Sie außerdem, dass nicht vorhandene Objekte Ziel sein können, wenn indirekte Eigenschaftenziele verwendet werden. Sie können beispielsweise annehmen, dass der Background einer bestimmten Schaltfläche mit einem SolidColorBrush festgelegt wurde, und versuchen, seine Farbe zu animieren, wenn ein LinearGradientBrush verwendet wurde, um den Hintergrund der Schaltfläche festzulegen. In diesen Fällen wird keine Ausnahme ausgelöst. Die Animation hat keinen sichtbaren Effekt, da LinearGradientBrush nicht auf Änderungen an der Color-Eigenschaft reagiert.

In den folgenden Abschnitten wird die Syntax für indirekte Eigenschaftenziele ausführlicher erklärt.

Verwenden einer Eigenschaft eines Freezable-Objekts in XAML als indirektes Ziel

Um eine Eigenschaft eines Freezable-Objekts in XAML als Ziel zu verwenden, geben Sie die folgende Syntax an.

Eigenschaftssyntax
ElementPropertyName.FreezablePropertyName

Hierbei gilt:

  • ElementPropertyName ist die Eigenschaft des FrameworkElement, mit dem das Freezable festgelegt wird.

  • FreezablePropertyName ist die zu animierende Eigenschaft des Freezable.

Im folgenden Code wird gezeigt, wie Sie die Color eines SolidColorBrush animieren, der zum Festlegen des Fill eines Rechteckelements verwendet wird.

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

Manchmal müssen Sie ein in einer Auflistung oder einem Array enthaltenes Freezable-Objekt als Ziel verwenden.

Um ein in einer Auflistung enthaltenes Freezable-Objekt als Ziel zu verwenden, geben Sie die folgende Pfadsyntax an.

Pfadsyntax
ElementPropertyName.Children[CollectionIndex].FreezablePropertyName

Wobei CollectionIndex der Index des Objekts im Array oder in der Auflistung ist.

Nehmen wir beispielsweise an, dass eine TransformGroup-Ressource auf die RenderTransform-Eigenschaft eines Rechtecks angewendet wurde und Sie eine der enthaltenen Transformationen animieren möchten.

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

An diesem Code sehen Sie, wie Sie die Angle-Eigenschaft des RotateTransform aus dem vorherigen Beispiel animieren.

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

Verwenden einer Eigenschaft eines Freezable-Objekts im Code als indirektes Ziel

Erstellen Sie in Code ein PropertyPath-Objekt. Wenn Sie den PropertyPath erstellen, geben Sie einen Path und PathParameters an.

Zum Erstellen von PathParameters erstellen Sie ein Array des Typs DependencyProperty, das eine Liste von Abhängigkeitseigenschaften-Bezeichnerfeldern enthält. Das erste Bezeichnerfeld gehört der Eigenschaft des FrameworkElement oder FrameworkContentElement, mit dem das Freezable-Element festgelegt wird. Das nächste Bezeichnerfeld stellt die Eigenschaft des Freezable-Objekts dar, die das Ziel ist. Betrachten Sie sie als Kette von Eigenschaften, die das Freezable-Objekt mit dem FrameworkElement-Objekt verbindet.

Das folgende Beispiel veranschaulicht eine Abhängigkeitseigenschaftskette, die als Ziel die Color eines SolidColorBrush verwendet, mit dem die Fill eines Rechteckelements festgelegt wird.

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

Sie müssen auch eine Path spezifizieren. Ein Path ist eine String, die der Path mitteilt, wie ihre PathParameters interpretiert werden sollen. Die folgende Syntax wird verwendet.

Eigenschaftenpfadsyntax
(OwnerPropertyArrayIndex).(FreezablePropertyArrayIndex)

Hierbei gilt:

  • OwnerPropertyArrayIndex ist der Index des DependencyProperty-Arrays, das den Bezeichner der Eigenschaft des FrameworkElement-Objekts enthält, die mit dem Freezable-Element festgelegt wird.

  • FreezablePropertyArrayIndex ist der Index des DependencyProperty-Arrays ist, das den Bezeichner der als Ziel zu verwendenden Eigenschaft enthält.

Das folgende Beispiel zeigt den Path, der den im vorigen Beispiel definierten von PathParameters begleitet.

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

Im folgenden Beispiel wird der Code der vorherigen Beispiele combiniert, um die Color eines SolidColorBrush zu animieren, der zum Festlegen des Fill eines Rechteckelements verwendet wird.


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

Manchmal müssen Sie ein in einer Auflistung oder einem Array enthaltenes Freezable-Objekt als Ziel verwenden. Nehmen wir beispielsweise an, dass eine TransformGroup-Ressource auf die RenderTransform-Eigenschaft eines Rechtecks angewendet wurde und Sie eine der enthaltenen Transformationen animieren möchten.

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

Um ein in einer Auflistung enthaltenes Freezable-Element als Ziel zu verwenden, geben Sie die folgende Pfadsyntax an.

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

Wobei CollectionIndex der Index des Objekts im Array oder in der Auflistung ist.

Um die Angle-Eigenschaft der RotateTransform als Ziel zu verwenden, der zweiten Transformation in der TransformGroup, verwenden Sie den folgenden Path und diese 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);

Das folgende Beispiel enthält den vollständigen Code zum Animieren der Angle einer in einer TransformGroup enthaltenen RotateTransform.

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

Indirektes Ziel mit einem Freezable-Objekt als Ausgangspunkt

In den vorherigen Abschnitten wurde beschrieben, wie ein Freezable-Objekt als indirektes Ziel verwendet wird, indem mit einem FrameworkElement oder einem FrameworkContentElement begonnen und eine Eigenschaftenkette zu einer Freezable-Untereigenschaft erstellt wird. Sie können auch ein Freezable-Objekt als Ausgangspunkt und eine seiner Freezable-Untereigenschaften als indirektes Ziel verwenden. Eine zusätzliche Einschränkung gilt bei Verwendung eines Freezable-Objekts als Ausgangspunkt für das indirekte Ziel: Das erste Freezable-Objekt und jedes Freezable-Objekt zwischen diesem und der als indirektes Ziel verwendeten Untereigenschaft dürfen nicht fixiert sein.

Interaktives Steuern eines Storyboards in XAML

Verwenden Sie eine BeginStoryboard-Triggeraktion, um ein Storyboard in Extensible Application Markup Language (XAML) zu starten. BeginStoryboard verteilt die Animationen auf die zu animierenden Objekte und Eigenschaften und startet dann das Storyboard. (Ausführliche Informationen zu diesem Prozess finden Sie in der Übersicht über das Animations- und Zeitsteuerungssystem.) Wenn Sie dem BeginStoryboard-Objekt einen Namen geben, indem Sie seine Name-Eigenschaft angeben, machen Sie es zu einem steuerbaren Storyboard. Sie können das Storyboard dann interaktiv steuern, nachdem es gestartet wurde. Die folgende Liste enthält Aktionen des steuerbaren Storyboards, die Sie mit Ereignistriggern zum Steuern eines Storyboards verwenden.

Im folgenden Beispiel werden Aktionen des steuerbaren Storyboards zum interaktiven Steuern eines Storyboards verwendet.

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

Interaktives Steuern eines Storyboards mithilfe von Code

In den vorherigen Beispielen wurde gezeigt, wie mithilfe von Triggeraktionen animiert wird. Im Code können Sie ein Storyboard auch mithilfe von interaktiven Methoden der Storyboard-Klasse steuern. Damit ein Storyboard im Code interaktiv wird, müssen Sie die geeignete Überladung der Begin-Methode des Storyboards verwenden und true angeben, damit sie steuerbar wird. Weitere Informationen finden Sie unter Begin(FrameworkElement, Boolean).

Die folgende Liste enthält die Methoden, die verwendet werden können, um ein Storyboard nach seinem Start zu bearbeiten:

Die Methoden haben den Vorteil, dass Sie keine Trigger- oder TriggerAction-Objekte erstellen müssen. Sie benötigen lediglich einen Verweis zum steuerbaren Storyboard, das Sie bearbeiten möchten.

Hinweis

Alle auf einer Clock und daher auch auf einem Storyboard durchgeführten Aktionen treten beim nächsten Teilstrich der Zeitsteuerungs-Engine kurz vor dem nächsten Rendering auf. Wenn Sie beispielsweise mit der Seek-Methode zu einem anderen Punkt in einer Animation springen, wird der Wert der Eigenschaft nicht sofort geändert. Der Wert ändert sich vielmehr beim nächsten Teilstrich der Zeitsteuerungs-Engine.

Das folgende Beispiel zeigt, wie Animationen mit interaktiven Methoden der Storyboard-Klasse angewendet und gesteuert werden.

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

Animieren in einem Stil

Sie können Storyboard-Objekte verwenden, um Animationen in einem Style zu definieren. Die Animation mit einem Storyboard in einem Storyboard ähnelt der Verwendung eines Style an anderer Stelle, mit folgenden drei Ausnahmen:

  • Sie geben keinen TargetName an. Das Storyboard hat stets das Element als Ziel, auf das der Style angewendet wird. Um FreezableObjekte als Ziel zu verwenden, müssen Sie auf indirektes Zielen zurückgreifen. Weitere Informationen über indirekte Ziele finden Sie unter Indirektes Ziel.

  • Sie können keinen SourceName für einen EventTrigger oder Trigger spezifizieren.

  • Sie können nicht mit dynamischen Ressourcenverweisen oder Datenbindungsausdrücken einen Storyboard- oder Animationseigenschaftenwert festlegen. Dies ist darauf zurückzuführen, dass alle Bestandteile eines Style threadsicher sein müssen, und das Zeitsteuerungssystem muss FreezeStoryboard-Objekte fixieren, um sie threadsicher zu machen. Ein Storyboard kann nicht fixiert werden, wenn es oder seine untergeordneten Zeitachsen dynamische Ressourcenreferenzen oder Datenbindungsausdrücke enthalten. Weitere Informationen über das Fixieren und andere Freezable-Funktionen finden Sie unter Übersicht über Freezable-Objekte.

  • Sie können in XAML keine Ereignishandler für Storyboard oder Animationsereignisse deklarieren.

Ein Beispiel, das veranschaulicht, wie ein Storyboard in einem Stil definiert wird, finden Sie unter Animieren in einem Stil.

Animieren in einer ControlTemplate

Sie können Storyboard-Objekte verwenden, um Animationen in einem ControlTemplate zu definieren. Die Animation mit einem Storyboard in einem Storyboard ähnelt der Verwendung eines ControlTemplate an anderer Stelle, mit folgenden zwei Ausnahmen:

  • Der TargetName kann sich nur auf untergeordnete Elemente des ControlTemplate beziehen. Wenn TargetName nicht spezifiziert ist, benutzt die Animation des Element als Ziel, auf das das ControlTemplate angewendet ist.

  • Der SourceName für einen EventTrigger oder einen Trigger kann sich nur auf untergeordnete Objekte des ControlTemplate beziehen.

  • Sie können nicht mit dynamischen Ressourcenverweisen oder Datenbindungsausdrücken einen Storyboard- oder Animationseigenschaftenwert festlegen. Dies ist darauf zurückzuführen, dass alle Bestandteile eines ControlTemplate threadsicher sein müssen, und das Zeitsteuerungssystem muss FreezeStoryboard-Objekte fixieren, um sie threadsicher zu machen. Ein Storyboard kann nicht fixiert werden, wenn es oder seine untergeordneten Zeitachsen dynamische Ressourcenreferenzen oder Datenbindungsausdrücke enthalten. Weitere Informationen über das Fixieren und andere Freezable-Funktionen finden Sie unter Übersicht über Freezable-Objekte.

  • Sie können in XAML keine Ereignishandler für Storyboard oder Animationsereignisse deklarieren.

Ein Beispiel, das veranschaulicht, wie ein Storyboard in einem ControlTemplate definiert wird, finden Sie unter Animieren in einer ControlTemplate.

Animieren, wenn sich ein Eigenschaftswert ändert

In Stilen und Steuerelementvorlagen können Sie mit Triggerobjekten ein Storyboard starten, wenn sich eine Eigenschaft ändert. Beispiele finden Sie unter Auslösen einer Animation, wenn sich eine Eigenschaft ändert und Animieren in einer ControlTemplate.

Animationen, die durch Objekte mit der Eigenschaft Trigger angewendet werden, verhalten sich komplexer als EventTrigger-Animationen oder Animationen, die mit Storyboard-Methoden gestartet werden. Sie „übergeben“ mit Animationen, die von anderen Trigger-Objekten definiert werden, aber mit EventTrigger und von Methoden ausgelösten Animationen zusammengesetzt sind.

Weitere Informationen