using System; using System.ComponentModel; using System.Windows; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Animation; namespace Vec3.Wpf { public abstract class ImageSourceAnimationBase : AnimationTimeline { public sealed override Type TargetPropertyType { get { return typeof( ImageSource ); } } protected ImageSourceAnimationBase() { } public new ImageSourceAnimationBase Clone() { return (ImageSourceAnimationBase)base.Clone(); } public sealed override object GetCurrentValue( object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock ) { if( animationClock == null ) throw new ArgumentNullException( "animationClock" ); if( animationClock.CurrentState == ClockState.Stopped ) return defaultDestinationValue; return GetCurrentValueCore( (ImageSource)defaultOriginValue, (ImageSource)defaultDestinationValue, animationClock ); } public ImageSource GetCurrentValue( ImageSource defaultOriginValue, ImageSource defaultDestinationValue, AnimationClock animationClock ) { if( animationClock == null ) throw new ArgumentNullException( "animationClock" ); if( animationClock.CurrentState == ClockState.Stopped ) return defaultDestinationValue; return GetCurrentValueCore( (ImageSource)defaultOriginValue, (ImageSource)defaultDestinationValue, animationClock ); } protected abstract ImageSource GetCurrentValueCore( ImageSource defaultOriginValue, ImageSource defaultDestinationValue, AnimationClock animationClock ); } [ContentProperty( "Frames" )] public class FlipBookAnimation : ImageSourceAnimationBase, IAddChild { #region Frames private FreezableCollection frames; public FreezableCollection Frames { get { ReadPreamble(); if( frames == null ) { if( IsFrozen ) { frames = emptyFrames; } else { WritePreamble(); frames = new FreezableCollection(); OnFreezablePropertyChanged( null, frames ); WritePostscript(); } } return frames; } set { if( value == null ) throw new ArgumentNullException(); WritePreamble(); if( frames == value ) return; OnFreezablePropertyChanged( frames, value ); frames = value; WritePostscript(); } } private static readonly FreezableCollection emptyFrames; static FlipBookAnimation() { emptyFrames = new FreezableCollection( 0 ); emptyFrames.Freeze(); } [EditorBrowsable( EditorBrowsableState.Never )] public bool ShouldSerializeFrames() { ReadPreamble(); return frames != null && frames.Count > 0; } protected virtual void AddChild( ImageSource value ) { Frames.Add( value ); } void IAddChild.AddChild( object value ) { var val = value as ImageSource; if( val == null ) throw new ArgumentException(); AddChild( val ); } void IAddChild.AddText( string text ) { throw new InvalidOperationException(); } #endregion #region FrameTime public static readonly DependencyProperty FrameTimeProperty = DependencyProperty.Register( "FrameTime", typeof( TimeSpan ), typeof( FlipBookAnimation ), new UIPropertyMetadata( (TimeSpan)TimeSpan.FromSeconds( 1.0F / 30.0F ) ), ValidateFrameTime ); public TimeSpan FrameTime { get { return (TimeSpan)GetValue( FrameTimeProperty ); } set { SetValue( FrameTimeProperty, value ); } } private static bool ValidateFrameTime( object value ) { var t = (TimeSpan)value; return t > TimeSpan.Zero; } #endregion #region Freezable public new FlipBookAnimation Clone() { return (FlipBookAnimation)base.Clone(); } protected override Freezable CreateInstanceCore() { return new FlipBookAnimation(); } protected override bool FreezeCore( bool isChecking ) { if( !base.FreezeCore( isChecking ) ) return false; if( frames != null && !Freezable.Freeze( frames, isChecking ) ) return false; return true; } protected override void CloneCore( Freezable sourceFreezable ) { base.CloneCore( sourceFreezable ); var source = (FlipBookAnimation)sourceFreezable; if( source.frames != null ) { frames = (FreezableCollection)source.frames.Clone(); OnFreezablePropertyChanged( null, frames ); } } protected override void CloneCurrentValueCore( Freezable sourceFreezable ) { base.CloneCurrentValueCore( sourceFreezable ); var source = (FlipBookAnimation)sourceFreezable; if( source.frames != null ) { frames = (FreezableCollection)source.frames.CloneCurrentValue(); OnFreezablePropertyChanged( null, frames ); } } protected override void GetAsFrozenCore( Freezable sourceFreezable ) { base.GetAsFrozenCore( sourceFreezable ); var source = (FlipBookAnimation)sourceFreezable; if( source.frames != null ) { frames = (FreezableCollection)source.frames.GetAsFrozen(); OnFreezablePropertyChanged( null, frames ); } } protected override void GetCurrentValueAsFrozenCore( Freezable sourceFreezable ) { base.GetCurrentValueAsFrozenCore( sourceFreezable ); var source = (FlipBookAnimation)sourceFreezable; if( source.frames != null ) { frames = (FreezableCollection)source.frames.GetCurrentValueAsFrozen(); OnFreezablePropertyChanged( null, frames ); } } #endregion protected override ImageSource GetCurrentValueCore( ImageSource defaultOriginValue, ImageSource defaultDestinationValue, AnimationClock animationClock ) { if( frames == null || frames.Count == 0 ) return defaultDestinationValue; var now = animationClock.CurrentTime.Value; long frame = now.Ticks / FrameTime.Ticks; if( frame <= 0 ) return frames[0]; return frames[(int)(frame % frames.Count)]; } protected override Duration GetNaturalDurationCore( Clock clock ) { int numFrames = frames != null ? frames.Count : 0; return new Duration( new TimeSpan( FrameTime.Ticks * numFrames ) ); } } }