[WPF] PlayAnimations ou comment jouer plusieurs animations les unes après les autres
Lors du développement de SmartCooking (notre projet Imagine Cup) en Mai 2008, j’ ai été confronté à quelques détails techniques avec WPF. Parmi eux : les animations !
Notre application se découpe en plusieurs écrans (menu principal, visualisation du stock, ajout de produits, etc…) tous liés entre eux par de petites animations pour y jouer des transitions entre chaque écran.
Plus globalement, si j’ ai trois écrans A, B et C, je crée quelques animations (storyboards) que je nomme ‘StoryboardOpenScreenA’, ‘StoryboardCloseScreenA’, ‘StoryboardOpenScreenB’, etc… Avec donc, à chaque fois, une animation de fermeture et une autre pour l’ ouverture de mon écran !
Il me suffit donc, dans mon code ou par l’ intermédiaire de Triggers, qu’ a jouer ces animations pour réaliser les transitions d’ un écran à l’ autre. Oui mais voila, si je place un trigger jouant plusieurs animations, du type :
1 2 3 4 5 6 | <window.triggers> <eventtrigger routedevent="ButtonBase.Click" sourcename="bt1"> <beginstoryboard storyboard="{StaticResource animation1}" /> <beginstoryboard storyboard="{StaticResource animation2}" /> </eventtrigger> </window.triggers> |
… ou directement dans le code, par appel de la fonction BeginStoryboard, mes deux animations se joueront en même temps !! Et croyez-moi, cela n’ est pas très joli surtout quand il y a plus de deux animations en même temps !
Ce que je veux donc, c’ est jouer les animations les unes après les autres ! Pour cela deux façons à ma connaissance :
- Soit, prévoir dans les storyboards des « blancs » pour démarrer l’ animation après quelques secondes (calé sur le temps de la précédente animation !). Mais de suite je vous arrête car cette méthode n’ est pas du tout convenable !
- Soit s’ abonner à l’ événement Completed de l’ objet Storyboard pour être notifié lors de la fin de l’ animation et ainsi lancer les animations suivantes !
Je me suis donc mis à la tache pour simplifier l’ utilisation de cette méthode par la création d’ une méthode d’ extension sur l’ objet Window (System.Windows.Window) permettant de jouer X animation(s) dans l’ ordre en précisant simplement leurs noms dans l’ ordre voulu !
On pourra ensuite, par exemple, dans notre Window et en une ligne de code, jouer les animations AnimA, AnimB et AnimC les une après les autres :
1 | this.PlayAnimations("AnimA", "AnimB", "AnimC"); |
Le nom des animations étant un « params string[] » on pourra y jouer autant d’ animation que l’ on veut (une seule, deux, trois, … dix, … ou plus).
Voici le code de cette extension :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | public static class MyWpfExtensions { public static void PlayAnimations(this Window window, params string[] animationsName) { // If animationsName is empty: return if (animationsName.Length <= 0) return; // For each animation for (int i = 0; i < animationsName.Length; i++) { // Get Storyboard object var anim = window.GetStoryBoard(animationsName[i]); if (anim != null) { // If not the last animation, if (i < (animationsName.Length - 1)) { // Add handler on the Completed event of the current storyboard to play the te next animation anim.Completed += new EventHandler( new StoryboardPlayNextHandler(window, anim, animationsName[i + 1]).OnHandler); } } } // Finally, play the 1st animation window.BeginStoryboard(window.GetStoryBoard(animationsName[0])); } public static Storyboard GetStoryBoard(this Window window, string name) { if (window.Resources.Contains(name)) return (Storyboard)window.Resources[name]; else return null; } public class StoryboardPlayNextHandler { private string _nextAnimationName; private Storyboard _actualStoryBoard; private Window _window; public StoryboardPlayNextHandler(Window window, Storyboard actualStoryBoard, string nextAnimationName) { this._actualStoryBoard = actualStoryBoard; this._nextAnimationName = nextAnimationName; this._window = window; } public void OnHandler(object sender, EventArgs e) { if (_actualStoryBoard != null) { // Remove the current handler on the completed event ! _actualStoryBoard.Completed -= new EventHandler(this.OnHandler); // Launch next storyboard _window.BeginStoryboard(_window.GetStoryBoard(_nextAnimationName)); } } } } |
En espérant que cela pourra vous être utile 🙂
Code complet de ma classe en cliquant ici !