📜 ⬆️ ⬇️

Cervelaat, animation and good old code-behind

I decided to dig a little bit in Silverlight, and make something cool on it. This is cool, of course, must move, shimmer, and smoothly twitch, for we have Web-Dwan or where? :) And here I had to face a rather good, in fact, animation system in WPF / Silverlight. Having smoked MSDN, I cheerfully started writing animations in XAML. One wrote, the second, the third ... And then I wanted to make them go in a certain sequence. And it was here that I realized that XAML, the infection, is very redundant. For the description of interfaces, it fits perfectly: it is immediately obvious that what the need for a visual editor is and there is no more than completely eliminated. But when you try to write some logic in this XAMLe, all its absurdity begins to appear. Having smoked google, I was very surprised that most people are trying hard to cram everything into XAML. They swear, get confused in the code, cry, but continue to write. Just like those cactus mice, honestly. And then I got the idea to accurately describe the animations in ordinary C # code. We, so to speak, oldfags, drew the interface with direct calls to WinAPI, do any animations frighten us? :)


The result is such a portable class AnimationBag . It is portable because, without any changes, it can be used in both WPF and Silverlight.

public class AnimationItem { public event EventHandler Completed; private void OnStoryboardComplete(object sender, EventArgs e) { if (Completed != null) Completed(this, EventArgs.Empty); } private Storyboard storyboard; public Storyboard Storyboard { get { return storyboard; } set { if (storyboard != null) storyboard.Completed -= OnStoryboardComplete; storyboard = value; storyboard.Completed += OnStoryboardComplete; } } public Action BeginAction { get; set; } public Action EndAction { get; set; } } public class AnimationBag { private readonly Dictionary<string, AnimationItem> storyboards = new Dictionary<string, AnimationItem>(); public void AddAnimation(string name, DependencyObject control, string propertyName, Timeline animation, Action beginAction = null, Action endAction = null) { Storyboard board = new Storyboard(); AnimationItem item = new AnimationItem { BeginAction = beginAction, EndAction = endAction }; Storyboard.SetTarget(animation, control); Storyboard.SetTargetProperty(animation, new PropertyPath(propertyName)); board.Children.Add(animation); if (endAction != null) item.Completed += item_Completed; item.Storyboard = board; storyboards[name] = item; } private void item_Completed(object sender, EventArgs e) { KeyValuePair<string, AnimationItem> pair = storyboards.Where(x => x.Value.Equals(sender)).FirstOrDefault(); if (pair.Value != null && pair.Value.EndAction != null) pair.Value.EndAction.Invoke(); } public void StartAnimation(string name) { if (!storyboards.ContainsKey(name)) return; if (storyboards[name].BeginAction != null) storyboards[name].BeginAction.Invoke(); storyboards[name].Storyboard.Begin(); } public void StopAnimation(string name) { if (!storyboards.ContainsKey(name)) return; storyboards[name].Storyboard.Stop(); if (storyboards[name].EndAction != null) storyboards[name].EndAction.Invoke(); } public static DoubleAnimation CreateDoubleAnimation(double? to, long durationMs, double? from = null, bool repeat = false) { DoubleAnimation ret = new DoubleAnimation { To = to, From = from, Duration = new Duration(TimeSpan.FromMilliseconds(durationMs)) }; if (repeat) ret.RepeatBehavior = RepeatBehavior.Forever; return ret; } } 

')
As can be seen from the code, the class is extremely simple. Here is an example of use.

XAML:
 <Window x:Class="Animation.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Rectangle Height="110" HorizontalAlignment="Left" Margin="55,71,0,0" Name="rectangle1" Stroke="Black" VerticalAlignment="Top" Width="181" Fill="#FF9D3434" /> <Button Content="Button" Height="36" HorizontalAlignment="Left" Margin="286,93,0,0" Name="button1" VerticalAlignment="Top" Width="149" Click="button1_Click" /> </Grid> </Window> 


Code-behind:
 public partial class MainWindow : Window { private readonly AnimationBag animations = new AnimationBag(); public MainWindow() { InitializeComponent(); InitAnimations(); } private void InitAnimations() { animations.AddAnimation( "fadeOut", rectangle1, "Opacity", AnimationBag.CreateDoubleAnimation(0, 500), null, () => animations.StartAnimation("fadeIn")); animations.AddAnimation( "fadeIn", rectangle1, "Opacity", AnimationBag.CreateDoubleAnimation(1, 500)); } private void button1_Click(object sender, RoutedEventArgs e) { animations.StartAnimation("fadeOut"); } } 


The class itself is something like a dictionary with keys — animation names and values ​​— animations. In the InitAnimations method, two animations are added to the collection, indicating the name, control over which the action will be performed, the property of this control, and the animation object itself. You can create it with pens, or you can add static helpers to an existing method for DoubleAnimation . Among other things, the AddAnimation method can accept two delegates that will be executed before and after the animation itself. For example, here after the completion of the animation “fadeOut”, “fadeIn” is immediately launched.

As a result, it turned out to be a rather convenient mechanism that allows you to describe animations in one line of code instead of kilobytes of overloaded XAML.

Download source code with test projects

Source: https://habr.com/ru/post/109261/


All Articles