// (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows; using System.Windows.Media.Animation; namespace System.Windows.Controls.DataVisualization { /// /// Represents a control that can animate the transitions between its specified /// dependency property. /// internal static class DependencyPropertyAnimationHelper { /// /// Number of key frames per second to generate the date time animations. /// public const int KeyFramesPerSecond = 20; /// /// The pattern used to ensure unique keys for the storyboards stored in /// a framework element's resource dictionary. /// private const string StoryboardKeyPattern = "__{0}__"; /// /// Returns a unique key for a storyboard. /// /// The property path of the property that /// the storyboard animates. /// A unique key for a storyboard. private static string GetStoryboardKey(string propertyPath) { return string.Format(CultureInfo.InvariantCulture, StoryboardKeyPattern, propertyPath); } /// /// Starts animating a dependency property of a framework element to a /// target value. /// /// The element to animate. /// The dependency property to /// animate. /// The path of the dependency property to /// animate. /// The value to animate the dependency /// property to. /// The duration of the animation. /// The easing function to uses to /// transition the data points. public static void BeginAnimation( this FrameworkElement target, DependencyProperty animatingDependencyProperty, string propertyPath, object targetValue, TimeSpan timeSpan, IEasingFunction easingFunction) { Storyboard storyBoard = target.Resources[GetStoryboardKey(propertyPath)] as Storyboard; if (storyBoard != null) { #if SILVERLIGHT // Save current value object currentValue = target.GetValue(animatingDependencyProperty); #endif storyBoard.Stop(); #if SILVERLIGHT // Restore that value so it doesn't snap back to its starting value target.SetValue(animatingDependencyProperty, currentValue); #endif target.Resources.Remove(GetStoryboardKey(propertyPath)); } storyBoard = CreateStoryboard(target, animatingDependencyProperty, propertyPath, ref targetValue, timeSpan, easingFunction); storyBoard.Completed += (source, args) => { storyBoard.Stop(); target.SetValue(animatingDependencyProperty, targetValue); target.Resources.Remove(GetStoryboardKey(propertyPath)); }; target.Resources.Add(GetStoryboardKey(propertyPath), storyBoard); storyBoard.Begin(); } /// /// Creates a story board that animates a dependency property to a /// value. /// /// The element that is the target of the /// storyboard. /// The dependency property /// to animate. /// The property path of the dependency /// property to animate. /// The value to animate the dependency property /// to. /// The duration of the animation. /// /// The easing function to use to /// transition the data points. /// The story board that animates the property. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "easingFunction", Justification = "This parameter is used in Silverlight.")] private static Storyboard CreateStoryboard( FrameworkElement target, DependencyProperty animatingDependencyProperty, string propertyPath, ref object toValue, TimeSpan durationTimeSpan, IEasingFunction easingFunction) { object fromValue = target.GetValue(animatingDependencyProperty); double fromDoubleValue; double toDoubleValue; DateTime fromDateTime; DateTime toDateTime; Storyboard storyBoard = new Storyboard(); Storyboard.SetTarget(storyBoard, target); Storyboard.SetTargetProperty(storyBoard, new PropertyPath(propertyPath)); if ((fromValue != null && toValue != null)) { if (ValueHelper.TryConvert(fromValue, out fromDoubleValue) && ValueHelper.TryConvert(toValue, out toDoubleValue)) { DoubleAnimation doubleAnimation = new DoubleAnimation(); #if !NO_EASING_FUNCTIONS doubleAnimation.EasingFunction = easingFunction; #endif doubleAnimation.Duration = durationTimeSpan; doubleAnimation.To = ValueHelper.ToDouble(toValue); toValue = doubleAnimation.To; storyBoard.Children.Add(doubleAnimation); } else if (ValueHelper.TryConvert(fromValue, out fromDateTime) && ValueHelper.TryConvert(toValue, out toDateTime)) { ObjectAnimationUsingKeyFrames keyFrameAnimation = new ObjectAnimationUsingKeyFrames(); keyFrameAnimation.Duration = durationTimeSpan; long intervals = (long)(durationTimeSpan.TotalSeconds * KeyFramesPerSecond); if (intervals < 2L) { intervals = 2L; } IEnumerable timeSpanIntervals = ValueHelper.GetTimeSpanIntervalsInclusive(durationTimeSpan, intervals); IEnumerable dateTimeIntervals = ValueHelper.GetDateTimesBetweenInclusive(fromDateTime, toDateTime, intervals); IEnumerable keyFrames = EnumerableFunctions.Zip( dateTimeIntervals, timeSpanIntervals, (dateTime, timeSpan) => new DiscreteObjectKeyFrame() { Value = dateTime, KeyTime = timeSpan }); foreach (DiscreteObjectKeyFrame keyFrame in keyFrames) { keyFrameAnimation.KeyFrames.Add(keyFrame); toValue = keyFrame.Value; } storyBoard.Children.Add(keyFrameAnimation); } } if (storyBoard.Children.Count == 0) { ObjectAnimationUsingKeyFrames keyFrameAnimation = new ObjectAnimationUsingKeyFrames(); DiscreteObjectKeyFrame endFrame = new DiscreteObjectKeyFrame() { Value = toValue, KeyTime = new TimeSpan(0, 0, 0) }; keyFrameAnimation.KeyFrames.Add(endFrame); storyBoard.Children.Add(keyFrameAnimation); } return storyBoard; } } }