Timeline.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / Media / Animation / Timeline.cs / 1305600 / Timeline.cs

                            //------------------------------------------------------------------------------ 
//  Microsoft Windows Client Platform
//  Copyright (c) Microsoft Corporation, 2003
//
//  File:       Timeline.cs 
//-----------------------------------------------------------------------------
 
#if DEBUG 
#define TRACE
#endif // DEBUG 

using MS.Internal;
using MS.Utility;
using System; 
using System.Collections;
using System.ComponentModel; 
using System.Diagnostics; 
using System.Runtime.InteropServices;
using System.Windows.Threading; 
using System.Windows;
using System.Windows.Markup;

using SR=MS.Internal.PresentationCore.SR; 
using SRID=MS.Internal.PresentationCore.SRID;
 
namespace System.Windows.Media.Animation 
{
    ///  
    /// Describes run-time timing behavior for timed objects.
    /// 
    /// 
    /// A Timeline object defines the run-time behavior of a Clock 
    /// object. Clock objects are arranged in trees. Correspondingly,
    /// Timeline objects are also arranged in trees. When a tree of clocks is 
    /// created, its structure follows that of the tree of Timeline objects. 
    /// 
    [RuntimeNameProperty("Name")] 
    [Localizability(LocalizationCategory.None, Readability=Readability.Unreadable)] // cannnot be read & localized as string
    public abstract partial class Timeline : Animatable
    {
        #region External interface 

        #region Construction 
 
        /// 
        /// Creates a Timeline with default properties. 
        /// 
        protected Timeline()
        {
#if DEBUG 
            lock (_debugLockObject)
            { 
                _debugIdentity = ++_nextIdentity; 
                WeakReference weakRef = new WeakReference(this);
                _objectTable[_debugIdentity] = weakRef; 
            }
#endif // DEBUG

        } 

        ///  
        /// Creates a Timeline with the specified BeginTime. 
        /// 
        ///  
        /// The scheduled BeginTime for this timeline.
        /// 
        protected Timeline(Nullable beginTime)
            : this() 
        {
            BeginTime = beginTime; 
        } 

        ///  
        /// Creates a Timeline with the specified begin time and duration.
        /// 
        /// 
        /// The scheduled BeginTime for this timeline. 
        /// 
        ///  
        /// The simple Duration of this timeline. 
        /// 
        protected Timeline(Nullable beginTime, Duration duration) 
            : this()
        {
            BeginTime = beginTime;
            Duration = duration; 
        }
 
        ///  
        /// Creates a Timeline with the specified BeginTime, Duration and RepeatBehavior.
        ///  
        /// 
        /// The scheduled BeginTime for this Timeline.
        /// 
        ///  
        /// The simple Duration of this Timeline.
        ///  
        ///  
        /// The RepeatBehavior for this Timeline.
        ///  
        protected Timeline(Nullable beginTime, Duration duration, RepeatBehavior repeatBehavior)
            : this()
        {
            BeginTime = beginTime; 
            Duration = duration;
            RepeatBehavior = repeatBehavior; 
        } 

        #endregion // Construction 

        #region Freezable

        ///  
        /// Override of FreezeCore.  We need to validate the timeline
        /// before Freezing it. 
        ///  
        /// 
        protected override bool FreezeCore(bool isChecking) 
        {
            ValidateTimeline();
            return base.FreezeCore(isChecking);
        } 

        // 
        // Overrides for GetAsFrozenCore and GetCurrentValueAsFrozenCore 
        // Timeline does not need to overide CloneCore and CloneCurrentValueCore
        // See the comment in CopyCommon 
        //

        /// 
        /// Creates a frozen base value clone of another Timeline. 
        /// 
        ///  
        /// The timeline to copy properties from. If this parameter is null, 
        /// this timeline is constructed with default property values.
        ///  
        /// 
        /// This method should be used by implementations of Freezable.CloneCommonCore.
        /// 
        protected override void GetAsFrozenCore(Freezable sourceFreezable) 
        {
            Timeline sourceTimeline = (Timeline)sourceFreezable; 
            base.GetAsFrozenCore(sourceFreezable); 

            CopyCommon(sourceTimeline); 
        }


        ///  
        /// Creates a frozen current value clone of another Timeline.
        ///  
        ///  
        /// The timeline to copy properties from. If this parameter is null,
        /// this timeline is constructed with default property values. 
        /// 
        /// 
        /// This method should be used by implementations of CopyCommonCore.
        ///  
        protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable)
        { 
            Timeline sourceTimeline = (Timeline)sourceFreezable; 
            base.GetCurrentValueAsFrozenCore(sourceFreezable);
 
            CopyCommon(sourceTimeline);
        }

        #endregion 

        #region Properties 
 
        private static void Timeline_PropertyChangedFunction(DependencyObject d,
                                                              DependencyPropertyChangedEventArgs e) 
        {
            ((Timeline)d).PropertyChanged(e.Property);
        }
 
        #region AccelerationRatio Property
 
 
        /// 
        /// AccelerationRatio Property 
        /// 
        public static readonly DependencyProperty AccelerationRatioProperty =
            DependencyProperty.Register(
                "AccelerationRatio", 
                typeof(double),
                typeof(Timeline), 
                new PropertyMetadata( 
                    (double)0.0,
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)), 
                    new ValidateValueCallback(ValidateAccelerationDecelerationRatio));

        /// 
        /// Gets or sets a value indicating the percentage of the duration of 
        /// an active period spent accelerating the passage of time from zero
        /// to its maximum rate. 
        ///  
        /// 
        /// The percentage of the duration of an active period spent 
        /// accelerating the passage of time from zero to its maximum rate.
        /// 
        /// 
        /// This property must be set to a value between 0 and 1, inclusive, 
        /// or it raises an InvalidArgumentException exception. This property
        /// has a default value of zero. 
        ///  
        public double AccelerationRatio
        { 
            get
            {
                return (double)GetValue(AccelerationRatioProperty);
            } 
            set
            { 
                SetValue(AccelerationRatioProperty, value); 
            }
        } 

        private static bool ValidateAccelerationDecelerationRatio(object value)
        {
            double newValue = (double)value; 

            if (newValue < 0 || newValue > 1 || double.IsNaN(newValue)) 
            { 
                throw new ArgumentException(SR.Get(SRID.Timing_InvalidArgAccelAndDecel), "value");
            } 

            return true;
        }
 
        #endregion // AccelerationRatio Property
 
 
        #region AutoReverse Property
 
        /// 
        /// AutoReverseProperty
        /// 
        public static readonly DependencyProperty AutoReverseProperty = 
            DependencyProperty.Register(
                "AutoReverse", 
                typeof(bool), 
                typeof(Timeline),
                new PropertyMetadata( 
                    false,
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)));

        ///  
        /// Gets or sets a value indicating whether a normal
        /// forward-progressing activation period should be succeeded by a 
        /// backward-progressing activation period. 
        /// 
        ///  
        /// true if a normal forward-progressing activation period should be
        /// succeeded by a backward-progressing activation period; otherwise,
        /// false.
        ///  
        /// 
        /// This property has a default value of false. 
        ///  
        [DefaultValue(false)]
        public bool AutoReverse 
        {
            get
            {
                return (bool)GetValue(AutoReverseProperty); 
            }
            set 
            { 
                SetValue(AutoReverseProperty, value);
            } 
        }

        #endregion
 

        #region BeginTime Property 
 
        /// 
        /// BeginTimeProperty 
        /// 
        public static readonly DependencyProperty BeginTimeProperty =
            DependencyProperty.Register(
                "BeginTime", 
                typeof(TimeSpan?),
                typeof(Timeline), 
                new PropertyMetadata( 
                    (TimeSpan?)TimeSpan.Zero,
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction))); 

        /// 
        /// Gets or sets the scheduled time at which this Timeline should
        /// begin, relative to its parents begin time, in local coordinates. 
        /// 
        ///  
        /// The scheduled time at which this Timeline should begin, relative 
        /// to its parents begin time, in local coordinates.
        ///  
        /// 
        /// This property has a default value of zero.
        /// 
        public TimeSpan? BeginTime 
        {
            get 
            { 
                return (TimeSpan?)GetValue(BeginTimeProperty);
            } 
            set
            {
                SetValue(BeginTimeProperty, value);
            } 
        }
 
        #endregion // BeginTime Property 

        #region DecelerationRatio Property 

        /// 
        /// DecelerationRatioProperty
        ///  
        public static readonly DependencyProperty DecelerationRatioProperty =
            DependencyProperty.Register( 
                "DecelerationRatio", 
                typeof(double),
                typeof(Timeline), 
                new PropertyMetadata(
                    (double)0.0,
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)),
                    new ValidateValueCallback(ValidateAccelerationDecelerationRatio)); 

        ///  
        /// Gets or sets a value indicating the percentage of the duration 
        /// of an active period spent decelerating the passage of time its
        /// maximum rate to from zero. 
        /// 
        /// 
        /// The percentage of the duration of an active period spent
        /// decelerating the passage of time its maximum rate to from zero. 
        /// 
        ///  
        /// This property must be set to a value between 0 and 1, inclusive, 
        /// or it raises an InvalidArgumentException exception. This
        /// property has a default value of zero. 
        /// 
        public double DecelerationRatio
        {
            get 
            {
                return (double)GetValue(DecelerationRatioProperty); 
            } 
            set
            { 
                SetValue(DecelerationRatioProperty, value);
            }
        }
 
        #endregion // DecelerationRatio Property
 
        #region DesiredFrameRate Property 

        ///  
        /// DesiredFrameRateProperty
        /// 
        public static readonly DependencyProperty DesiredFrameRateProperty =
            DependencyProperty.RegisterAttached( 
                "DesiredFrameRate",
                typeof(Int32?), 
                typeof(Timeline), 
                new PropertyMetadata(
                    (Int32?)null, 
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)),
                    new ValidateValueCallback(ValidateDesiredFrameRate));

        private static bool ValidateDesiredFrameRate(object value) 
        {
            Int32? desiredFrameRate = (Int32?)value; 
 
            return  (!desiredFrameRate.HasValue || desiredFrameRate.Value > 0);
        } 

        /// 
        /// Reads the attached property DesiredFrameRate from the given Timeline.
        ///  
        /// Timeline from which to read the attached property.
        /// The property's value. 
        ///  
        public static Int32? GetDesiredFrameRate(Timeline timeline)
        { 
            if (timeline == null) { throw new ArgumentNullException("timeline"); }

            return (Int32?)timeline.GetValue(DesiredFrameRateProperty);
        } 

        ///  
        /// Writes the attached property DesiredFrameRate to the given Timeline. 
        /// 
        /// Timeline to which to write the attached property. 
        /// The property value to set
        /// 
        public static void SetDesiredFrameRate(Timeline timeline, Int32? desiredFrameRate)
        { 
            if (timeline == null) { throw new ArgumentNullException("timeline"); }
 
            timeline.SetValue(DesiredFrameRateProperty, desiredFrameRate); 
        }
 
        #endregion // DesiredFrameRate Property

        #region Duration Property
        ///  
        /// DurationProperty
        ///  
        public static readonly DependencyProperty DurationProperty = 
            DependencyProperty.Register(
                "Duration", 
                typeof(Duration),
                typeof(Timeline),
                new PropertyMetadata(
                    Duration.Automatic, 
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)));
 
 
        /// 
        /// Gets or sets a value indicating the natural length of an 
        /// activation period, in local coordinates.
        /// 
        /// 
        /// The natural length of an activation period, in local coordinates. 
        /// 
        ///  
        /// This length represents a single forward or backward section of a 
        /// single repeat iteration. This property has a default value of
        /// Duration.Automatic. 
        /// 
        public Duration Duration
        {
            get 
            {
               return (Duration)GetValue(DurationProperty); 
            } 
            set
            { 
                SetValue(DurationProperty, value);
            }
        }
 
        #endregion // Duration Property
 
        #region FillBehavior Property 

        ///  
        /// FillBehavior Property
        /// 
        public static readonly DependencyProperty FillBehaviorProperty =
            DependencyProperty.Register( 
                "FillBehavior",
                typeof(FillBehavior), 
                typeof(Timeline), 
                new PropertyMetadata(
                    FillBehavior.HoldEnd, 
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)),
                new ValidateValueCallback(ValidateFillBehavior));

 
        private static bool ValidateFillBehavior(object value)
        { 
            return TimeEnumHelper.IsValidFillBehavior((FillBehavior)value); 
        }
 
        /// 
        /// This property indicates how a Timeline will behave when it is outside
        /// of its active period but its parent is in its active or hold period.
        ///  
        /// 
        public FillBehavior FillBehavior 
        { 
            get
            { 
                return (FillBehavior)GetValue(FillBehaviorProperty);
            }
            set
            { 
                SetValue(FillBehaviorProperty, value);
            } 
        } 

        #endregion // FillBehavior Property 

        #region Name Property

        ///  
        /// Name Property
        ///  
        public static readonly DependencyProperty NameProperty = 
            DependencyProperty.Register(
                "Name", 
                typeof(string),
                typeof(Timeline),
                new PropertyMetadata(
                    (string)null, 
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)),
                new ValidateValueCallback(System.Windows.Markup.NameValidationHelper.NameValidationCallback)); 
 
        /// 
        /// Gets or sets the Name of this Timeline. 
        /// 
        /// 
        /// The Name of this Timeline.
        ///  
        /// 
        /// This property can be used to set up [....]-arcs between sibling 
        /// Timeline objects. A [....]-arc is established when the 
        ///  property of one Timeline
        /// corresponds to the Name of another Timeline. 
        /// 
        [DefaultValue((string)null)]
        [MergableProperty(false)]
        public string Name 
        {
            get 
            { 
                return (string)GetValue(NameProperty);
            } 
            set
            {
                SetValue(NameProperty, value);
            } 
        }
 
        #endregion // Name Property 

        #region RepeatBehavior Property 

        /// 
        /// RepeatBehaviorProperty
        ///  
        public static readonly DependencyProperty RepeatBehaviorProperty =
            DependencyProperty.Register( 
                "RepeatBehavior", 
                typeof(RepeatBehavior),
                typeof(Timeline), 
                new PropertyMetadata(
                    new RepeatBehavior(1.0),
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)));
 

        ///  
        /// Gets or sets the a RepeatBehavior structure which specifies the way this Timeline will 
        /// repeat its simple duration.
        ///  
        /// A RepeatBehavior structure which specifies the way this Timeline will repeat its
        /// simple duration.
        public RepeatBehavior RepeatBehavior
        { 
            get
            { 
                return (RepeatBehavior)GetValue(RepeatBehaviorProperty); 
            }
            set 
            {
                SetValue(RepeatBehaviorProperty, value);
            }
        } 

        #endregion // RepeatBehavior Property 
 

        #region SpeedRatio Property 
        /// 
        /// SpeedRatioProperty
        /// 
        public static readonly DependencyProperty SpeedRatioProperty = 
            DependencyProperty.Register(
                "SpeedRatio", 
                typeof(double), 
                typeof(Timeline),
                new PropertyMetadata( 
                    (double)1.0,
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)),
                new ValidateValueCallback(ValidateSpeedRatio));
 
        /// 
        /// Gets or sets the ratio at which time progresses on this Timeline, 
        /// relative to its parent. 
        /// 
        ///  
        /// The ratio at which time progresses on this Timeline, relative to
        /// its parent.
        /// 
        ///  
        /// If Acceleration or Deceleration are specified, this ratio is the
        /// average ratio over the natural length of the Timeline. This 
        /// property has a default value of 1.0. 
        /// 
        [DefaultValue((double)1.0)] 
        public double SpeedRatio
        {
            get
            { 
                return (double)GetValue(SpeedRatioProperty);
            } 
            set 
            {
                SetValue(SpeedRatioProperty, value); 
            }
        }

        private static bool ValidateSpeedRatio(object value) 
        {
            double newValue = (double)value; 
 
            if (newValue <= 0 || newValue > double.MaxValue || double.IsNaN(newValue))
            { 
                throw new ArgumentException(SR.Get(SRID.Timing_InvalidArgFinitePositive), "value");
            }

            return true; 
        }
 
        #endregion // SpeedRatio Property 

        #endregion // Properties 

        #region Methods

        ///  
        /// Called by the  method to
        /// create a type-specific clock for this Timeline. 
        ///  
        /// 
        /// A clock for this Timeline. 
        /// 
        /// 
        /// If a derived class overrides this method, it should only create
        /// and return an object of a class inheriting from Clock. 
        /// 
        protected internal virtual Clock AllocateClock() 
        { 
            return new Clock(this);
        } 

        /// 
        /// Creates a new Clock using this Timeline as the root. If this
        /// Timeline has children, a tree of clocks will be created. 
        /// 
        ///  
        /// Although this Timeline may be included as a child of one or more 
        /// TimelineGroups, this information will be ignored. For the purposes
        /// of this method this Timeline will be treated as a root Timeline. 
        /// 
        /// 
        /// A new Clock or tree of Clocks depending on whether
        /// or not this Timeline is a TimelineGroup that contains children. 
        /// 
        public Clock CreateClock() 
        { 
            return CreateClock(true);
        } 

        /// 
        /// Creates a new Clock using this Timeline as the root. If this
        /// Timeline has children, a tree of clocks will be created. 
        /// 
        /// True if the root Clock returned should 
        /// return a ClockController from its Controller property so that 
        /// the Clock tree can be interactively controlled.
        ///  
        /// Although this Timeline may be included as a child of one or more
        /// TimelineGroups, this information will be ignored. For the purposes
        /// of this method this Timeline will be treated as a root Timeline.
        ///  
        /// 
        /// A new Clock or tree of Clocks depending on whether 
        /// or not this Timeline is a TimelineGroup that contains children. 
        /// 
        public Clock CreateClock(bool hasControllableRoot) 
        {
            // Create the tree of clocks from this timeline
            return Clock.BuildClockTreeFromTimeline(this, hasControllableRoot);
        } 

        ///  
        /// Returns the period of a single iteration.  This will only be called when 
        /// the Duration property is set to Automatic.  If Duration is Automatic,
        /// the natural duration is determined by the nature of the specific timeline class, 
        /// as determined by its author.  If GetNaturalDuration returns Automatic, it means
        /// that the natural duration is unknown, which temporarily implies Forever.
        /// Streaming media would fit this case.
        ///  
        /// 
        /// The Clock whose natural duration is desired. 
        ///  
        /// 
        /// A Duration quantity representing the natural duration. 
        /// 
        internal protected Duration GetNaturalDuration(Clock clock)
        {
            return GetNaturalDurationCore(clock); 
        }
 
        ///  
        /// Implemented by the class author to provide a custom natural Duration
        /// in the case that the Duration property is set to Automatic.  If the author 
        /// cannot determine the Duration, this method should return Automatic.
        /// 
        /// 
        /// The Clock whose natural duration is desired. 
        /// 
        ///  
        /// A Duration quantity representing the natural duration. 
        /// 
        protected virtual Duration GetNaturalDurationCore(Clock clock) 
        {
            return Duration.Automatic;
        }
 
        /// 
        /// This method will throw an exception if a timeline has been incorrectly 
        /// constructed.  Currently we validate all possible values when they are 
        /// set, but it is not possible to do this for Acceleration/DecelerationRatio.
        /// 
        /// The reason is when a timeline is instantiated in xaml the properties are
        /// set with direct calls to DependencyObject.SetValue.  This means our only
        /// chance to validate the value is in the ValidateValue callback, which does
        /// not allow querying of other DPs.  Acceleration/DecelerationRatio are invalid 
        /// if their sum is > 1.  This can't be verified in the ValidateValue callback,
        /// so is done here. 
        ///  
        private void ValidateTimeline()
        { 
            if (AccelerationRatio + DecelerationRatio > 1)
            {
                throw new InvalidOperationException(SR.Get(SRID.Timing_AccelAndDecelGreaterThanOne));
            } 
        }
 
        #endregion // Methods 

        #region Events 

        /// 
        /// Raised whenever the value of the CurrentStateInvalidated property changes.
        ///  
        public event EventHandler CurrentStateInvalidated
        { 
            add 
            {
                AddEventHandler(CurrentStateInvalidatedKey, value); 
            }
            remove
            {
                RemoveEventHandler(CurrentStateInvalidatedKey, value); 
            }
        } 
 
        /// 
        /// Raised whenever the value of the CurrentTimeInvalidated property changes. 
        /// 
        public event EventHandler CurrentTimeInvalidated
        {
            add 
            {
                AddEventHandler(CurrentTimeInvalidatedKey, value); 
            } 
            remove
            { 
                RemoveEventHandler(CurrentTimeInvalidatedKey, value);
            }
        }
 
        /// 
        /// Raised whenever the value of the CurrentGlobalSpeed property changes. 
        ///  
        public event EventHandler CurrentGlobalSpeedInvalidated
        { 
            add
            {
                AddEventHandler(CurrentGlobalSpeedInvalidatedKey, value);
            } 
            remove
            { 
                RemoveEventHandler(CurrentGlobalSpeedInvalidatedKey, value); 
            }
        } 

        /// 
        /// Raised whenever the value of the Completed property changes.
        ///  
        public event EventHandler Completed
        { 
            add 
            {
                AddEventHandler(CompletedKey, value); 
            }
            remove
            {
                RemoveEventHandler(CompletedKey, value); 
            }
        } 
 
        /// 
        /// Raised whenever the value of the RemoveRequested property changes. 
        /// 
        public event EventHandler RemoveRequested
        {
            add 
            {
                AddEventHandler(RemoveRequestedKey, value); 
            } 
            remove
            { 
                RemoveEventHandler(RemoveRequestedKey, value);
            }
        }
 
        #endregion // Events
 
        #endregion // External interface 

        #region Internal implementation 

        #region Properties

        ///  
        /// Read-only access to the EventHandlerStore.  Used by
        /// Clock to copy the Timeline's events 
        ///  
        internal EventHandlersStore InternalEventHandlersStore
        { 
            get
            {
                return EventHandlersStoreField.GetValue(this);
            } 
        }
 
        #endregion // Properties 

        #region Methods 

        /// 
        /// Exposes the OnFreezablePropertyChanged protected method for use
        /// by the TimelineCollection class. 
        /// 
        ///  
        /// The previous value of the timeline. 
        /// 
        ///  
        /// The new value of the timeline.
        /// 
        internal void InternalOnFreezablePropertyChanged(Timeline originalTimeline, Timeline newTimeline)
        { 
            OnFreezablePropertyChanged(originalTimeline, newTimeline);
        } 
 
        /// 
        /// Called by the TimelineCollection class to propagate the 
        /// Freezable.FreezeCore call to this object.
        /// 
        internal bool InternalFreeze(bool isChecking)
        { 
            return Freeze(this, isChecking);
        } 
 
        /// 
        /// Asks this timeline to perform read verification. 
        /// 
        internal void InternalReadPreamble()
        {
            ReadPreamble(); 
        }
 
        ///  
        /// Notifies this timeline that a change has been made.
        ///  
        internal void InternalWritePostscript()
        {
            WritePostscript();
        } 

 
        ///  
        /// Adds a delegate to the list of event handlers on this object.
        ///  
        /// 
        /// A unique identifier for the event handler.
        /// 
        /// The delegate to add. 
        private void AddEventHandler(EventPrivateKey key, Delegate handler)
        { 
            WritePreamble(); 

            EventHandlersStore store = EventHandlersStoreField.GetValue(this); 

            if (store == null)
            {
                store = new EventHandlersStore(); 
                EventHandlersStoreField.SetValue(this, store);
            } 
 
            store.Add(key, handler);
            WritePostscript(); 
        }

        /// 
        /// Implements copy functionalty for GetAsFrozenCore and GetCurrentValueAsFrozenCore 
        /// Timeline does not need to override CloneCore and CloneCurrentValueCore.
        ///  
        ///  
        private void CopyCommon(Timeline sourceTimeline)
        { 
            // When creating a frozen copy of a Timeline we want to copy the
            // event handlers. This is for two reasons
            //
            //   1.) Internally when creating a clock tree we use 
            //       a frozen copy of the timing tree.  If that frozen
            //       copy does not preserve the event handlers then 
            //       any callbacks registered on the Timelines will be lost. 
            //
            //   2.) GetAsFrozen and GetCurrentValueAsFrozen don't always clone. 
            //       If any object in the tree is frozen it'll simply return it.
            //       If we did not copy the event handlers GetAsFrozen
            //       would return different results depending on whether a
            //       Timeline was frozen before the call. 
            //
            // 
            // The other two clone methods make unfrozen clones, so it's consisent 
            // to not copy the event handlers for those methods.  Cloning an object
            // is basically the only way to get a 'fresh' copy without event handlers 
            // attached.  If someone wants a frozen clone they probably want an exact
            // copy of the original.

            EventHandlersStore sourceStore = EventHandlersStoreField.GetValue(sourceTimeline); 
            if (sourceStore != null)
            { 
                Debug.Assert(sourceStore.Count > 0); 
                EventHandlersStoreField.SetValue(this, new EventHandlersStore(sourceStore));
            } 
        }

        /// 
        /// Removes a delegate from the list of event handlers on this object. 
        /// 
        ///  
        /// A unique identifier for the event handler. 
        /// 
        /// The delegate to remove. 
        private void RemoveEventHandler(EventPrivateKey key, Delegate handler)
        {
            WritePreamble();
 
            EventHandlersStore store = EventHandlersStoreField.GetValue(this);
            if (store != null) 
            { 
                store.Remove(key, handler);
                if (store.Count == 0) 
                {
                    // last event handler was removed -- throw away underlying EventHandlersStore
                    EventHandlersStoreField.ClearValue(this);
                } 

                WritePostscript(); 
            } 
        }
 
        #endregion // Methods


        #region Debugging instrumentation 

#if DEBUG 
 
        /// 
        /// Dumps the description of the subtree rooted at this timeline. 
        /// 
        internal void Dump()
        {
            System.Text.StringBuilder builder = new System.Text.StringBuilder(); 
            builder.Capacity = 1024;
            builder.Append("========================================\n"); 
            builder.Append("Timelines rooted at Timeline "); 
            builder.Append(_debugIdentity);
            builder.Append('\n'); 
            builder.Append("----------------------------------------\n");
            BuildInfoRecursive(builder, 0);
            builder.Append("----------------------------------------\n");
            Trace.Write(builder.ToString()); 
        }
 
        ///  
        /// Dumps the description of all timelines in the known timeline table.
        ///  
        internal static void DumpAll()
        {
            System.Text.StringBuilder builder = new System.Text.StringBuilder();
            int timelineCount = 0; 
            builder.Capacity = 1024;
            builder.Append("========================================\n"); 
            builder.Append("Timelines in the GC heap\n"); 
            builder.Append("----------------------------------------\n");
 
            lock (_debugLockObject)
            {
                if (_objectTable.Count > 0)
                { 
                    // Output the timelines sorted by Name
                    int[] idTable = new int[_objectTable.Count]; 
                    _objectTable.Keys.CopyTo(idTable, 0); 
                    Array.Sort(idTable);
 
                    for (int index = 0; index < idTable.Length; index++)
                    {
                        WeakReference weakRef = (WeakReference)_objectTable[idTable[index]];
                        Timeline timeline = (Timeline)weakRef.Target; 
                        if (timeline != null)
                        { 
                            timeline.BuildInfo(builder, 0, true); 
                            timelineCount++;
                        } 
                    }
                }
            }
 
            if (timelineCount == 0)
            { 
                builder.Append("There are no Timelines in the GC heap.\n"); 
            }
 
            builder.Append("----------------------------------------\n");
            Trace.Write(builder.ToString());
        }
 
        /// 
        /// Dumps the description of the subtree rooted at this timeline. 
        ///  
        /// 
        /// A StringBuilder that accumulates the description text. 
        /// 
        /// 
        /// The depth of recursion for this timeline.
        ///  
        internal void BuildInfoRecursive(System.Text.StringBuilder builder, int depth)
        { 
            // Add the info for this timeline 
            BuildInfo(builder, depth, true);
 
            // Recurse into the children
            depth++;
            TimelineGroup timelineGroup = this as TimelineGroup;
 
            if (timelineGroup != null)
            { 
                TimelineCollection children = timelineGroup.Children; 
                if (children != null)
                { 
                    for (int childIndex = 0; childIndex < children.Count; childIndex++)
                    {
                        children.Internal_GetItem(childIndex).BuildInfoRecursive(builder, depth);
                    } 
                }
            } 
        } 

        ///  
        /// Dumps the description of this timeline.
        /// 
        /// 
        /// A StringBuilder that accumulates the description text. 
        /// 
        ///  
        /// The depth of recursion for this timeline. 
        /// 
        ///  
        /// Whether or not to include the debug ID in the description.
        /// 
        internal void BuildInfo(System.Text.StringBuilder builder, int depth, bool includeDebugID)
        { 
            // Start with the name of the timeline
            if (includeDebugID) 
            { 
                builder.Append(' ', depth);
                builder.Append("Timeline "); 
                builder.Append(_debugIdentity);
            }
            builder.Append(" (");
            builder.Append(GetType().Name); 

            // Build attributes 
            if (Name != null) 
            {
                builder.Append(", Name=\""); 
                builder.Append(Name);
                builder.Append("\"");
            }
            if (AccelerationRatio != 0.0f) 
            {
                builder.Append(", AccelerationRatio = "); 
                builder.Append(AccelerationRatio.ToString()); 
            }
            if (AutoReverse != false) 
            {
                builder.Append(", AutoReverse = ");
                builder.Append(AutoReverse.ToString());
            } 
            if (DecelerationRatio != 0.0f)
            { 
                builder.Append(", DecelerationRatio = "); 
                builder.Append(DecelerationRatio.ToString());
            } 
            if (Duration != Duration.Automatic)
            {
                builder.Append(", Duration = ");
                builder.Append(Duration.ToString()); 
            }
            if (FillBehavior != FillBehavior.HoldEnd) 
            { 
                builder.Append(", FillBehavior = ");
                builder.Append(FillBehavior.ToString()); 
            }
            if (SpeedRatio != 1.0f)
            {
                builder.Append(", Speed = "); 
                builder.Append(SpeedRatio);
            } 
            builder.Append(")\n"); 

        } 

        /// 
        /// Finds a previously registered object.
        ///  
        /// 
        /// The Name of the object to look for 
        ///  
        /// 
        /// The object if found, null otherwise. 
        /// 
        internal static Timeline Find(int id)
        {
            Timeline timeline = null; 

            lock (_debugLockObject) 
            { 
                object handleReference = _objectTable[id];
                if (handleReference != null) 
                {
                    WeakReference weakRef = (WeakReference)handleReference;
                    timeline = (Timeline)weakRef.Target;
                    if (timeline == null) 
                    {
                        // Object has been destroyed, so remove the weakRef. 
                        _objectTable.Remove(id); 
                    }
                } 
            }

            return timeline;
        } 

        ///  
        /// Cleans up the known timelines table by removing dead weak 
        /// references.
        ///  
        internal static void CleanKnownTimelinesTable()
        {
            lock (_debugLockObject)
            { 
                Hashtable removeTable = new Hashtable();
 
                // Identify dead references 
                foreach (DictionaryEntry e in _objectTable)
                { 
                    WeakReference weakRef = (WeakReference) e.Value;
                    if (weakRef.Target == null)
                    {
                        removeTable[e.Key] = weakRef; 
                    }
                } 
 
                // Remove dead references
                foreach (DictionaryEntry e in removeTable) 
                {
                    _objectTable.Remove(e.Key);
                }
            } 
        }
 
#endif // DEBUG 

        #endregion // Debugging instrumentation 

        #region Data

        #region Event Handler Storage 

        internal static readonly UncommonField EventHandlersStoreField = new UncommonField(); 
 
        // Unique identifiers for each of the events defined on Timeline.
        // This is used as a key in the EventHandlerStore 
        internal static readonly EventPrivateKey CurrentGlobalSpeedInvalidatedKey = new EventPrivateKey();
        internal static readonly EventPrivateKey CurrentStateInvalidatedKey = new EventPrivateKey();
        internal static readonly EventPrivateKey CurrentTimeInvalidatedKey = new EventPrivateKey();
        internal static readonly EventPrivateKey CompletedKey = new EventPrivateKey(); 
        internal static readonly EventPrivateKey RemoveRequestedKey = new EventPrivateKey();
 
        #endregion // Event Handler Storage 

        #region Debug data 

#if DEBUG

        internal int                _debugIdentity; 

        internal static int         _nextIdentity; 
        internal static Hashtable   _objectTable = new Hashtable(); 
        internal static object      _debugLockObject = new object();
 
#endif // DEBUG

        #endregion // Debug data
 
        #endregion // Data
 
        #endregion // Internal implementation 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------ 
//  Microsoft Windows Client Platform
//  Copyright (c) Microsoft Corporation, 2003
//
//  File:       Timeline.cs 
//-----------------------------------------------------------------------------
 
#if DEBUG 
#define TRACE
#endif // DEBUG 

using MS.Internal;
using MS.Utility;
using System; 
using System.Collections;
using System.ComponentModel; 
using System.Diagnostics; 
using System.Runtime.InteropServices;
using System.Windows.Threading; 
using System.Windows;
using System.Windows.Markup;

using SR=MS.Internal.PresentationCore.SR; 
using SRID=MS.Internal.PresentationCore.SRID;
 
namespace System.Windows.Media.Animation 
{
    ///  
    /// Describes run-time timing behavior for timed objects.
    /// 
    /// 
    /// A Timeline object defines the run-time behavior of a Clock 
    /// object. Clock objects are arranged in trees. Correspondingly,
    /// Timeline objects are also arranged in trees. When a tree of clocks is 
    /// created, its structure follows that of the tree of Timeline objects. 
    /// 
    [RuntimeNameProperty("Name")] 
    [Localizability(LocalizationCategory.None, Readability=Readability.Unreadable)] // cannnot be read & localized as string
    public abstract partial class Timeline : Animatable
    {
        #region External interface 

        #region Construction 
 
        /// 
        /// Creates a Timeline with default properties. 
        /// 
        protected Timeline()
        {
#if DEBUG 
            lock (_debugLockObject)
            { 
                _debugIdentity = ++_nextIdentity; 
                WeakReference weakRef = new WeakReference(this);
                _objectTable[_debugIdentity] = weakRef; 
            }
#endif // DEBUG

        } 

        ///  
        /// Creates a Timeline with the specified BeginTime. 
        /// 
        ///  
        /// The scheduled BeginTime for this timeline.
        /// 
        protected Timeline(Nullable beginTime)
            : this() 
        {
            BeginTime = beginTime; 
        } 

        ///  
        /// Creates a Timeline with the specified begin time and duration.
        /// 
        /// 
        /// The scheduled BeginTime for this timeline. 
        /// 
        ///  
        /// The simple Duration of this timeline. 
        /// 
        protected Timeline(Nullable beginTime, Duration duration) 
            : this()
        {
            BeginTime = beginTime;
            Duration = duration; 
        }
 
        ///  
        /// Creates a Timeline with the specified BeginTime, Duration and RepeatBehavior.
        ///  
        /// 
        /// The scheduled BeginTime for this Timeline.
        /// 
        ///  
        /// The simple Duration of this Timeline.
        ///  
        ///  
        /// The RepeatBehavior for this Timeline.
        ///  
        protected Timeline(Nullable beginTime, Duration duration, RepeatBehavior repeatBehavior)
            : this()
        {
            BeginTime = beginTime; 
            Duration = duration;
            RepeatBehavior = repeatBehavior; 
        } 

        #endregion // Construction 

        #region Freezable

        ///  
        /// Override of FreezeCore.  We need to validate the timeline
        /// before Freezing it. 
        ///  
        /// 
        protected override bool FreezeCore(bool isChecking) 
        {
            ValidateTimeline();
            return base.FreezeCore(isChecking);
        } 

        // 
        // Overrides for GetAsFrozenCore and GetCurrentValueAsFrozenCore 
        // Timeline does not need to overide CloneCore and CloneCurrentValueCore
        // See the comment in CopyCommon 
        //

        /// 
        /// Creates a frozen base value clone of another Timeline. 
        /// 
        ///  
        /// The timeline to copy properties from. If this parameter is null, 
        /// this timeline is constructed with default property values.
        ///  
        /// 
        /// This method should be used by implementations of Freezable.CloneCommonCore.
        /// 
        protected override void GetAsFrozenCore(Freezable sourceFreezable) 
        {
            Timeline sourceTimeline = (Timeline)sourceFreezable; 
            base.GetAsFrozenCore(sourceFreezable); 

            CopyCommon(sourceTimeline); 
        }


        ///  
        /// Creates a frozen current value clone of another Timeline.
        ///  
        ///  
        /// The timeline to copy properties from. If this parameter is null,
        /// this timeline is constructed with default property values. 
        /// 
        /// 
        /// This method should be used by implementations of CopyCommonCore.
        ///  
        protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable)
        { 
            Timeline sourceTimeline = (Timeline)sourceFreezable; 
            base.GetCurrentValueAsFrozenCore(sourceFreezable);
 
            CopyCommon(sourceTimeline);
        }

        #endregion 

        #region Properties 
 
        private static void Timeline_PropertyChangedFunction(DependencyObject d,
                                                              DependencyPropertyChangedEventArgs e) 
        {
            ((Timeline)d).PropertyChanged(e.Property);
        }
 
        #region AccelerationRatio Property
 
 
        /// 
        /// AccelerationRatio Property 
        /// 
        public static readonly DependencyProperty AccelerationRatioProperty =
            DependencyProperty.Register(
                "AccelerationRatio", 
                typeof(double),
                typeof(Timeline), 
                new PropertyMetadata( 
                    (double)0.0,
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)), 
                    new ValidateValueCallback(ValidateAccelerationDecelerationRatio));

        /// 
        /// Gets or sets a value indicating the percentage of the duration of 
        /// an active period spent accelerating the passage of time from zero
        /// to its maximum rate. 
        ///  
        /// 
        /// The percentage of the duration of an active period spent 
        /// accelerating the passage of time from zero to its maximum rate.
        /// 
        /// 
        /// This property must be set to a value between 0 and 1, inclusive, 
        /// or it raises an InvalidArgumentException exception. This property
        /// has a default value of zero. 
        ///  
        public double AccelerationRatio
        { 
            get
            {
                return (double)GetValue(AccelerationRatioProperty);
            } 
            set
            { 
                SetValue(AccelerationRatioProperty, value); 
            }
        } 

        private static bool ValidateAccelerationDecelerationRatio(object value)
        {
            double newValue = (double)value; 

            if (newValue < 0 || newValue > 1 || double.IsNaN(newValue)) 
            { 
                throw new ArgumentException(SR.Get(SRID.Timing_InvalidArgAccelAndDecel), "value");
            } 

            return true;
        }
 
        #endregion // AccelerationRatio Property
 
 
        #region AutoReverse Property
 
        /// 
        /// AutoReverseProperty
        /// 
        public static readonly DependencyProperty AutoReverseProperty = 
            DependencyProperty.Register(
                "AutoReverse", 
                typeof(bool), 
                typeof(Timeline),
                new PropertyMetadata( 
                    false,
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)));

        ///  
        /// Gets or sets a value indicating whether a normal
        /// forward-progressing activation period should be succeeded by a 
        /// backward-progressing activation period. 
        /// 
        ///  
        /// true if a normal forward-progressing activation period should be
        /// succeeded by a backward-progressing activation period; otherwise,
        /// false.
        ///  
        /// 
        /// This property has a default value of false. 
        ///  
        [DefaultValue(false)]
        public bool AutoReverse 
        {
            get
            {
                return (bool)GetValue(AutoReverseProperty); 
            }
            set 
            { 
                SetValue(AutoReverseProperty, value);
            } 
        }

        #endregion
 

        #region BeginTime Property 
 
        /// 
        /// BeginTimeProperty 
        /// 
        public static readonly DependencyProperty BeginTimeProperty =
            DependencyProperty.Register(
                "BeginTime", 
                typeof(TimeSpan?),
                typeof(Timeline), 
                new PropertyMetadata( 
                    (TimeSpan?)TimeSpan.Zero,
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction))); 

        /// 
        /// Gets or sets the scheduled time at which this Timeline should
        /// begin, relative to its parents begin time, in local coordinates. 
        /// 
        ///  
        /// The scheduled time at which this Timeline should begin, relative 
        /// to its parents begin time, in local coordinates.
        ///  
        /// 
        /// This property has a default value of zero.
        /// 
        public TimeSpan? BeginTime 
        {
            get 
            { 
                return (TimeSpan?)GetValue(BeginTimeProperty);
            } 
            set
            {
                SetValue(BeginTimeProperty, value);
            } 
        }
 
        #endregion // BeginTime Property 

        #region DecelerationRatio Property 

        /// 
        /// DecelerationRatioProperty
        ///  
        public static readonly DependencyProperty DecelerationRatioProperty =
            DependencyProperty.Register( 
                "DecelerationRatio", 
                typeof(double),
                typeof(Timeline), 
                new PropertyMetadata(
                    (double)0.0,
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)),
                    new ValidateValueCallback(ValidateAccelerationDecelerationRatio)); 

        ///  
        /// Gets or sets a value indicating the percentage of the duration 
        /// of an active period spent decelerating the passage of time its
        /// maximum rate to from zero. 
        /// 
        /// 
        /// The percentage of the duration of an active period spent
        /// decelerating the passage of time its maximum rate to from zero. 
        /// 
        ///  
        /// This property must be set to a value between 0 and 1, inclusive, 
        /// or it raises an InvalidArgumentException exception. This
        /// property has a default value of zero. 
        /// 
        public double DecelerationRatio
        {
            get 
            {
                return (double)GetValue(DecelerationRatioProperty); 
            } 
            set
            { 
                SetValue(DecelerationRatioProperty, value);
            }
        }
 
        #endregion // DecelerationRatio Property
 
        #region DesiredFrameRate Property 

        ///  
        /// DesiredFrameRateProperty
        /// 
        public static readonly DependencyProperty DesiredFrameRateProperty =
            DependencyProperty.RegisterAttached( 
                "DesiredFrameRate",
                typeof(Int32?), 
                typeof(Timeline), 
                new PropertyMetadata(
                    (Int32?)null, 
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)),
                    new ValidateValueCallback(ValidateDesiredFrameRate));

        private static bool ValidateDesiredFrameRate(object value) 
        {
            Int32? desiredFrameRate = (Int32?)value; 
 
            return  (!desiredFrameRate.HasValue || desiredFrameRate.Value > 0);
        } 

        /// 
        /// Reads the attached property DesiredFrameRate from the given Timeline.
        ///  
        /// Timeline from which to read the attached property.
        /// The property's value. 
        ///  
        public static Int32? GetDesiredFrameRate(Timeline timeline)
        { 
            if (timeline == null) { throw new ArgumentNullException("timeline"); }

            return (Int32?)timeline.GetValue(DesiredFrameRateProperty);
        } 

        ///  
        /// Writes the attached property DesiredFrameRate to the given Timeline. 
        /// 
        /// Timeline to which to write the attached property. 
        /// The property value to set
        /// 
        public static void SetDesiredFrameRate(Timeline timeline, Int32? desiredFrameRate)
        { 
            if (timeline == null) { throw new ArgumentNullException("timeline"); }
 
            timeline.SetValue(DesiredFrameRateProperty, desiredFrameRate); 
        }
 
        #endregion // DesiredFrameRate Property

        #region Duration Property
        ///  
        /// DurationProperty
        ///  
        public static readonly DependencyProperty DurationProperty = 
            DependencyProperty.Register(
                "Duration", 
                typeof(Duration),
                typeof(Timeline),
                new PropertyMetadata(
                    Duration.Automatic, 
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)));
 
 
        /// 
        /// Gets or sets a value indicating the natural length of an 
        /// activation period, in local coordinates.
        /// 
        /// 
        /// The natural length of an activation period, in local coordinates. 
        /// 
        ///  
        /// This length represents a single forward or backward section of a 
        /// single repeat iteration. This property has a default value of
        /// Duration.Automatic. 
        /// 
        public Duration Duration
        {
            get 
            {
               return (Duration)GetValue(DurationProperty); 
            } 
            set
            { 
                SetValue(DurationProperty, value);
            }
        }
 
        #endregion // Duration Property
 
        #region FillBehavior Property 

        ///  
        /// FillBehavior Property
        /// 
        public static readonly DependencyProperty FillBehaviorProperty =
            DependencyProperty.Register( 
                "FillBehavior",
                typeof(FillBehavior), 
                typeof(Timeline), 
                new PropertyMetadata(
                    FillBehavior.HoldEnd, 
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)),
                new ValidateValueCallback(ValidateFillBehavior));

 
        private static bool ValidateFillBehavior(object value)
        { 
            return TimeEnumHelper.IsValidFillBehavior((FillBehavior)value); 
        }
 
        /// 
        /// This property indicates how a Timeline will behave when it is outside
        /// of its active period but its parent is in its active or hold period.
        ///  
        /// 
        public FillBehavior FillBehavior 
        { 
            get
            { 
                return (FillBehavior)GetValue(FillBehaviorProperty);
            }
            set
            { 
                SetValue(FillBehaviorProperty, value);
            } 
        } 

        #endregion // FillBehavior Property 

        #region Name Property

        ///  
        /// Name Property
        ///  
        public static readonly DependencyProperty NameProperty = 
            DependencyProperty.Register(
                "Name", 
                typeof(string),
                typeof(Timeline),
                new PropertyMetadata(
                    (string)null, 
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)),
                new ValidateValueCallback(System.Windows.Markup.NameValidationHelper.NameValidationCallback)); 
 
        /// 
        /// Gets or sets the Name of this Timeline. 
        /// 
        /// 
        /// The Name of this Timeline.
        ///  
        /// 
        /// This property can be used to set up [....]-arcs between sibling 
        /// Timeline objects. A [....]-arc is established when the 
        ///  property of one Timeline
        /// corresponds to the Name of another Timeline. 
        /// 
        [DefaultValue((string)null)]
        [MergableProperty(false)]
        public string Name 
        {
            get 
            { 
                return (string)GetValue(NameProperty);
            } 
            set
            {
                SetValue(NameProperty, value);
            } 
        }
 
        #endregion // Name Property 

        #region RepeatBehavior Property 

        /// 
        /// RepeatBehaviorProperty
        ///  
        public static readonly DependencyProperty RepeatBehaviorProperty =
            DependencyProperty.Register( 
                "RepeatBehavior", 
                typeof(RepeatBehavior),
                typeof(Timeline), 
                new PropertyMetadata(
                    new RepeatBehavior(1.0),
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)));
 

        ///  
        /// Gets or sets the a RepeatBehavior structure which specifies the way this Timeline will 
        /// repeat its simple duration.
        ///  
        /// A RepeatBehavior structure which specifies the way this Timeline will repeat its
        /// simple duration.
        public RepeatBehavior RepeatBehavior
        { 
            get
            { 
                return (RepeatBehavior)GetValue(RepeatBehaviorProperty); 
            }
            set 
            {
                SetValue(RepeatBehaviorProperty, value);
            }
        } 

        #endregion // RepeatBehavior Property 
 

        #region SpeedRatio Property 
        /// 
        /// SpeedRatioProperty
        /// 
        public static readonly DependencyProperty SpeedRatioProperty = 
            DependencyProperty.Register(
                "SpeedRatio", 
                typeof(double), 
                typeof(Timeline),
                new PropertyMetadata( 
                    (double)1.0,
                    new PropertyChangedCallback(Timeline_PropertyChangedFunction)),
                new ValidateValueCallback(ValidateSpeedRatio));
 
        /// 
        /// Gets or sets the ratio at which time progresses on this Timeline, 
        /// relative to its parent. 
        /// 
        ///  
        /// The ratio at which time progresses on this Timeline, relative to
        /// its parent.
        /// 
        ///  
        /// If Acceleration or Deceleration are specified, this ratio is the
        /// average ratio over the natural length of the Timeline. This 
        /// property has a default value of 1.0. 
        /// 
        [DefaultValue((double)1.0)] 
        public double SpeedRatio
        {
            get
            { 
                return (double)GetValue(SpeedRatioProperty);
            } 
            set 
            {
                SetValue(SpeedRatioProperty, value); 
            }
        }

        private static bool ValidateSpeedRatio(object value) 
        {
            double newValue = (double)value; 
 
            if (newValue <= 0 || newValue > double.MaxValue || double.IsNaN(newValue))
            { 
                throw new ArgumentException(SR.Get(SRID.Timing_InvalidArgFinitePositive), "value");
            }

            return true; 
        }
 
        #endregion // SpeedRatio Property 

        #endregion // Properties 

        #region Methods

        ///  
        /// Called by the  method to
        /// create a type-specific clock for this Timeline. 
        ///  
        /// 
        /// A clock for this Timeline. 
        /// 
        /// 
        /// If a derived class overrides this method, it should only create
        /// and return an object of a class inheriting from Clock. 
        /// 
        protected internal virtual Clock AllocateClock() 
        { 
            return new Clock(this);
        } 

        /// 
        /// Creates a new Clock using this Timeline as the root. If this
        /// Timeline has children, a tree of clocks will be created. 
        /// 
        ///  
        /// Although this Timeline may be included as a child of one or more 
        /// TimelineGroups, this information will be ignored. For the purposes
        /// of this method this Timeline will be treated as a root Timeline. 
        /// 
        /// 
        /// A new Clock or tree of Clocks depending on whether
        /// or not this Timeline is a TimelineGroup that contains children. 
        /// 
        public Clock CreateClock() 
        { 
            return CreateClock(true);
        } 

        /// 
        /// Creates a new Clock using this Timeline as the root. If this
        /// Timeline has children, a tree of clocks will be created. 
        /// 
        /// True if the root Clock returned should 
        /// return a ClockController from its Controller property so that 
        /// the Clock tree can be interactively controlled.
        ///  
        /// Although this Timeline may be included as a child of one or more
        /// TimelineGroups, this information will be ignored. For the purposes
        /// of this method this Timeline will be treated as a root Timeline.
        ///  
        /// 
        /// A new Clock or tree of Clocks depending on whether 
        /// or not this Timeline is a TimelineGroup that contains children. 
        /// 
        public Clock CreateClock(bool hasControllableRoot) 
        {
            // Create the tree of clocks from this timeline
            return Clock.BuildClockTreeFromTimeline(this, hasControllableRoot);
        } 

        ///  
        /// Returns the period of a single iteration.  This will only be called when 
        /// the Duration property is set to Automatic.  If Duration is Automatic,
        /// the natural duration is determined by the nature of the specific timeline class, 
        /// as determined by its author.  If GetNaturalDuration returns Automatic, it means
        /// that the natural duration is unknown, which temporarily implies Forever.
        /// Streaming media would fit this case.
        ///  
        /// 
        /// The Clock whose natural duration is desired. 
        ///  
        /// 
        /// A Duration quantity representing the natural duration. 
        /// 
        internal protected Duration GetNaturalDuration(Clock clock)
        {
            return GetNaturalDurationCore(clock); 
        }
 
        ///  
        /// Implemented by the class author to provide a custom natural Duration
        /// in the case that the Duration property is set to Automatic.  If the author 
        /// cannot determine the Duration, this method should return Automatic.
        /// 
        /// 
        /// The Clock whose natural duration is desired. 
        /// 
        ///  
        /// A Duration quantity representing the natural duration. 
        /// 
        protected virtual Duration GetNaturalDurationCore(Clock clock) 
        {
            return Duration.Automatic;
        }
 
        /// 
        /// This method will throw an exception if a timeline has been incorrectly 
        /// constructed.  Currently we validate all possible values when they are 
        /// set, but it is not possible to do this for Acceleration/DecelerationRatio.
        /// 
        /// The reason is when a timeline is instantiated in xaml the properties are
        /// set with direct calls to DependencyObject.SetValue.  This means our only
        /// chance to validate the value is in the ValidateValue callback, which does
        /// not allow querying of other DPs.  Acceleration/DecelerationRatio are invalid 
        /// if their sum is > 1.  This can't be verified in the ValidateValue callback,
        /// so is done here. 
        ///  
        private void ValidateTimeline()
        { 
            if (AccelerationRatio + DecelerationRatio > 1)
            {
                throw new InvalidOperationException(SR.Get(SRID.Timing_AccelAndDecelGreaterThanOne));
            } 
        }
 
        #endregion // Methods 

        #region Events 

        /// 
        /// Raised whenever the value of the CurrentStateInvalidated property changes.
        ///  
        public event EventHandler CurrentStateInvalidated
        { 
            add 
            {
                AddEventHandler(CurrentStateInvalidatedKey, value); 
            }
            remove
            {
                RemoveEventHandler(CurrentStateInvalidatedKey, value); 
            }
        } 
 
        /// 
        /// Raised whenever the value of the CurrentTimeInvalidated property changes. 
        /// 
        public event EventHandler CurrentTimeInvalidated
        {
            add 
            {
                AddEventHandler(CurrentTimeInvalidatedKey, value); 
            } 
            remove
            { 
                RemoveEventHandler(CurrentTimeInvalidatedKey, value);
            }
        }
 
        /// 
        /// Raised whenever the value of the CurrentGlobalSpeed property changes. 
        ///  
        public event EventHandler CurrentGlobalSpeedInvalidated
        { 
            add
            {
                AddEventHandler(CurrentGlobalSpeedInvalidatedKey, value);
            } 
            remove
            { 
                RemoveEventHandler(CurrentGlobalSpeedInvalidatedKey, value); 
            }
        } 

        /// 
        /// Raised whenever the value of the Completed property changes.
        ///  
        public event EventHandler Completed
        { 
            add 
            {
                AddEventHandler(CompletedKey, value); 
            }
            remove
            {
                RemoveEventHandler(CompletedKey, value); 
            }
        } 
 
        /// 
        /// Raised whenever the value of the RemoveRequested property changes. 
        /// 
        public event EventHandler RemoveRequested
        {
            add 
            {
                AddEventHandler(RemoveRequestedKey, value); 
            } 
            remove
            { 
                RemoveEventHandler(RemoveRequestedKey, value);
            }
        }
 
        #endregion // Events
 
        #endregion // External interface 

        #region Internal implementation 

        #region Properties

        ///  
        /// Read-only access to the EventHandlerStore.  Used by
        /// Clock to copy the Timeline's events 
        ///  
        internal EventHandlersStore InternalEventHandlersStore
        { 
            get
            {
                return EventHandlersStoreField.GetValue(this);
            } 
        }
 
        #endregion // Properties 

        #region Methods 

        /// 
        /// Exposes the OnFreezablePropertyChanged protected method for use
        /// by the TimelineCollection class. 
        /// 
        ///  
        /// The previous value of the timeline. 
        /// 
        ///  
        /// The new value of the timeline.
        /// 
        internal void InternalOnFreezablePropertyChanged(Timeline originalTimeline, Timeline newTimeline)
        { 
            OnFreezablePropertyChanged(originalTimeline, newTimeline);
        } 
 
        /// 
        /// Called by the TimelineCollection class to propagate the 
        /// Freezable.FreezeCore call to this object.
        /// 
        internal bool InternalFreeze(bool isChecking)
        { 
            return Freeze(this, isChecking);
        } 
 
        /// 
        /// Asks this timeline to perform read verification. 
        /// 
        internal void InternalReadPreamble()
        {
            ReadPreamble(); 
        }
 
        ///  
        /// Notifies this timeline that a change has been made.
        ///  
        internal void InternalWritePostscript()
        {
            WritePostscript();
        } 

 
        ///  
        /// Adds a delegate to the list of event handlers on this object.
        ///  
        /// 
        /// A unique identifier for the event handler.
        /// 
        /// The delegate to add. 
        private void AddEventHandler(EventPrivateKey key, Delegate handler)
        { 
            WritePreamble(); 

            EventHandlersStore store = EventHandlersStoreField.GetValue(this); 

            if (store == null)
            {
                store = new EventHandlersStore(); 
                EventHandlersStoreField.SetValue(this, store);
            } 
 
            store.Add(key, handler);
            WritePostscript(); 
        }

        /// 
        /// Implements copy functionalty for GetAsFrozenCore and GetCurrentValueAsFrozenCore 
        /// Timeline does not need to override CloneCore and CloneCurrentValueCore.
        ///  
        ///  
        private void CopyCommon(Timeline sourceTimeline)
        { 
            // When creating a frozen copy of a Timeline we want to copy the
            // event handlers. This is for two reasons
            //
            //   1.) Internally when creating a clock tree we use 
            //       a frozen copy of the timing tree.  If that frozen
            //       copy does not preserve the event handlers then 
            //       any callbacks registered on the Timelines will be lost. 
            //
            //   2.) GetAsFrozen and GetCurrentValueAsFrozen don't always clone. 
            //       If any object in the tree is frozen it'll simply return it.
            //       If we did not copy the event handlers GetAsFrozen
            //       would return different results depending on whether a
            //       Timeline was frozen before the call. 
            //
            // 
            // The other two clone methods make unfrozen clones, so it's consisent 
            // to not copy the event handlers for those methods.  Cloning an object
            // is basically the only way to get a 'fresh' copy without event handlers 
            // attached.  If someone wants a frozen clone they probably want an exact
            // copy of the original.

            EventHandlersStore sourceStore = EventHandlersStoreField.GetValue(sourceTimeline); 
            if (sourceStore != null)
            { 
                Debug.Assert(sourceStore.Count > 0); 
                EventHandlersStoreField.SetValue(this, new EventHandlersStore(sourceStore));
            } 
        }

        /// 
        /// Removes a delegate from the list of event handlers on this object. 
        /// 
        ///  
        /// A unique identifier for the event handler. 
        /// 
        /// The delegate to remove. 
        private void RemoveEventHandler(EventPrivateKey key, Delegate handler)
        {
            WritePreamble();
 
            EventHandlersStore store = EventHandlersStoreField.GetValue(this);
            if (store != null) 
            { 
                store.Remove(key, handler);
                if (store.Count == 0) 
                {
                    // last event handler was removed -- throw away underlying EventHandlersStore
                    EventHandlersStoreField.ClearValue(this);
                } 

                WritePostscript(); 
            } 
        }
 
        #endregion // Methods


        #region Debugging instrumentation 

#if DEBUG 
 
        /// 
        /// Dumps the description of the subtree rooted at this timeline. 
        /// 
        internal void Dump()
        {
            System.Text.StringBuilder builder = new System.Text.StringBuilder(); 
            builder.Capacity = 1024;
            builder.Append("========================================\n"); 
            builder.Append("Timelines rooted at Timeline "); 
            builder.Append(_debugIdentity);
            builder.Append('\n'); 
            builder.Append("----------------------------------------\n");
            BuildInfoRecursive(builder, 0);
            builder.Append("----------------------------------------\n");
            Trace.Write(builder.ToString()); 
        }
 
        ///  
        /// Dumps the description of all timelines in the known timeline table.
        ///  
        internal static void DumpAll()
        {
            System.Text.StringBuilder builder = new System.Text.StringBuilder();
            int timelineCount = 0; 
            builder.Capacity = 1024;
            builder.Append("========================================\n"); 
            builder.Append("Timelines in the GC heap\n"); 
            builder.Append("----------------------------------------\n");
 
            lock (_debugLockObject)
            {
                if (_objectTable.Count > 0)
                { 
                    // Output the timelines sorted by Name
                    int[] idTable = new int[_objectTable.Count]; 
                    _objectTable.Keys.CopyTo(idTable, 0); 
                    Array.Sort(idTable);
 
                    for (int index = 0; index < idTable.Length; index++)
                    {
                        WeakReference weakRef = (WeakReference)_objectTable[idTable[index]];
                        Timeline timeline = (Timeline)weakRef.Target; 
                        if (timeline != null)
                        { 
                            timeline.BuildInfo(builder, 0, true); 
                            timelineCount++;
                        } 
                    }
                }
            }
 
            if (timelineCount == 0)
            { 
                builder.Append("There are no Timelines in the GC heap.\n"); 
            }
 
            builder.Append("----------------------------------------\n");
            Trace.Write(builder.ToString());
        }
 
        /// 
        /// Dumps the description of the subtree rooted at this timeline. 
        ///  
        /// 
        /// A StringBuilder that accumulates the description text. 
        /// 
        /// 
        /// The depth of recursion for this timeline.
        ///  
        internal void BuildInfoRecursive(System.Text.StringBuilder builder, int depth)
        { 
            // Add the info for this timeline 
            BuildInfo(builder, depth, true);
 
            // Recurse into the children
            depth++;
            TimelineGroup timelineGroup = this as TimelineGroup;
 
            if (timelineGroup != null)
            { 
                TimelineCollection children = timelineGroup.Children; 
                if (children != null)
                { 
                    for (int childIndex = 0; childIndex < children.Count; childIndex++)
                    {
                        children.Internal_GetItem(childIndex).BuildInfoRecursive(builder, depth);
                    } 
                }
            } 
        } 

        ///  
        /// Dumps the description of this timeline.
        /// 
        /// 
        /// A StringBuilder that accumulates the description text. 
        /// 
        ///  
        /// The depth of recursion for this timeline. 
        /// 
        ///  
        /// Whether or not to include the debug ID in the description.
        /// 
        internal void BuildInfo(System.Text.StringBuilder builder, int depth, bool includeDebugID)
        { 
            // Start with the name of the timeline
            if (includeDebugID) 
            { 
                builder.Append(' ', depth);
                builder.Append("Timeline "); 
                builder.Append(_debugIdentity);
            }
            builder.Append(" (");
            builder.Append(GetType().Name); 

            // Build attributes 
            if (Name != null) 
            {
                builder.Append(", Name=\""); 
                builder.Append(Name);
                builder.Append("\"");
            }
            if (AccelerationRatio != 0.0f) 
            {
                builder.Append(", AccelerationRatio = "); 
                builder.Append(AccelerationRatio.ToString()); 
            }
            if (AutoReverse != false) 
            {
                builder.Append(", AutoReverse = ");
                builder.Append(AutoReverse.ToString());
            } 
            if (DecelerationRatio != 0.0f)
            { 
                builder.Append(", DecelerationRatio = "); 
                builder.Append(DecelerationRatio.ToString());
            } 
            if (Duration != Duration.Automatic)
            {
                builder.Append(", Duration = ");
                builder.Append(Duration.ToString()); 
            }
            if (FillBehavior != FillBehavior.HoldEnd) 
            { 
                builder.Append(", FillBehavior = ");
                builder.Append(FillBehavior.ToString()); 
            }
            if (SpeedRatio != 1.0f)
            {
                builder.Append(", Speed = "); 
                builder.Append(SpeedRatio);
            } 
            builder.Append(")\n"); 

        } 

        /// 
        /// Finds a previously registered object.
        ///  
        /// 
        /// The Name of the object to look for 
        ///  
        /// 
        /// The object if found, null otherwise. 
        /// 
        internal static Timeline Find(int id)
        {
            Timeline timeline = null; 

            lock (_debugLockObject) 
            { 
                object handleReference = _objectTable[id];
                if (handleReference != null) 
                {
                    WeakReference weakRef = (WeakReference)handleReference;
                    timeline = (Timeline)weakRef.Target;
                    if (timeline == null) 
                    {
                        // Object has been destroyed, so remove the weakRef. 
                        _objectTable.Remove(id); 
                    }
                } 
            }

            return timeline;
        } 

        ///  
        /// Cleans up the known timelines table by removing dead weak 
        /// references.
        ///  
        internal static void CleanKnownTimelinesTable()
        {
            lock (_debugLockObject)
            { 
                Hashtable removeTable = new Hashtable();
 
                // Identify dead references 
                foreach (DictionaryEntry e in _objectTable)
                { 
                    WeakReference weakRef = (WeakReference) e.Value;
                    if (weakRef.Target == null)
                    {
                        removeTable[e.Key] = weakRef; 
                    }
                } 
 
                // Remove dead references
                foreach (DictionaryEntry e in removeTable) 
                {
                    _objectTable.Remove(e.Key);
                }
            } 
        }
 
#endif // DEBUG 

        #endregion // Debugging instrumentation 

        #region Data

        #region Event Handler Storage 

        internal static readonly UncommonField EventHandlersStoreField = new UncommonField(); 
 
        // Unique identifiers for each of the events defined on Timeline.
        // This is used as a key in the EventHandlerStore 
        internal static readonly EventPrivateKey CurrentGlobalSpeedInvalidatedKey = new EventPrivateKey();
        internal static readonly EventPrivateKey CurrentStateInvalidatedKey = new EventPrivateKey();
        internal static readonly EventPrivateKey CurrentTimeInvalidatedKey = new EventPrivateKey();
        internal static readonly EventPrivateKey CompletedKey = new EventPrivateKey(); 
        internal static readonly EventPrivateKey RemoveRequestedKey = new EventPrivateKey();
 
        #endregion // Event Handler Storage 

        #region Debug data 

#if DEBUG

        internal int                _debugIdentity; 

        internal static int         _nextIdentity; 
        internal static Hashtable   _objectTable = new Hashtable(); 
        internal static object      _debugLockObject = new object();
 
#endif // DEBUG

        #endregion // Debug data
 
        #endregion // Data
 
        #endregion // Internal implementation 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK