Tipps und Tricks zu Animationen

Bei der Arbeit mit Animationen in WPF, gibt es eine Reihe von Tipps und Tricks, wie Sie die Leistung Ihrer Animation verbessern und Probleme vermeiden können.

Allgemeine Probleme

Durch Animieren der Position einer Bildlaufleiste oder eines Schiebereglers wird das Element gesperrt

Wenn Sie die Position einer Bildlaufleiste oder eines Schiebereglers mithilfe einer Animation animieren, die eine FillBehavior von HoldEnd hat (der Standardwert), kann der Benutzer die Bildlaufleiste oder den Schieberegler nicht mehr verschieben. Das liegt daran, dass, obwohl die Animation beendet wurde, nach wie vor der Basiswert der Zieleigenschaft überschrieben wird. Um zu verhindern, dass die Animation den aktuellen Wert der Eigenschaft überschreibt, entfernen Sie sie oder geben Sie ihr FillBehaviorvon Stop. Weitere Informationen und ein Beispiel finden Sie unter Festlegen einer Eigenschaft nach einer Storyboard-Animation.

Das Animieren der Ausgabe einer Animation hat keine Auswirkungen

Sie können kein Objekt animieren, das die Ausgabe einer anderen Animation ist. Wenn Sie z. B. eine ObjectAnimationUsingKeyFrames zum Animieren der Fill von einer Rectangle von einer RadialGradientBrush auf eine SolidColorBrush verwenden, können Sie keine Eigenschaften von RadialGradientBrush oder SolidColorBrush animieren.

Das Ändern des Werts einer Eigenschaft ist nach dem Animieren nicht möglich

Möglicherweise kommt es vor, dass Sie den Wert einer einmal animierten Eigenschaft, auch nachdem die Animation beendet wurde, nicht verändern können. Das liegt daran, dass, obwohl die Animation beendet wurde, immer noch der Basiswert der Eigenschaft überschrieben wird. Um zu verhindern, dass die Animation den aktuellen Wert der Eigenschaft überschreibt, entfernen Sie sie oder geben Sie ihr FillBehaviorvon Stop. Weitere Informationen und ein Beispiel finden Sie unter Festlegen einer Eigenschaft nach einer Storyboard-Animation.

Das Ändern einer Zeitachse hat keine Auswirkung

Obwohl die meisten Timeline-Eigenschaften animierbar sind und datengebunden werden können, scheint das Ändern der Eigenschaftswerte eines aktiven Timeline keine Auswirkungen zu haben. Das liegt daran, dass das Timing-System eine Kopie des Timelines anfertigt, wenn ein Timeline begonnen wird, und diese verwendet, um ein Clock-Objekt zu erstellen. Eine Änderung des Original hat keine Auswirkung auf die Kopie des Systems.

Damit eine Timeline Änderungen widerspiegelt, muss die Uhr neu generiert und dazu verwendet werden, die zuvor erstellte Uhr zu ersetzen. Uhren werden nicht automatisch neu generiert. Im Folgenden werden verschiedene Methoden beschrieben, wie Änderungen der Zeitachse übernommen werden können:

  • Wenn die Zeitleiste eineStoryboard ist oder zu einer gehört, können Sie Änderungen vornehmen, indem Sie das Storyboard mit einer BeginStoryboard- oder der Begin-Methode erneut anwenden. Dies hat den Nebeneffekt, dass auch die Animation neu gestartet wird. Im Code können Sie die Seek-Methode verwenden, um das Storyboard wieder an seine vorherige Position zu verschieben.

  • Wenn Sie mit der BeginAnimation-Methode eine Animation direkt auf eine Eigenschaft angewendet haben, rufen Sie die BeginAnimation-Methode erneut auf und übergeben ihr die geänderte Animation.

  • Wenn Sie direkt auf der Uhrebene arbeiten, erstellen Sie einen neuen Satz von Uhren, und verwenden sie diese, um den zuvor generierten Satz von Uhren zu ersetzen.

Weitere Informationen zu Zeitachsen und Uhren finden Sie unter Übersicht über das Animations- und Zeitsteuerungssystem.

FillBehavior.Stop funktioniert nicht wie erwartet

Es gibt Fälle, in denen die Einstellung der FillBehavior-Eigenschaft auf Stop scheinbar keine Wirkung hat, z. B. wenn eine Animation an eine andere "übergibt", weil sie die HandoffBehavior-Einstellung von SnapshotAndReplace hat.

Das folgende Beispiel erstellt eine Canvas, ein Rectangle und ein TranslateTransform. Dies TranslateTransform wird animiert, um Rectangle um Canvas zu verschieben.

<Canvas Width="600" Height="200">
  <Rectangle 
    Canvas.Top="50" Canvas.Left="0" 
    Width="50" Height="50" Fill="Red">
    <Rectangle.RenderTransform>
      <TranslateTransform 
        x:Name="MyTranslateTransform" 
        X="0" Y="0" />
    </Rectangle.RenderTransform>
  </Rectangle>
</Canvas>

In den Beispielen in diesem Abschnitt werden die vorhergehenden Objekte verwendet, um mehrere Fälle zu veranschaulichen, in denen sich die FillBehavior-Eigenschaft möglicherweise nicht Ihren Erwartungen entsprechend verhält.

FillBehavior="Stop" und HandoffBehavior mit mehreren Animationen

Manchmal ignoriert eine Animation scheinbar ihre FillBehavior-Eigenschaft, wenn sie durch eine zweite Animation ersetzt wird. Nehmen Sie das folgende Beispiel, in dem zwei Storyboard-Objekte erstellt und zur Animation des im vorangegangenen Beispiel gezeigten TranslateTransform verwendet werden.

Das erste Storyboard, B1 animiert die X-Eigenschaft des TranslateTransform von 0 bis 350, wodurch das Rechteck um 350 Pixel nach rechts verschoben wird. Wenn die Animation das Ende ihrer Dauer erreicht und die Wiedergabe beendet ist, wird die X-Eigenschaft auf ihren ursprünglichen Wert, 0, zurückgesetzt. Das Rechteck wird daraufhin 350 Pixel nach rechts bewegt, und springt dann zurück an seine ursprüngliche Position.

<Button Content="Start Storyboard B1">
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Click">
      <BeginStoryboard>
        <Storyboard x:Name="B1">
          <DoubleAnimation 
            Storyboard.TargetName="MyTranslateTransform"
            Storyboard.TargetProperty="X"
            From="0" To="350" Duration="0:0:5"
            FillBehavior="Stop"
            />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>

Die zweite Storyboard, B2, animiert auch die X-Eigenschaft desselben TranslateTransform. Da hier nur die To-Eigenschaft der Animation in Storyboard festgelegt wird, verwendet die Animation den aktuellen Wert der Eigenschaft, die sie animiert, als Startwert.


<!-- Animates the same object and property as the preceding
     Storyboard. -->
<Button Content="Start Storyboard B2">
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Click">
      <BeginStoryboard>
        <Storyboard x:Name="B2">
          <DoubleAnimation 
            Storyboard.TargetName="MyTranslateTransform"
            Storyboard.TargetProperty="X"
            To="500" Duration="0:0:5" 
            FillBehavior="Stop" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>

Klicken Sie auf die zweite Schaltfläche, während das erste Storyboard abgespielt wird, ist das folgende Verhalten zu erwarten:

  1. Das erste Storyboard endet und sendet das Rechteck zurück an seine ursprüngliche Position, da die Animation eine FillBehavior von Stop hat.

  2. Das zweite Storyboard wird wirksam und animiert von der aktuellen Position, 0, auf 500.

Dies geschieht jedoch nicht. Stattdessen springt das Rechteck nicht zurück; es verschiebt sich weiter nach rechts. Der Grund ist die zweite Animation, welche den aktuellen Wert der ersten Animation als ihren Startwert verwendet und von diesem Wert auf 500 animiert. Wenn die zweite Animation die erste ersetzt, weil die SnapshotAndReplaceHandoffBehavior verwendet wird, spielt die FillBehavior der ersten Animation keine Rolle.

FillBehavior und das abgeschlossene Ereignis

Die nächsten Beispiele zeigen ein weiteres Szenario, in dem StopFillBehavior scheinbar keine Wirkung hat. Auch in diesem Beispiel wird ein Storyboard verwendet, um die X-Eigenschaft des TranslateTransform von 0 bis 350 zu animieren. Diesmal registriert sich das Beispiel jedoch für das Completed-Ereignis.

<Button Content="Start Storyboard C">
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Click">
      <BeginStoryboard>
        <Storyboard Completed="StoryboardC_Completed">
          <DoubleAnimation 
            Storyboard.TargetName="MyTranslateTransform"
            Storyboard.TargetProperty="X"
            From="0" To="350" Duration="0:0:5"
            FillBehavior="Stop" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>

Der Completed-Ereignishandler startet einen weiteren Storyboard, der dieselbe Eigenschaft von ihrem aktuellen Wert auf 500 animiert.

private void StoryboardC_Completed(object sender, EventArgs e)
{

    Storyboard translationAnimationStoryboard =
        (Storyboard)this.Resources["TranslationAnimationStoryboardResource"];
    translationAnimationStoryboard.Begin(this);
}
Private Sub StoryboardC_Completed(ByVal sender As Object, ByVal e As EventArgs)

    Dim translationAnimationStoryboard As Storyboard = CType(Me.Resources("TranslationAnimationStoryboardResource"), Storyboard)
    translationAnimationStoryboard.Begin(Me)
End Sub

Im Folgenden finden Sie das Markup, das die zweite Storyboard als Ressource definiert.

<Page.Resources>
  <Storyboard x:Key="TranslationAnimationStoryboardResource">
    <DoubleAnimation 
      Storyboard.TargetName="MyTranslateTransform"
      Storyboard.TargetProperty="X"
      To="500" Duration="0:0:5" />
  </Storyboard>
</Page.Resources>

Wenn Sie das Storyboard ausführen, könnten Sie erwarten, dass die X-Eigenschaft des TranslateTransform von 0 bis 350 animiert wird, dann auf 0 zurückgeht (weil sie eine FillBehavior-Einstellung von Stop hat) und dann von 0 bis 500 animiert wird. Stattdessen animiert TranslateTransform von 0 auf 350 und dann auf 500.

Das liegt an der Reihenfolge, in der WPF Ereignisse auslöst, und daran, dass Eigenschaftswerte zwischengespeichert und nicht neu berechnet werden, wenn die Eigenschaft ungültig ist. Das Completed-Ereignis wird zuerst verarbeitet, da es von der Stammzeitleiste (der ersten Storyboard) ausgelöst wurde. Zu diesem Zeitpunkt gibt die X-Eigenschaft immer noch den animierten Wert zurück, weil er noch nicht ungültig gemacht wurde. Der zweite Storyboard verwendet den zwischengespeicherten Wert als Startwert und beginnt mit der Animation.

Leistung

Animationen werden, auch nach Verlassen einer Seite fortgesetzt

Wenn Sie eine Page, die ausgeführte Animationen enthält verlassen, werden diese Animationen bis zur Übergabe der Page an den Garbage Collector weiter ausgeführt. Abhängig vom Navigationssystem, das Sie verwenden, bleibt eine Seite, die Sie Verlassen möglicherweise für einen unbestimmten Zeitraum im Speicher , und es werden aufgrund von Animationen Ressourcen belegt. Dies ist am deutlichsten sichtbar, wenn eine Seite ständig ausgeführte(„Umgebungs“-) Animationen enthält.

Aus diesem Grund ist es ratsam, das Unloaded-Ereignis zu verwenden, wenn Sie eine Seite verlassen.

Es gibt verschiedene Möglichkeiten, eine Animation zu entfernen. Die folgenden Verfahren können verwendet werden, um Animationen zu entfernen, die zu einem Storyboard gehören.

Die nächste Technik kann verwendet werden, unabhängig davon, wie die Animation gestartet wurde.

  • Um Animationen von einer bestimmten Eigenschaft zu entfernen, verwenden Sie dieBeginAnimation(DependencyProperty, AnimationTimeline)- Methode. Geben Sie die zu animierende Eigenschaft als ersten Parameter und null als zweiten an. Dies entfernt alle Animationsuhren aus der Eigenschaft.

Weitere Informationen zu den verschiedenen Methoden zum Animieren von Eigenschaften finden Sie unter Übersicht über die Verfahren zur Animation von Eigenschaften.

Das Verwenden von Compose HandoffBehavior belegt Systemressourcen

Wenn Sie eine Storyboard, AnimationTimeline, oder AnimationClock auf eine Eigenschaft die ComposeHandoffBehavior anwenden, verbrauchen alle Clock-Objekte, die zuvor mit dieser Eigenschaft verknüpft waren, weiterhin Systemressourcen; das Timing-System wird diese Uhren nicht automatisch entfernen.

Um Leistungsprobleme zu vermeiden, wenn Sie eine große Anzahl von Uhren mithilfe von Compose anwenden, sollten Sie zusammengesetzte Uhren, nachdem sie ausgeführt wurden, aus der animierten Eigenschaft entfernen. Es gibt mehrere Möglichkeiten, eine Uhr zu entfernen.

Dies betrifft hauptsächlich Animationen für Objekte, die eine lange Lebensdauer haben. Wenn ein Objekt an den Garbage Collector übergeben wird, werden auch die Uhren getrennt und an den Garbage Collector übergeben.

Weitere Informationen zu Uhr-Objekten finden Sie unter Übersicht über das Animations- und Zeitsteuerungssystem.

Weitere Informationen