Tips och tricks för animering
När du arbetar med animeringar i WPF finns det ett antal tips och tricks som kan få dina animeringar att prestera bättre och spara frustration.
Allmänna problem
Om du animerar rullningslistens eller skjutreglagets position låses den
Om du animerar positionen för ett rullningslist eller skjutreglage med hjälp av en animering som har en FillBehavior av HoldEnd (standardvärdet) kommer användaren inte längre att kunna flytta rullningslisten eller skjutreglaget. Det beror på att även om animeringen har avslutats, fortsätter den att åsidosätta målegenskapens basvärde. Om du vill hindra animeringen från att åsidosätta egenskapens aktuella värde tar du bort den eller ger den en FillBehavior av Stop. Mer information och ett exempel finns i Ange en egenskap efter att ha animerat denna med en Storyboard.
Att animera utdata från en animering har ingen effekt
Du kan inte animera ett objekt som är utdata från en annan animering. Om du till exempel använder en ObjectAnimationUsingKeyFrames för att animera Fill för en Rectangle från en RadialGradientBrush till en SolidColorBrushkan du inte animera några egenskaper för RadialGradientBrush eller SolidColorBrush.
Det går inte att ändra värdet för en egenskap efter att den har animerats
I vissa fall kan det verka som om du inte kan ändra värdet för en egenskap när den har animerats, även efter att animeringen har avslutats. Det beror på att även om animeringen avslutades åsidosätter den fortfarande egenskapens basvärde. Om du vill hindra animeringen från att åsidosätta egenskapens aktuella värde tar du bort den eller ger den en FillBehavior av Stop. För mer information och ett exempel, se Ange en egenskap efter att ha animerat den med en Storyboard.
Att ändra en tidslinje har ingen effekt
Även om de flesta Timeline-egenskaper är animerbara och kan vara databundna, har det ingen effekt att ändra egenskapsvärdena för en aktiv Timeline. Det beror på att när en Timeline påbörjas gör tidssystemet en kopia av Timeline och använder den för att skapa ett Clock-objekt. Att ändra originalet har ingen effekt på systemets kopia.
För att en Timeline ska återspegla ändringar måste dess klocka återskapas och användas för att ersätta den tidigare skapade klockan. Klockor återskapas inte automatiskt. Följande är flera sätt att tillämpa tidslinjeändringar:
Om tidslinjen är eller tillhör en Storyboardkan du göra så att den återspeglar ändringar genom att tillämpa dess storyboard på nytt med hjälp av en BeginStoryboard- eller Begin-metod. Detta har sidoeffekten av att även starta om animeringen. I kod kan du använda metoden Seek för att flytta tillbaka storyboarden till dess tidigare position.
Om du har tillämpat en animering direkt på en egenskap med hjälp av metoden BeginAnimation anropar du metoden BeginAnimation igen och skickar den animeringen som har ändrats.
Om du arbetar direkt på klocknivå skapar och tillämpar du en ny uppsättning klockor och använder dem för att ersätta den tidigare uppsättningen genererade klockor.
Mer information om tidslinjer och klockor finns i Översikt över animerings- och tidsschemasystem.
FillBehavior.Stop fungerar inte som förväntat
Det finns tillfällen när du ställer in egenskapen FillBehavior till Stop, verkar det inte ha någon effekt, till exempel när en animering överförs till en annan eftersom den har en HandoffBehavior-inställning till SnapshotAndReplace.
I följande exempel skapas en Canvas, en Rectangle och en TranslateTransform. TranslateTransform kommer att animeras för att flytta Rectangle runt Canvas.
<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>
Exemplen i det här avsnittet använder föregående objekt för att visa flera fall där egenskapen FillBehavior inte fungerar som du kan förvänta dig.
FillBehavior="Stop" och HandoffBehavior med flera animeringar
Ibland verkar det som om en animering ignorerar dess FillBehavior egenskap när den ersätts av en andra animering. Ta följande exempel, som skapar två Storyboard objekt och använder dem för att animera samma TranslateTransform som visas i föregående exempel.
Den första Storyboard, B1
, animerar egenskapen X för TranslateTransform från 0 till 350, vilket flyttar rektangeln 350 bildpunkter åt höger. När animeringen når slutet av sin varaktighet och slutar spelas upp återgår egenskapen X till sitt ursprungliga värde, 0. Därför flyttas rektangeln till höger 350 bildpunkter och hoppar sedan tillbaka till sin ursprungliga 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>
Den andra Storyboard, B2
, animerar också egenskapen X för samma TranslateTransform. Eftersom endast den To egenskapen för animeringen i den här Storyboard anges använder animeringen det aktuella värdet för egenskapen som den animerar som startvärde.
<!-- 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>
Om du klickar på den andra knappen medan den första Storyboard spelas upp kan du förvänta dig följande beteende:
Den första storyboarden slutar och skickar tillbaka rektangeln till sin ursprungliga position, eftersom animeringen har ett FillBehavior på Stop.
Den andra storyboarden börjar gälla och animerar från den nuvarande positionen, som nu är 0, till 500.
Men det är inte vad som händer. I stället hoppar rektangeln inte tillbaka. den fortsätter att flyttas åt höger. Det beror på att den andra animeringen använder det aktuella värdet för den första animeringen som startvärde och animerar från det värdet till 500. När den andra animeringen ersätter den första eftersom SnapshotAndReplaceHandoffBehavior används spelar FillBehavior för den första animeringen ingen roll.
FillBehavior och slutförd händelse
Nästa exempel visar ett annat scenario där StopFillBehavior verkar ha ingen effekt. Återigen använder exemplet en Storyboard för att animera egenskapen X för TranslateTransform från 0 till 350. Men den här gången registreras exemplet för händelsen Completed.
<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>
Händelsehanteraren Completed startar en annan Storyboard som animerar samma egenskap från dess aktuella värde till 500.
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
Följande är den markeringen som definierar den andra Storyboard som en resurs.
<Page.Resources>
<Storyboard x:Key="TranslationAnimationStoryboardResource">
<DoubleAnimation
Storyboard.TargetName="MyTranslateTransform"
Storyboard.TargetProperty="X"
To="500" Duration="0:0:5" />
</Storyboard>
</Page.Resources>
När du kör Storyboardkan du förvänta dig att egenskapen X för TranslateTransform animeras från 0 till 350 och sedan återgår till 0 när den har slutförts (eftersom den har en FillBehavior inställning för Stop) och sedan animera från 0 till 500. I stället animerar TranslateTransform från 0 till 350 och sedan till 500.
Det beror på i vilken ordning WPF genererar händelser samt på att egenskapsvärden cachelagras och inte beräknas om förrän egenskapen blir ogiltig. Händelsen Completed bearbetas först eftersom den utlöstes av rottidslinjen (den första Storyboard). För närvarande returnerar egenskapen X fortfarande dess animerade värde eftersom den inte har ogiltigförklarats ännu. Den andra Storyboard använder det cachelagrade värdet som startvärde och börjar animera.
Prestanda
Animeringar fortsätter att köras när du har navigerat bort från en sida
När du navigerar bort från en Page som innehåller animeringar som körs fortsätter animeringarna att spelas upp tills Page är skräpinsamling. Beroende på vilket navigeringssystem du använder kan en sida som du navigerar bort från stanna kvar i minnet på obestämd tid, samtidigt som du förbrukar resurser med dess animeringar. Detta är mest märkbart när en sida innehåller animeringar som körs hela tiden ("ambient").
Därför är det bra att använda händelsen Unloaded för att ta bort animeringar när du navigerar bort från en sida.
Det finns olika sätt att ta bort en animering. Följande tekniker kan användas för att ta bort animeringar som tillhör en Storyboard.
För att ta bort en Storyboard som du började med en händelseutlösare, se Hur du tar bort en Storyboard.
Om du vill använda kod för att ta bort en Storyboardläser du metoden Remove.
Nästa teknik kan användas oavsett hur animeringen startades.
- Om du vill ta bort animeringar från en viss egenskap använder du metoden BeginAnimation(DependencyProperty, AnimationTimeline). Ange egenskapen som ska animeras som den första parametern och
null
som den andra. Då tas alla animeringsklockor bort från egenskapen.
Mer information om de olika sätten att animera egenskaper finns i Översikt över egenskapsanimeringstekniker.
Använda Compose HandoffBehavior förbrukar systemresurser
När du tillämpar en Storyboard, AnimationTimelineeller AnimationClock på en egenskap med hjälp av ComposeHandoffBehaviorfortsätter alla Clock objekt som tidigare var associerade med den egenskapen att förbruka systemresurser. tidsschemat tar inte bort dessa klockor automatiskt.
För att undvika prestandaproblem när du använder ett stort antal klockor med hjälp av Composebör du ta bort komponerande klockor från den animerade egenskapen när de har slutförts. Det finns flera sätt att ta bort en klocka.
Om du vill ta bort alla klockor från en egenskap använder du metoden ApplyAnimationClock(DependencyProperty, AnimationClock) eller BeginAnimation(DependencyProperty, AnimationTimeline) för det animerade objektet. Ange den egenskap som animeras som första parametern, och
null
som den andra. Då tas alla animeringsklockor bort från egenskapen.Om du vill ta bort en specifik AnimationClock från en lista med klockor använder du egenskapen Controller för AnimationClock för att hämta en ClockControlleroch anropar sedan metoden Remove för ClockController. Detta görs vanligtvis i Completed händelsehanterare för en klocka. Observera att endast rotklockor kan styras av en ClockController; egenskapen Controller för en sekundärklocka returnerar
null
. Observera också att händelse Completed inte anropas om den effektiva varaktigheten för klockan är oändlig. I så fall måste användaren bestämma när Removeska anropas.
Det här är främst ett problem för animeringar på objekt som har en lång livslängd. När ett objekt är skräpinsamling kopplas även klockorna från och skräp samlas in.
Mer information om klockobjekt finns i Översikt över animerings- och tidsschemasystem.
Se även
- Översikt över Animering
.NET Desktop feedback