Storyboard
è l'oggetto che gestisce il ciclo di vita di una animazione. Come abbiamo già visto nelle precedenti lezioni, il suo utilizzo principale è quello di avviare l'animazione. Esiste però un altro modo, oltre all'invocazione del metodo Begin
, per eseguire uno Storyboard
ed è tramite l'uso di EventTrigger con l'elemento BeginStoryboard
.
La differenza sostanziale sta nel fatto che il primo metodo lo possiamo utilizzare via codice procedurale (per esempio C#), mentre il secondo possiamo sfruttarlo direttamente nel codice XAML. Prima di vedere un esempio di quest'ultimo spieghiamo cos'è un EventTrigger, è un elemento che incapsula una porzione di codice XAML che viene attivata solo al verificarsi di un determinato RoutedEvent.
La sintassi è molto semplice, basta definirlo come elemento figlio della proprietà Triggers
di un controllo e settare l'attributo RoutedEvent
.
<Rectangle ...> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.Loaded"> ... </EventTrigger> </Rectangle.Triggers> </Rectangle>
Gli EventTrigger
hanno una grossa limitazione, dato che al momento l'unico evento supportato è Loaded
. Sfruttando questo meccanismo possiamo eseguire un'animazione al verificarsi dell'evento Loaded
, basta inserire una Storyboard
racchiusa in un elemento BeginStoryboard
. Vediamo un esempio completo.
<Canvas x:Name="LayoutRoot" Background="Azure"> <Rectangle x:Name="MyRectangle" Canvas.Left="25" Canvas.Top="40" Width="100" Height="100" Fill="Orange"> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimation From="100" To="300" Duration="0:0:3" Storyboard.TargetName="MyRectangle" Storyboard.TargetProperty="Width" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> </Canvas>
Diversamente se controlliamo l'animazione da codice abbiamo a disposizione molte più possibilità, in pratica possiamo immaginare l'oggetto Storyboard
come un player video, quindi eseguire su di esso le tipiche operazione di avvio (Begin
), pausa (Pause
), ripresa (Resume
), interruzione (Stop
) e tante altre.
Per dimostrare alcune di queste funzionalità partiamo dall'animazione precedente, impostiamo, per le proprietà To
e Duration
, valori più alti ed aggiungiamo sotto al rettangolo una serie di Button
per eseguire, da codice C#, le attività descritte prima.
<Canvas x:Name="LayoutRoot" Background="Azure"> <Canvas.Resources> <Storyboard x:Name="myStoryboard"> <DoubleAnimation x:Name="myAnimation" From="100" To="1000" Duration="0:0:10" Storyboard.TargetName="myRectangle" Storyboard.TargetProperty="Width" /> </Storyboard> </Canvas.Resources> <StackPanel Canvas.Left="25" Canvas.Top="40"> <Rectangle x:Name="myRectangle" Width="100" Height="100" Fill="Orange" HorizontalAlignment="Left" /> <StackPanel Margin="10" Orientation="Horizontal"> <Button ... Click="myBeginButton_Click" /> <Button ... Click="myPauseButton_Click" /> <Button ... Click="myResumeButton_Click" /> <Button ... Click="myStopButton_Click" /> <Button ... Click="myMoveButton_Click" /> </StackPanel> </StackPanel> </Canvas>
Nel code-behind non dobbiamo far altro che invocare i rispettivi metodi dell'oggetto Storyboard
.
private void myBeginButton_Click(object sender, RoutedEventArgs e) { myStoryboard.Begin(); } private void myPauseButton_Click(object sender, RoutedEventArgs e) { myStoryboard.Pause(); } private void myResumeButton_Click(object sender, RoutedEventArgs e) { myStoryboard.Resume(); } private void myStopButton_Click(object sender, RoutedEventArgs e) { myStoryboard.Stop(); } private void myMoveButton_Click(object sender, RoutedEventArgs e) { myStoryboard.Begin(); var seconds = myAnimation.Duration.TimeSpan.TotalSeconds / 2; myStoryboard.Seek(TimeSpan.FromSeconds(seconds)); }
Eseguendo l'applicazione il risultato sarà simile a quello visualizzato nell'immagine seguente, catturata durante l'animazione.
I primi metodi sono auto esplicativi, ma l'ultimo forse richiede un minimo di spiegazione. Tramite il metodo Seek possiamo posizionare l'animazione ad un determinato intervallo di tempo passatogli come parametro, di tipo TimeSpan
. Nel codice, prima invochiamo il metodo Begin
per essere sicuri che l'animazione sia già in esecuzione, dopo calcoliamo l'intervallo di tempo desiderato ed infine passiamo al metodo Seek
un TimeSpan
generato dai secondi calcolati.
Esiste un altro aspetto importante da trattare per quanto riguarda il ciclo di vita di un'animazione, infatti anche quando è conclusa di fatto continua ad essere attiva, nell'esempio precedente anche dopo dieci secondi l'animazione continua ad applicare il valore To
alla proprietà Width
del rettangolo, invece di tornare al suo valore originale.
Questo comportamento è definito dalla proprietà FillBehavior
dell'oggetto Storyboard
, la quale per default è impostata a HoldEnd
. Se vogliamo che al termine dell'animazione venga ripristinato il valore originale, in questo caso della larghezza del rettangolo, dobbiamo fermare l'animazione, per farlo possiamo agire in due modi, il primo è impostando FillBehavior
a Stop
, l'altro invece prevede che siamo noi ad invocare esplicitamente il metodo Stop
dello Storyboard
quando l'animazione è conclusa, per farlo dobbiamo intercettare l'evento Completed
, come nel prossimo esempio.
<Canvas ...> <Canvas.Resources> <Storyboard x:Name="myStoryboard" Completed="myStoryboard_Completed"> <DoubleAnimation ... /> </Storyboard> </Canvas.Resources> ... </Canvas> private void myStoryboard_Completed(object sender, EventArgs e) { myStoryboard.Stop(); }