Tipps und Tricks für Animationen
Beim Arbeiten mit Animationen in WPF gibt es eine Reihe von Tipps und Tricks, mit denen Ihre Animationen besser ausgeführt werden können und Sie Frust sparen können.
Allgemeine Probleme
Das Animieren der Position einer Bildlaufleiste oder eines Schiebereglers friert diese ein.
Wenn Sie die Position einer Bildlaufleiste oder eines Schiebereglers mithilfe einer Animation mit einer FillBehavior von HoldEnd (Standardwert) animieren, kann der Benutzer die Bildlaufleiste oder den Schieberegler nicht mehr verschieben. Das liegt daran, dass, obwohl die Animation zu Ende ist, sie weiterhin den Grundwert der Zieleigenschaft überschreibt. Um zu verhindern, dass die Animation den aktuellen Wert der Eigenschaft überschreibt, entfernen Sie sie oder setzen Sie den Wert auf FillBehavior bei Stop. Weitere Informationen und ein Beispiel finden Sie unter Festlegen einer Eigenschaft nach dem Animieren mit einem Storyboard.
Das Animieren des Ergebnisses einer Animation hat keinen Effekt.
Sie können ein Objekt nicht animieren, das die Ausgabe einer anderen Animation ist. Wenn Sie beispielsweise eine ObjectAnimationUsingKeyFrames verwenden, um die Fill eines Rectangle von einem RadialGradientBrush in eine SolidColorBrushzu animieren, können Sie keine Eigenschaften der RadialGradientBrush oder SolidColorBrushanimieren.
Der Wert einer Eigenschaft kann nach dem Animieren nicht geändert werden.
In einigen Fällen kann es vorkommen, dass Sie den Wert einer Eigenschaft nach dem Animieren nicht mehr ändern können, auch nachdem die Animation beendet wurde. Das liegt daran, dass die Animation zwar beendet wurde, dennoch aber den Basiswert der Eigenschaft außer Kraft setzt. Um zu verhindern, dass die Animation den aktuellen Wert der Eigenschaft überschreibt, entfernen Sie sie, oder geben Sie ihm eine FillBehavior von Stop. Weitere Informationen und ein Beispiel finden Sie unter Festlegen einer Eigenschaft, nachdem es mit einem Storyboard animiert wurde.
Das Ändern einer Zeitachse hat keine Auswirkung
Obwohl die meisten Timeline Eigenschaften animierbar sind und datengebunden sein können, scheint das Ändern der Eigenschaftswerte eines aktiven Timeline keine Auswirkung zu haben. Der Grund: Wenn ein Timeline begonnen wird, erstellt das Timing-System eine Kopie des Timeline und verwendet es, um ein Clock-Objekt zu erstellen. Das Ändern des Originals hat keine Auswirkungen auf die Kopie im System.
Damit ein Timeline Änderungen widerspiegelt, muss seine Uhr regeneriert und verwendet werden, um die zuvor erstellte Uhr zu ersetzen. Uhren werden nicht automatisch für Sie rekonstruiert. Im Folgenden finden Sie verschiedene Möglichkeiten zum Anwenden von Änderungen an der Zeitleiste:
Wenn die Zeitachse zu einem Storyboardgehört, können Sie diese ändern, indem Sie das Storyboard mithilfe einer BeginStoryboard- oder der Begin-Methode erneut anwenden, um die Änderungen widerzuspiegeln. Dies hat den Nebeneffekt, die Animation auch neu zu starten. Im Code können Sie die Seek-Methode verwenden, um das Storyboard zurück zur vorherigen Position zu wechseln.
Wenn Sie eine Animation mithilfe der BeginAnimation-Methode direkt auf eine Eigenschaft angewendet haben, rufen Sie die BeginAnimation-Methode erneut auf und übergeben Sie die geänderte Animation.
Wenn Sie direkt auf der Taktebene arbeiten, erstellen Sie einen neuen Satz von Takten und wenden Sie sie an, um den vorherigen Satz von generierten Takten zu ersetzen.
Weitere Informationen zu Zeitleisten und Uhren finden Sie unter Animation and Timing System Overview.
FillBehavior.Stop funktioniert nicht wie erwartet
Es gibt Zeiten, in denen das Festlegen der FillBehavior-Eigenschaft auf Stop keine Auswirkung hat, z. B. wenn eine Animation "aushändigt" an eine andere, weil sie eine HandoffBehavior Einstellung von SnapshotAndReplacehat.
Im folgenden Beispiel werden ein Canvas, ein Rectangle und ein TranslateTransformerstellt. Der TranslateTransform wird animiert, um den Rectangle um den Canvaszu bewegen.
<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 vorherigen Objekte verwendet, um mehrere Fälle zu veranschaulichen, in denen sich die FillBehavior-Eigenschaft nicht wie erwartet verhält.
FillBehavior="Stop" und HandoffBehavior mit mehreren Animationen
Manchmal scheint es so, als ob eine Animation ihre FillBehavior Eigenschaft ignoriert, wenn sie durch eine zweite Animation ersetzt wird. Im folgenden Beispiel werden zwei Storyboard Objekte erstellt, um denselben TranslateTransform zu animieren, der im vorhergehenden Beispiel gezeigt wurde.
Die ersten Storyboard, B1
animieren die X-Eigenschaft des TranslateTransform von 0 bis 350, wodurch das Rechteck um 350 Pixel nach rechts verschoben wird. Wenn die Animation das Ende der Dauer erreicht und die Wiedergabe beendet, wird die X-Eigenschaft auf den ursprünglichen Wert 0 zurückgesetzt. Daher bewegt sich das Rechteck 350 Pixel nach rechts und springt dann zurück zu seiner ursprünglichen 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 nur die Eigenschaft To der Animation in diesem Storyboard festgelegt ist, 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>
Wenn Sie auf die zweite Schaltfläche klicken, während die erste Storyboard abgespielt wird, könnten Sie folgendes Verhalten erwarten:
Das erste Storyboard endet und sendet das Rechteck an seine ursprüngliche Position zurück, weil die Animation einen FillBehavior-Wert von Stopbesitzt.
Das zweite Storyboard tritt in Kraft und animiert von der aktuellen Position, die jetzt 0 ist, bis 500.
Aber das passiert nicht. Stattdessen springt das Rechteck nicht zurück; es bewegt sich weiter nach rechts. Das liegt daran, dass die zweite Animation den aktuellen Wert der ersten Animation als Startwert verwendet und von dort bis 500 animiert wird. Wenn die zweite Animation die erste ersetzt, weil die SnapshotAndReplaceundHandoffBehavior verwendet werden, spielt die FillBehavior der ersten Animation keine Rolle.
FillBehavior und das Completed-Ereignis
Die nächsten Beispiele veranschaulichen ein weiteres Szenario, in dem die StopFillBehavior scheinbar keine Auswirkung hat. Wieder verwendet das Beispiel ein Storyboard, um die X-Eigenschaft des TranslateTransform von 0 nach 350 zu animieren. Dieses Mal 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 eine weitere Storyboard, die dieselbe Eigenschaft vom 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 sehen 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 die Storyboardausführen, erwarten Sie möglicherweise, dass die X-Eigenschaft des TranslateTransform von 0 bis 350 animiert wird, bevor sie dann nach Abschluss auf 0 zurückgesetzt wird (da sie eine FillBehavior-Einstellung von Stophat) und danach von 0 bis 500 animiert wird. Stattdessen animiert sich die TranslateTransform von 0 bis 350 und dann bis 500.
Das liegt daran, weil WPF die Ereignisse in einer bestimmten Reihenfolge auslöst und Eigenschaftswerte zwischengespeichert werden und nicht neu berechnet werden, außer wenn die Eigenschaft ungültig ist. Das Completed-Ereignis wird zuerst verarbeitet, da es von der Stammzeitachse (dem ersten Storyboard) ausgelöst wurde. Derzeit gibt die Eigenschaft X ihren animierten Wert weiterhin zurück, da sie noch nicht ungültig gemacht wurde. Die zweite Storyboard verwendet den zwischengespeicherten Wert als Startwert und startet die Animation.
Leistung
Animationen laufen weiter, nachdem man von einer Seite weg navigiert ist.
Wenn Sie von einem Page weg navigieren, das laufende Animationen enthält, werden diese Animationen fortgesetzt, bis der Page vom Garbage Collector aufgeräumt wird. Je nach verwendetem Navigationssystem bleibt eine Seite, von der Sie weg navigieren, möglicherweise für eine unbegrenzte Zeit im Arbeitsspeicher, während Ressourcen mit ihren Animationen verbraucht werden. Dies ist besonders auffällig, wenn eine Seite ständig laufende Ambient-Animationen enthält.
Aus diesem Grund empfiehlt es sich, mit dem Unloaded-Ereignis Animationen zu entfernen, wenn Sie von einer Seite weg navigieren.
Es gibt verschiedene Möglichkeiten zum Entfernen einer Animation. Die folgenden Techniken können verwendet werden, um Animationen zu entfernen, die zu einem Storyboardgehören.
Informationen zum Entfernen einer Storyboard, die Sie mit einem Ereignistrigger gestartet haben, finden Sie unter Anleitung: Entfernen eines Storyboards.
Um einen Storyboardmithilfe von Code zu entfernen, siehe die Remove-Methode.
Die nächste Technik kann unabhängig davon verwendet werden, wie die Animation gestartet wurde.
- Verwenden Sie zum Entfernen von Animationen aus einer bestimmten Eigenschaft die BeginAnimation(DependencyProperty, AnimationTimeline)-Methode. Geben Sie die Eigenschaft an, die als erster Parameter animiert wird, und
null
als zweite. Dadurch werden alle Animationsuhren aus der Eigenschaft entfernt.
Weitere Informationen zu den verschiedenen Methoden zum Animieren von Eigenschaften finden Sie unter Property Animation Techniques Overview.
Die Verwendung des Compose-Handoff-Verhaltens beansprucht Systemressourcen.
Wenn Sie Storyboard, AnimationTimelineoder AnimationClock mit dem ComposeHandoffBehaviorauf eine Eigenschaft anwenden, verbrauchen alle zuvor dieser Eigenschaft zugeordneten Clock Objekte weiterhin Systemressourcen; das Timing-System entfernt diese Uhren nicht automatisch.
Um Leistungsprobleme zu vermeiden, wenn Sie eine große Anzahl von Timern mit Composeanwenden, sollten Sie die zusammengesetzten Timer nach Abschluss der Animation aus der animierten Eigenschaft entfernen. Es gibt mehrere Möglichkeiten, eine Uhr zu entfernen.
Um alle Uhren aus einem Objekt zu entfernen, verwenden Sie die Methode ApplyAnimationClock(DependencyProperty, AnimationClock) oder BeginAnimation(DependencyProperty, AnimationTimeline) des animierten Objekts. Geben Sie die Eigenschaft an, die als erster Parameter animiert wird, und
null
als zweite. Dadurch werden alle Animationsuhren aus der Eigenschaft entfernt.Um eine bestimmte AnimationClock aus einer Liste von Uhren zu entfernen, verwenden Sie die Controller-Eigenschaft des AnimationClock, um eine ClockControllerabzurufen, und rufen Sie dann die Remove-Methode der ClockControllerauf. Dies erfolgt in der Regel im Completed Ereignishandler für eine Uhr. Beachten Sie, dass nur Stammuhren durch eine ClockControllergesteuert werden können; die Controller Eigenschaft einer untergeordneten Uhr gibt
null
zurück. Beachten Sie auch, dass das Completed-Ereignis nicht aufgerufen wird, wenn die effektive Dauer der Uhr unendlich ist. In diesem Fall muss der Benutzer bestimmen, wann Removeaufgerufen werden soll.
Dies ist in erster Linie ein Problem bei Animationen für Objekte, die eine lange Lebensdauer aufweisen. Wenn ein Objekt einer Müllsammlung unterzogen wird, werden seine Uhren ebenfalls getrennt und entsorgt.
Weitere Informationen zu Uhr-Objekten finden Sie unter Übersicht über das Animations- und Zeitsystem.
Siehe auch
.NET Desktop feedback