AnimationStorage.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / System / Windows / Media / Animation / AnimationStorage.cs / 1 / AnimationStorage.cs

                            // DependencyPropertyAnimationStorage.cs 

using MS.Internal;
using MS.Utility;
using System.Collections.Generic; 
using System.Diagnostics;
using System.Windows.Threading; 
using System.Windows.Media.Composition; 
using System.Windows.Media.Media3D;
 
using MS.Internal.PresentationCore;     // SR, SRID, FriendAccessAllowed

namespace System.Windows.Media.Animation
{ 
    internal class AnimationStorage
    { 
 
#if DEBUG
 
        private static int _nextID = 1;

        private readonly int DebugID = _nextID++;
 
#endif
 
        #region Constructor 

        protected AnimationStorage() 
        {
            _currentTimeInvalidatedHandler = new EventHandler(OnCurrentTimeInvalidated);
            _removeRequestedHandler = new EventHandler(OnRemoveRequested);
        } 

        #endregion 
 
        #region Uncommon Fields
 
        private static readonly UncommonField AnimatedPropertyMapField = new UncommonField();

        #endregion
 
        #region Properties
 
        internal bool IsEmpty 
        {
            get 
            {
                Debug.Assert(   _animationClocks == null
                             || _animationClocks.Count > 0);
 
                Debug.Assert(   _propertyTriggerLayers == null
                             || _propertyTriggerLayers.Count > 0); 
 
                return _animationClocks == null
                    && _propertyTriggerLayers == null 
                    && _snapshotValue == DependencyProperty.UnsetValue;
            }
        }
 
        #endregion
 
        #region Methods 

        ///  
        /// Attaches an AnimationClock but does not add it to the collection.
        /// 
        /// 
        /// It's expected that the AnimationClock will have been added to the 
        /// collection before this is called.
        ///  
        /// The AnimationClock. 
        /// 
        /// The event handler to be executed when the RemoveRequested event is 
        /// raised by the AnimationClock.
        /// 
        internal void AttachAnimationClock(
            AnimationClock animationClock, 
            EventHandler removeRequestedHandler)
        { 
            Debug.Assert(animationClock != null); 
            Debug.Assert(_dependencyObject.Target != null);
            Debug.Assert(_currentTimeInvalidatedHandler != null); 

            animationClock.CurrentTimeInvalidated += _currentTimeInvalidatedHandler;

            if (animationClock.HasControllableRoot) 
            {
                animationClock.RemoveRequested += removeRequestedHandler; 
            } 
        }
 
        /// 
        /// Detaches an AnimationClock but does not remove it from the collection.
        /// 
        ///  
        /// It's expected that the AnimationClock will be removed from the
        /// collection immediately after calling this method. 
        ///  
        /// The AnimationClock.
        ///  
        /// The event handler associated with the RemoveRequested event on the
        /// AnimationClock.
        /// 
        internal void DetachAnimationClock( 
            AnimationClock animationClock,
            EventHandler removeRequestedHandler) 
        { 
            Debug.Assert(animationClock != null);
            Debug.Assert(_currentTimeInvalidatedHandler != null); 

            animationClock.CurrentTimeInvalidated -= _currentTimeInvalidatedHandler;

            if (animationClock.HasControllableRoot) 
            {
                animationClock.RemoveRequested -= removeRequestedHandler; 
            } 
        }
 
        internal void Initialize(DependencyObject d, DependencyProperty dp)
        {
            Debug.Assert(_dependencyObject == null);
            Debug.Assert(_dependencyProperty == null); 

            Animatable a = d as Animatable; 
 
            if (a != null)
            { 
                _dependencyObject = a.GetWeakReference();
            }
            else
            { 
                //
 
                _dependencyObject = new WeakReference(d); 
            }
 
            _dependencyProperty = dp;
        }

        ///  
        ///
        ///  
        ///  
        internal void RemoveLayer(AnimationLayer layer)
        { 
            Debug.Assert(_propertyTriggerLayers != null);
            Debug.Assert(_propertyTriggerLayers.ContainsValue(layer));

            int index = _propertyTriggerLayers.IndexOfValue(layer); 

            Debug.Assert(index >= 0); 
 
            _propertyTriggerLayers.RemoveAt(index);
 
            if (_propertyTriggerLayers.Count == 0)
            {
                _propertyTriggerLayers = null;
            } 
        }
 
        ///  
        /// This should be called at the end of any method that alters the
        /// storage in any way.  This method will make sure the peer dp is 
        /// set correctly and notify Animatables if something changes.
        /// 
        internal void WritePostscript()
        { 
            DependencyObject d = (DependencyObject)_dependencyObject.Target;
 
            if (d == null) 
            {
                return; 
            }

            FrugalMap animatedPropertyMap = AnimatedPropertyMapField.GetValue(d);
 
            if (   animatedPropertyMap.Count == 0
                || animatedPropertyMap[_dependencyProperty.GlobalIndex] == DependencyProperty.UnsetValue) 
            { 
                if (!IsEmpty)
                { 
                    // This is kind of tricky:
                    //
                    // Because FrugalMap is a struct instead of a class, we must
                    // be sure to add this AnimationStorage to the map before 
                    // setting the FrugalMap into the UncommonField storage. If
                    // we don't and the FrugalMap is empty then an empty FrugalMap 
                    // will be set into the UncommonField storage. If we were to 
                    // then add our AnimationStorage to the local FrugalMap, it
                    // would only allocate its own internal storage at that point 
                    // which would not apply to the FrugalMap we set into the
                    // UncommonField storage. Once a FrugalMap has allocated its
                    // internal storage, though, that storage is copied with the
                    // FrugalMap. This is what will happen when we add our 
                    // AnimationStorage to the FrugalMap first as we do below:
 
                    animatedPropertyMap[_dependencyProperty.GlobalIndex] = this; 

                    // Since FrugalMap is a struct and adding a new value to it 
                    // may re-allocate the storage, we need to set this value
                    // each time we make a change to the map.

                    AnimatedPropertyMapField.SetValue(d, animatedPropertyMap); 

                    if (animatedPropertyMap.Count == 1) 
                    { 
                        d.IAnimatable_HasAnimatedProperties = true;
                    } 

                    // If this the target is an Animatable we'll need to
                    // invalidate it so that the animation resource for this
                    // newly animated property will be passed across to the UCE. 
                    Animatable a = d as Animatable;
 
                    if (a != null) 
                    {
                        a.RegisterForAsyncUpdateResource(); 
                    }

                    // If this AnimationStorage is a resource, add it to the
                    // channel now. 
                    DUCE.IResource animationResource = this as DUCE.IResource;
 
                    if (animationResource != null) 
                    {
                        DUCE.IResource targetResource = d as DUCE.IResource; 

                        if (targetResource != null)
                        {
                            using (CompositionEngineLock.Acquire()) 
                            {
                                int channelCount = targetResource.GetChannelCount(); 
 
                                for (int i = 0; i < channelCount; i++)
                                { 
                                    DUCE.Channel channel = targetResource.GetChannel(i);
                                    if (!targetResource.GetHandle(channel).IsNull)
                                    {
                                        animationResource.AddRefOnChannel(channel); 
                                    }
                                } 
                            } 
                        }
                    } 
                }
            }
            else
            { 
                Debug.Assert(animatedPropertyMap.Count > 0);
                Debug.Assert(animatedPropertyMap[_dependencyProperty.GlobalIndex] != DependencyProperty.UnsetValue); 
 
                if (IsEmpty)
                { 
                    // If this AnimationStorage is a resource, release it from
                    // the channel now.
                    DUCE.IResource animationResource = this as DUCE.IResource;
 
                    if (animationResource != null)
                    { 
                        DUCE.IResource targetResource = d as DUCE.IResource; 

                        if (targetResource != null) 
                        {
                            using (CompositionEngineLock.Acquire())
                            {
                                int channelCount = targetResource.GetChannelCount(); 

                                for (int i = 0; i < channelCount; i++) 
                                { 
                                    DUCE.Channel channel = targetResource.GetChannel(i);
                                    if (!targetResource.GetHandle(channel).IsNull) 
                                    {
                                        animationResource.ReleaseOnChannel(channel);
                                    }
                                } 
                            }
                        } 
                    } 

                    // If this the target is an Animatable we'll need to 
                    // invalidate it so that the animation resource for this
                    // no longer animated property will no longer be passed
                    // across to the UCE.
                    Animatable a = d as Animatable; 

                    if (a != null) 
                    { 
                        a.RegisterForAsyncUpdateResource();
                    } 

                    animatedPropertyMap[_dependencyProperty.GlobalIndex] = DependencyProperty.UnsetValue;

                    if (animatedPropertyMap.Count == 0) 
                    {
                        AnimatedPropertyMapField.ClearValue(d); 
 
                        d.IAnimatable_HasAnimatedProperties = false;
                    } 
                    else
                    {
                        AnimatedPropertyMapField.SetValue(d, animatedPropertyMap);
                    } 

                    // We've removed animation storage for this DP, so if we were storing the local 
                    // base value here then it has to go back to its non-animated storage spot. 
                    if (_baseValue != DependencyProperty.UnsetValue)
                    { 
                        d.SetValue(_dependencyProperty, _baseValue);
                    }
                }
            } 

            // recompute animated value 
            d.InvalidateProperty(_dependencyProperty); 
        }
 

        #endregion

        #region Private 

        private void OnCurrentTimeInvalidated(object sender, EventArgs args) 
        { 
            object target = _dependencyObject.Target;
 
            if (target == null)
            {
                // If the target has been garbage collected, remove this handler
                // from the AnimationClock so that this collection can be 
                // released also.
                DetachAnimationClock((AnimationClock)sender, _removeRequestedHandler); 
            } 
            else
            { 
                // recompute animated value
                try
                {
                    DependencyObject targetDO = ((DependencyObject)target); 

                    // fetch the existing entry 
                    EffectiveValueEntry oldEntry = targetDO.GetValueEntry( 
                            targetDO.LookupEntry(_dependencyProperty.GlobalIndex),
                            _dependencyProperty, 
                            null,
                            RequestFlags.RawEntry);

                    EffectiveValueEntry newEntry; 
                    object value;
 
                    // create a copy of that entry, removing animated & coerced values 

                    if (!oldEntry.HasModifiers) 
                    {
                        // no modifiers; just use it, removing deferred references
                        newEntry = oldEntry;
                        value = newEntry.Value; 
                        if (newEntry.IsDeferredReference)
                        { 
                            value = ((DeferredReference) value).GetValue(newEntry.BaseValueSourceInternal); 
                            newEntry.Value = value;
                            newEntry.IsDeferredReference = false; 
                        }
                    }
                    else
                    { 
                        // else entry has modifiers; preserve expression but throw away
                        // coerced & animated values, since we'll be recomputing an animated value 
                        newEntry = new EffectiveValueEntry(); 
                        newEntry.BaseValueSourceInternal = oldEntry.BaseValueSourceInternal;
                        newEntry.PropertyIndex = oldEntry.PropertyIndex; 
                        newEntry.HasExpressionMarker = oldEntry.HasExpressionMarker;

                        value = oldEntry.ModifiedValue.BaseValue;
                        if (oldEntry.IsDeferredReference) 
                        {
                            DeferredReference dr = value as DeferredReference; 
                            if (dr != null) 
                            {
                                value = dr.GetValue(newEntry.BaseValueSourceInternal); 
                            }
                        }

                        newEntry.Value = value; 

                        if (oldEntry.IsExpression) 
                        { 
                            value = oldEntry.ModifiedValue.ExpressionValue;
                            if (oldEntry.IsDeferredReference) 
                            {
                                DeferredReference dr = value as DeferredReference;
                                if (dr != null)
                                { 
                                    value = dr.GetValue(newEntry.BaseValueSourceInternal);
                                } 
                            } 
                            newEntry.SetExpressionValue(value, newEntry.Value);
                        } 
                    }

                    // compute the new value for the property
 
                    PropertyMetadata metadata = _dependencyProperty.GetMetadata(targetDO.DependencyObjectType);
                    object animatedValue = AnimationStorage.GetCurrentPropertyValue(this, targetDO, _dependencyProperty, metadata, value); 
 
                    // update the new entry to contain the new animated value
                    newEntry.SetAnimatedValue(animatedValue, value); 

                    // call UpdateEffectiveValue to put the new entry in targetDO's effective values table
                    targetDO.UpdateEffectiveValue(
                            targetDO.LookupEntry(_dependencyProperty.GlobalIndex), 
                            _dependencyProperty,
                            metadata, 
                            oldEntry, 
                            ref newEntry,
                            false /* coerceWithDeferredReference */, 
                            OperationType.Unknown);
                }
                catch (Exception e)
                { 
                    // Catch all exceptions thrown during the InvalidateProperty callstack
                    // and wrap them in an AnimationException 
 
                    throw new AnimationException(
                        (AnimationClock)sender, 
                        _dependencyProperty,
                        (IAnimatable)target,
                        SR.Get(
                            SRID.Animation_Exception, 
                            _dependencyProperty.Name,
                            target.GetType().FullName, 
                            ((AnimationClock)sender).Timeline.GetType().FullName), 
                        e);
                } 
            }
        }

        private void OnRemoveRequested(object sender, EventArgs args) 
        {
            Debug.Assert(   _animationClocks != null 
                         && _animationClocks.Count > 0, 
                "An AnimationClock no longer associated with a property should not have a RemoveRequested event handler.");
 
            AnimationClock animationClock = (AnimationClock)sender;

            int index = _animationClocks.IndexOf(animationClock);
 
            Debug.Assert(index > -1,
                "An AnimationClock no longer associated with a property should not have a RemoveRequested event handler."); 
 
            _animationClocks.RemoveAt(index);
 
            if (   _hasStickySnapshotValue
                && index == 0)
            {
                // The first clock is always the one that would have unstuck 
                // the snapshot value. Since it's been removed, instick the
                // snapshot value now. 
 
                _hasStickySnapshotValue = false;
                animationClock.CurrentStateInvalidated -= new EventHandler(OnCurrentStateInvalidated); 
            }

            if (_animationClocks.Count == 0)
            { 
                _animationClocks = null;
                _snapshotValue = DependencyProperty.UnsetValue; 
            } 

            DetachAnimationClock(animationClock, _removeRequestedHandler); 

            WritePostscript();
        }
 
        private void OnCurrentStateInvalidated(object sender, EventArgs args)
        { 
            Debug.Assert(_hasStickySnapshotValue, 
                "_hasStickySnapshotValue should be set to true if OnCurrentStateInvalidated has been called.");
 
            _hasStickySnapshotValue = false;

            ((AnimationClock)sender).CurrentStateInvalidated -= new EventHandler(OnCurrentStateInvalidated);
        } 

        private void ClearAnimations() 
        { 
            if (_animationClocks != null)
            { 
                Debug.Assert(_animationClocks.Count > 0);

                for (int i = 0; i < _animationClocks.Count; i++)
                { 
                    DetachAnimationClock(_animationClocks[i], _removeRequestedHandler);
                } 
 
                _animationClocks = null;
            } 
        }

#endregion
 
        #region Static Helper Methods
 
        internal static void ApplyAnimationClock( 
            DependencyObject d,
            DependencyProperty dp, 
            AnimationClock animationClock,
            HandoffBehavior handoffBehavior)
        {
            if (animationClock == null) 
            {
                BeginAnimation(d, dp, null, handoffBehavior); 
            } 
            else
            { 
                //

                ApplyAnimationClocks(d, dp, new AnimationClock[] { animationClock }, handoffBehavior);
            } 
        }
 
        [FriendAccessAllowed] // Built into Core, also used by Framework. 
        internal static void ApplyAnimationClocks(
            DependencyObject d, 
            DependencyProperty dp,
            IList animationClocks,
            HandoffBehavior handoffBehavior)
        { 
            Debug.Assert(animationClocks != null,
                "The animationClocks parameter should not be passed in as null."); 
            Debug.Assert(animationClocks.Count > 0, 
                "The animationClocks parameter should contain at least one clock.");
            Debug.Assert(!animationClocks.Contains(null), 
                "The animationClocks parameter should not contain a null entry.");
            Debug.Assert(HandoffBehaviorEnum.IsDefined(handoffBehavior),
                "Public API caller of this internal method is responsible for validating that the HandoffBehavior value is valid." );
 
            AnimationStorage storage = GetStorage(d, dp);
 
            // handoffBehavior is SnapshotAndReplace or the situation is such 
            // that it is the equivalent because we have nothing to compose
            // with. 
            if (   handoffBehavior == HandoffBehavior.SnapshotAndReplace
                || storage == null
                || storage._animationClocks == null)
            { 
                if (storage != null)
                { 
                    EventHandler handler = new EventHandler(storage.OnCurrentStateInvalidated); 

                    // If we have a sticky snapshot value, the clock that would have 
                    // unstuck it is being replaced, so we need to remove our event
                    // handler from that clock.
                    if (storage._hasStickySnapshotValue)
                    { 
                        storage._animationClocks[0].CurrentStateInvalidated -= handler;
                    } 
                    // Calculate a snapshot value if we don't already have one 
                    // since the last tick.
                    else 
                    {
                        storage._snapshotValue = d.GetValue(dp);
                    }
 
                    // If we have a new clock in a stopped state, then the snapshot
                    // value will be sticky. 
                    if (animationClocks[0].CurrentState == ClockState.Stopped) 
                    {
                        storage._hasStickySnapshotValue = true; 
                        animationClocks[0].CurrentStateInvalidated += new EventHandler(storage.OnCurrentStateInvalidated);
                    }
                    // Otherwise it won't be sticky.
                    else 
                    {
                        storage._hasStickySnapshotValue = false; 
                    } 

                    storage.ClearAnimations(); 
                }
                else
                {
                    storage = CreateStorage(d, dp); 
                }
 
                // Add and attach new animation. 
                storage._animationClocks = new FrugalObjectList(animationClocks.Count);
 
                for (int i = 0; i < animationClocks.Count; i++)
                {
                    Debug.Assert(animationClocks[i] != null);
 
                    storage._animationClocks.Add(animationClocks[i]);
                    storage.AttachAnimationClock(animationClocks[i], storage._removeRequestedHandler); 
                } 
            }
            else 
            {
                Debug.Assert(handoffBehavior == HandoffBehavior.Compose);
                Debug.Assert(storage != null);
                Debug.Assert(storage._animationClocks != null); 

                FrugalObjectList newClockCollection = new FrugalObjectList(storage._animationClocks.Count + animationClocks.Count); 
 
                for (int i = 0; i < storage._animationClocks.Count; i++)
                { 
                    newClockCollection.Add(storage._animationClocks[i]);
                }

                storage._animationClocks = newClockCollection; 

                for (int i = 0; i < animationClocks.Count; i++) 
                { 
                    newClockCollection.Add(animationClocks[i]);
                    storage.AttachAnimationClock(animationClocks[i], storage._removeRequestedHandler); 
                }
            }

            storage.WritePostscript(); 
        }
 
        ///  
        /// Applies animation clocks to a layer
        ///  
        /// 
        /// 
        /// 
        ///  
        /// 
        [FriendAccessAllowed] 
        internal static void ApplyAnimationClocksToLayer( 
            DependencyObject d,
            DependencyProperty dp, 
            IList animationClocks,
            HandoffBehavior handoffBehavior,
            Int64 propertyTriggerLayerIndex)
        { 
            if( propertyTriggerLayerIndex == 1 )
            { 
                // Layer 1 is a special layer, where it gets treated as if there 
                //  was no layer specification at all.
                ApplyAnimationClocks( d, dp, animationClocks, handoffBehavior ); 
                return;
            }

            Debug.Assert(animationClocks != null); 
            Debug.Assert(!animationClocks.Contains(null));
 
            Debug.Assert(HandoffBehaviorEnum.IsDefined(handoffBehavior), 
                "Public API caller of this internal method is responsible for validating that the HandoffBehavior value is valid.");
 
            AnimationStorage storage = GetStorage(d, dp);

            if (storage == null)
            { 
                storage = CreateStorage(d, dp);
            } 
 
            SortedList propertyTriggerLayers = storage._propertyTriggerLayers;
 
            if (propertyTriggerLayers == null)
            {
                propertyTriggerLayers = new SortedList(1);
                storage._propertyTriggerLayers = propertyTriggerLayers; 
            }
 
            AnimationLayer layer; 

            if (propertyTriggerLayers.ContainsKey(propertyTriggerLayerIndex)) 
            {
                layer = propertyTriggerLayers[propertyTriggerLayerIndex];
            }
            else 
            {
                layer = new AnimationLayer(storage); 
 
                propertyTriggerLayers[propertyTriggerLayerIndex] = layer;
            } 

            object defaultDestinationValue = DependencyProperty.UnsetValue;

            if (handoffBehavior == HandoffBehavior.SnapshotAndReplace) 
            {
                // 
                defaultDestinationValue = ((IAnimatable)d).GetAnimationBaseValue(dp); 

                int count = propertyTriggerLayers.Count; 

                if (count > 1)
                {
                    IList keys = propertyTriggerLayers.Keys; 

                    for (int i = 0; i < count && keys[i] < propertyTriggerLayerIndex; i++) 
                    { 
                        AnimationLayer currentLayer;
 
                        propertyTriggerLayers.TryGetValue(keys[i], out currentLayer);

                        defaultDestinationValue = currentLayer.GetCurrentValue(defaultDestinationValue);
                    } 
                }
            } 
 
            layer.ApplyAnimationClocks(
                animationClocks, 
                handoffBehavior,
                defaultDestinationValue);

            storage.WritePostscript(); 
        }
 
        internal static void BeginAnimation( 
            DependencyObject d,
            DependencyProperty dp, 
            AnimationTimeline animation,
            HandoffBehavior handoffBehavior)
        {
            // Caller should be validating animation. 
            Debug.Assert(animation == null || IsAnimationValid(dp, animation));
            Debug.Assert(IsPropertyAnimatable(d, dp)); 
 
            Debug.Assert(HandoffBehaviorEnum.IsDefined(handoffBehavior),
                "Public API caller of this internal method is responsible for validating that the HandoffBehavior value is valid." ); 

            AnimationStorage storage = GetStorage(d, dp);

            if (animation == null) 
            {
                if (   storage == null 
                    || handoffBehavior == HandoffBehavior.Compose) 
                {
                    // Composing with a null animation is a no-op. 
                    return;
                }
                else
                { 
                    // When the incoming animation is passed in as null and
                    // handoffBehavior == HandoffBehavior.SnapshotAndReplace it means 
                    // that we should stop any and all animation behavior for 
                    // this property.
                    if (storage._hasStickySnapshotValue) 
                    {
                        storage._hasStickySnapshotValue = false;
                        storage._animationClocks[0].CurrentStateInvalidated -= new EventHandler(storage.OnCurrentStateInvalidated);
                    } 

                    storage._snapshotValue = DependencyProperty.UnsetValue; 
                    storage.ClearAnimations(); 
                }
            } 
            else if (animation.BeginTime.HasValue)
            {
                // We have an animation
                AnimationClock animationClock; 
                animationClock = animation.CreateClock();  // note that CreateClock also calls InternalBeginIn
 
                ApplyAnimationClocks(d, dp, new AnimationClock[] { animationClock }, handoffBehavior); 

                // ApplyAnimationClocks has fixed up the storage and called 
                // WritePostscript already so we can just return.
                return;
            }
            else if (storage == null) 
            {
                //  The user gave us an animation with a BeginTime of null which 
                // means snapshot the current value and throw away all animations 
                // for SnapshotAndReplace and means nothing for Compose.
                //  But since we don't have any animations the current we don't 
                // have to do anything for either of these cases.

                return;
            } 
            else if (handoffBehavior == HandoffBehavior.SnapshotAndReplace)
            { 
                // This block handles the case where the user has called 
                // BeginAnimation with an animation that has a null begin time.
                // We handle this by taking a snapshot value and throwing 
                // out the animation, which is the same as keeping it because
                // we know it will never start.
                //
                // If the handoffBehavior is Compose, we ignore the user's call 
                // because it wouldn't mean anything unless they were planning
                // on changing the BeginTime of their animation later, which we 
                // don't support. 

                // If _hasStickySnapshotValue is set, unset it and remove our 
                // event handler from the clock. The current snapshot value
                // will still be used.
                if (storage._hasStickySnapshotValue)
                { 
                    Debug.Assert(storage._animationClocks != null && storage._animationClocks.Count > 0,
                        "If _hasStickySnapshotValue is set we should have at least one animation clock stored in the AnimationStorage."); 
 
                    storage._hasStickySnapshotValue = false;
                    storage._animationClocks[0].CurrentStateInvalidated -= new EventHandler(storage.OnCurrentStateInvalidated); 
                }
                // Otherwise take a new snapshot value.
                else
                { 
                    storage._snapshotValue = d.GetValue(dp);
                } 
 
                storage.ClearAnimations();
            } 

            // If storage were null we would have returned already.
            storage.WritePostscript();
        } 

        internal static AnimationStorage EnsureStorage( 
            DependencyObject d, 
            DependencyProperty dp)
        { 
            FrugalMap animatedPropertyMap = AnimatedPropertyMapField.GetValue(d);
            object currentStorage = animatedPropertyMap[dp.GlobalIndex];

            if (currentStorage == DependencyProperty.UnsetValue) 
            {
                return CreateStorage(d, dp); 
            } 
            else
            { 
                return (AnimationStorage)currentStorage;
            }
        }
 
        /// 
        /// GetCurrentPropertyValue 
        ///  
        /// 
        internal static object GetCurrentPropertyValue( 
            AnimationStorage storage,
            DependencyObject d,
            DependencyProperty dp,
            PropertyMetadata metadata, 
            object baseValue)
        { 
            Debug.Assert(storage != null, 
                "The 'storage' parameter cannot be passed into the GetCurrentPropertyValue method as null.");
 
            // If changes have been made to the snapshot value since the last tick
            // that value represents the current value of the property until the
            // next tick when the flag will be cleared. We will only take one
            // snapshot value between ticks. 
            //
            // Since CurrentTimeInvaliated is raised before CurrentStateInvalidated 
            // we need to check the state of the first clock as well to avoid 
            // potential first frame issues. In this case _hasStickySnapshotValue
            // will be updated to false shortly. 
            if (   storage._hasStickySnapshotValue
                && storage._animationClocks[0].CurrentState == ClockState.Stopped)
            {
                return storage._snapshotValue; 
            }
 
            if (   storage._animationClocks == null 
                && storage._propertyTriggerLayers == null)
            { 
                Debug.Assert(storage._snapshotValue != DependencyProperty.UnsetValue);

                return storage._snapshotValue;
            } 

            object currentPropertyValue = baseValue; 
 
            if (currentPropertyValue == DependencyProperty.UnsetValue)
            { 
                currentPropertyValue = metadata.GetDefaultValue(d, dp);
            }

            Debug.Assert(currentPropertyValue != DependencyProperty.UnsetValue); 

            // 
            // Process property trigger animations. 
            //
 
            if (storage._propertyTriggerLayers != null)
            {
                int count = storage._propertyTriggerLayers.Count;
 
                Debug.Assert(count > 0);
 
                IList layers = storage._propertyTriggerLayers.Values; 

                for (int i = 0; i < count; i++) 
                {
                    currentPropertyValue = layers[i].GetCurrentValue(currentPropertyValue);
                }
            } 

            // 
            // Process local animations 
            //
 
            if (storage._animationClocks != null)
            {
                FrugalObjectList clocks = storage._animationClocks;
                int clocksCount = clocks.Count; 
                bool hasActiveOrFillingClock = false;
 
                // default destination value will be the current property value 
                // calculated by the previous layer.
                object defaultDestinationValue = currentPropertyValue; 
                object currentLayerValue = currentPropertyValue;

                // if we have a snapshot value, then that will be the new
                // initial current property value. 
                if (storage._snapshotValue != DependencyProperty.UnsetValue)
                { 
                    currentLayerValue = storage._snapshotValue; 
                }
 
                Debug.Assert(clocksCount > 0);
                Debug.Assert(defaultDestinationValue != DependencyProperty.UnsetValue);

                for (int i = 0; i < clocksCount; i++) 
                {
                    if (clocks[i].CurrentState != ClockState.Stopped) 
                    { 
                        hasActiveOrFillingClock = true;
 
                        currentLayerValue = clocks[i].GetCurrentValue(currentLayerValue, defaultDestinationValue);

                        // An animation may not return DependencyProperty.UnsetValue as its
                        // current value. 
                        if (currentLayerValue == DependencyProperty.UnsetValue)
                        { 
                            throw new InvalidOperationException(SR.Get( 
                                SRID.Animation_ReturnedUnsetValueInstance,
                                clocks[i].Timeline.GetType().FullName, 
                                dp.Name,
                                d.GetType().FullName));
                        }
                    } 
                }
 
                // The currentLayerValue only applies when there is at least one 
                // active or filling clock.
                if (hasActiveOrFillingClock) 
                {
                    currentPropertyValue = currentLayerValue;
                }
            } 

            // We have a calculated currentPropertyValue, so validate it and 
            // then return it. 
            if (dp.IsValidValue(currentPropertyValue))
            { 
                return currentPropertyValue;
            }
            else
            { 
                // If the animation(s) applied to the property have calculated an
                // invalid value for the property then raise an exception. 
                throw new InvalidOperationException( 
                    SR.Get(
                        SRID.Animation_CalculatedValueIsInvalidForProperty, 
                        dp.Name,
                        (currentPropertyValue == null ? "null" : currentPropertyValue.ToString())));
            }
        } 

        [FriendAccessAllowed] // Built into Core, also used by Framework. 
        internal static bool IsPropertyAnimatable( 
            DependencyObject d,
            DependencyProperty dp) 
        {
            //

 

            if (dp.PropertyType != typeof(Visual3DCollection) && dp.ReadOnly) 
            { 
                return false;
            } 

            UIPropertyMetadata uiMetadata = dp.GetMetadata(d.DependencyObjectType) as UIPropertyMetadata;

            if (   uiMetadata != null 
                && uiMetadata.IsAnimationProhibited)
            { 
                return false; 
            }
 
            return true;
        }

        internal static bool IsAnimationValid( 
            DependencyProperty dp,
            AnimationTimeline animation) 
        { 
            return dp.PropertyType.IsAssignableFrom(animation.TargetPropertyType)
                || (animation.TargetPropertyType == typeof(Object)); 
        }

        [FriendAccessAllowed] // Built into Core, also used by Framework.
        internal static bool IsAnimationClockValid( 
            DependencyProperty dp,
            AnimationClock animation) 
        { 
            return IsAnimationValid(dp, (AnimationTimeline)animation.Timeline);
        } 

        /// 
        /// Returns the list of animated DependencyProperties for a
        /// given DependencyObject. 
        /// 
        ///  
        /// The DependencyObject. 
        /// 
        ///  
        /// The list of animated DependencyProperties if any are animated;
        /// otherwise null.
        /// 
        internal static FrugalMap GetAnimatedPropertiesMap(DependencyObject d) 
        {
            return AnimatedPropertyMapField.GetValue(d); 
        } 

        ///  
        /// Returns the AnimationStorage associated with a given DependencyProperty
        /// on a give DependencyObject.
        /// 
        /// The DependencyObject. 
        /// The DependencyProperty.
        ///  
        /// The AnimationStorage associated with the DependencyProperty if it 
        /// has any; otherwise null.
        ///  
        internal static AnimationStorage GetStorage(DependencyObject d, DependencyProperty dp)
        {
            Debug.Assert(   AnimatedPropertyMapField.GetValue(d)[dp.GlobalIndex] == DependencyProperty.UnsetValue
                         || AnimatedPropertyMapField.GetValue(d)[dp.GlobalIndex] is AnimationStorage); 

            return AnimatedPropertyMapField.GetValue(d)[dp.GlobalIndex] as AnimationStorage; 
        } 

        private static AnimationStorage CreateStorage( 
             DependencyObject d,
             DependencyProperty dp)
        {
            AnimationStorage newStorage; 

            if (dp.GetMetadata(d.DependencyObjectType) is IndependentlyAnimatedPropertyMetadata) 
            { 
                newStorage = CreateIndependentAnimationStorageForType(dp.PropertyType);
            } 
            else
            {
                newStorage = new AnimationStorage();
            } 

            newStorage.Initialize(d, dp); 
 
            return newStorage;
        } 

        private static IndependentAnimationStorage CreateIndependentAnimationStorageForType(Type type)
        {
            if (type == typeof(Double)) 
            {
                return new DoubleIndependentAnimationStorage(); 
            } 
            else if (type == typeof(Color))
            { 
                return new ColorIndependentAnimationStorage();
            }
            else if (type == typeof(Matrix))
            { 
                return new MatrixIndependentAnimationStorage();
            } 
            else if (type == typeof(Point3D)) 
            {
                return new Point3DIndependentAnimationStorage(); 
            }
            else if (type == typeof(Point))
            {
                return new PointIndependentAnimationStorage(); 
            }
            else if (type == typeof(Quaternion)) 
            { 
                return new QuaternionIndependentAnimationStorage();
            } 
            else if (type == typeof(Rect))
            {
                return new RectIndependentAnimationStorage();
            } 
            else if (type == typeof(Size))
            { 
                return new SizeIndependentAnimationStorage(); 
            }
            else 
            {
                Debug.Assert(type == typeof(Vector3D), "Application is trying to create independent animation storage for an unsupported type.");

                return new Vector3DIndependentAnimationStorage(); 
            }
        } 
 
        #endregion
 
        #region Data

        protected WeakReference _dependencyObject;
        protected DependencyProperty _dependencyProperty; 
        protected FrugalObjectList _animationClocks;
        private SortedList _propertyTriggerLayers; 
 
        private EventHandler _currentTimeInvalidatedHandler;
        private EventHandler _removeRequestedHandler; 
        private object _snapshotValue = DependencyProperty.UnsetValue;
        private bool _hasStickySnapshotValue;

        // This can be used by Animatables to store a local value if 
        // they provide a WriteLocalValueOverride but need to spill
        // the local value if they are animated because they also use 
        // the local value storage as a current value cache. 
        internal object _baseValue = DependencyProperty.UnsetValue;
 
        #endregion
    }
}

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

using MS.Internal;
using MS.Utility;
using System.Collections.Generic; 
using System.Diagnostics;
using System.Windows.Threading; 
using System.Windows.Media.Composition; 
using System.Windows.Media.Media3D;
 
using MS.Internal.PresentationCore;     // SR, SRID, FriendAccessAllowed

namespace System.Windows.Media.Animation
{ 
    internal class AnimationStorage
    { 
 
#if DEBUG
 
        private static int _nextID = 1;

        private readonly int DebugID = _nextID++;
 
#endif
 
        #region Constructor 

        protected AnimationStorage() 
        {
            _currentTimeInvalidatedHandler = new EventHandler(OnCurrentTimeInvalidated);
            _removeRequestedHandler = new EventHandler(OnRemoveRequested);
        } 

        #endregion 
 
        #region Uncommon Fields
 
        private static readonly UncommonField AnimatedPropertyMapField = new UncommonField();

        #endregion
 
        #region Properties
 
        internal bool IsEmpty 
        {
            get 
            {
                Debug.Assert(   _animationClocks == null
                             || _animationClocks.Count > 0);
 
                Debug.Assert(   _propertyTriggerLayers == null
                             || _propertyTriggerLayers.Count > 0); 
 
                return _animationClocks == null
                    && _propertyTriggerLayers == null 
                    && _snapshotValue == DependencyProperty.UnsetValue;
            }
        }
 
        #endregion
 
        #region Methods 

        ///  
        /// Attaches an AnimationClock but does not add it to the collection.
        /// 
        /// 
        /// It's expected that the AnimationClock will have been added to the 
        /// collection before this is called.
        ///  
        /// The AnimationClock. 
        /// 
        /// The event handler to be executed when the RemoveRequested event is 
        /// raised by the AnimationClock.
        /// 
        internal void AttachAnimationClock(
            AnimationClock animationClock, 
            EventHandler removeRequestedHandler)
        { 
            Debug.Assert(animationClock != null); 
            Debug.Assert(_dependencyObject.Target != null);
            Debug.Assert(_currentTimeInvalidatedHandler != null); 

            animationClock.CurrentTimeInvalidated += _currentTimeInvalidatedHandler;

            if (animationClock.HasControllableRoot) 
            {
                animationClock.RemoveRequested += removeRequestedHandler; 
            } 
        }
 
        /// 
        /// Detaches an AnimationClock but does not remove it from the collection.
        /// 
        ///  
        /// It's expected that the AnimationClock will be removed from the
        /// collection immediately after calling this method. 
        ///  
        /// The AnimationClock.
        ///  
        /// The event handler associated with the RemoveRequested event on the
        /// AnimationClock.
        /// 
        internal void DetachAnimationClock( 
            AnimationClock animationClock,
            EventHandler removeRequestedHandler) 
        { 
            Debug.Assert(animationClock != null);
            Debug.Assert(_currentTimeInvalidatedHandler != null); 

            animationClock.CurrentTimeInvalidated -= _currentTimeInvalidatedHandler;

            if (animationClock.HasControllableRoot) 
            {
                animationClock.RemoveRequested -= removeRequestedHandler; 
            } 
        }
 
        internal void Initialize(DependencyObject d, DependencyProperty dp)
        {
            Debug.Assert(_dependencyObject == null);
            Debug.Assert(_dependencyProperty == null); 

            Animatable a = d as Animatable; 
 
            if (a != null)
            { 
                _dependencyObject = a.GetWeakReference();
            }
            else
            { 
                //
 
                _dependencyObject = new WeakReference(d); 
            }
 
            _dependencyProperty = dp;
        }

        ///  
        ///
        ///  
        ///  
        internal void RemoveLayer(AnimationLayer layer)
        { 
            Debug.Assert(_propertyTriggerLayers != null);
            Debug.Assert(_propertyTriggerLayers.ContainsValue(layer));

            int index = _propertyTriggerLayers.IndexOfValue(layer); 

            Debug.Assert(index >= 0); 
 
            _propertyTriggerLayers.RemoveAt(index);
 
            if (_propertyTriggerLayers.Count == 0)
            {
                _propertyTriggerLayers = null;
            } 
        }
 
        ///  
        /// This should be called at the end of any method that alters the
        /// storage in any way.  This method will make sure the peer dp is 
        /// set correctly and notify Animatables if something changes.
        /// 
        internal void WritePostscript()
        { 
            DependencyObject d = (DependencyObject)_dependencyObject.Target;
 
            if (d == null) 
            {
                return; 
            }

            FrugalMap animatedPropertyMap = AnimatedPropertyMapField.GetValue(d);
 
            if (   animatedPropertyMap.Count == 0
                || animatedPropertyMap[_dependencyProperty.GlobalIndex] == DependencyProperty.UnsetValue) 
            { 
                if (!IsEmpty)
                { 
                    // This is kind of tricky:
                    //
                    // Because FrugalMap is a struct instead of a class, we must
                    // be sure to add this AnimationStorage to the map before 
                    // setting the FrugalMap into the UncommonField storage. If
                    // we don't and the FrugalMap is empty then an empty FrugalMap 
                    // will be set into the UncommonField storage. If we were to 
                    // then add our AnimationStorage to the local FrugalMap, it
                    // would only allocate its own internal storage at that point 
                    // which would not apply to the FrugalMap we set into the
                    // UncommonField storage. Once a FrugalMap has allocated its
                    // internal storage, though, that storage is copied with the
                    // FrugalMap. This is what will happen when we add our 
                    // AnimationStorage to the FrugalMap first as we do below:
 
                    animatedPropertyMap[_dependencyProperty.GlobalIndex] = this; 

                    // Since FrugalMap is a struct and adding a new value to it 
                    // may re-allocate the storage, we need to set this value
                    // each time we make a change to the map.

                    AnimatedPropertyMapField.SetValue(d, animatedPropertyMap); 

                    if (animatedPropertyMap.Count == 1) 
                    { 
                        d.IAnimatable_HasAnimatedProperties = true;
                    } 

                    // If this the target is an Animatable we'll need to
                    // invalidate it so that the animation resource for this
                    // newly animated property will be passed across to the UCE. 
                    Animatable a = d as Animatable;
 
                    if (a != null) 
                    {
                        a.RegisterForAsyncUpdateResource(); 
                    }

                    // If this AnimationStorage is a resource, add it to the
                    // channel now. 
                    DUCE.IResource animationResource = this as DUCE.IResource;
 
                    if (animationResource != null) 
                    {
                        DUCE.IResource targetResource = d as DUCE.IResource; 

                        if (targetResource != null)
                        {
                            using (CompositionEngineLock.Acquire()) 
                            {
                                int channelCount = targetResource.GetChannelCount(); 
 
                                for (int i = 0; i < channelCount; i++)
                                { 
                                    DUCE.Channel channel = targetResource.GetChannel(i);
                                    if (!targetResource.GetHandle(channel).IsNull)
                                    {
                                        animationResource.AddRefOnChannel(channel); 
                                    }
                                } 
                            } 
                        }
                    } 
                }
            }
            else
            { 
                Debug.Assert(animatedPropertyMap.Count > 0);
                Debug.Assert(animatedPropertyMap[_dependencyProperty.GlobalIndex] != DependencyProperty.UnsetValue); 
 
                if (IsEmpty)
                { 
                    // If this AnimationStorage is a resource, release it from
                    // the channel now.
                    DUCE.IResource animationResource = this as DUCE.IResource;
 
                    if (animationResource != null)
                    { 
                        DUCE.IResource targetResource = d as DUCE.IResource; 

                        if (targetResource != null) 
                        {
                            using (CompositionEngineLock.Acquire())
                            {
                                int channelCount = targetResource.GetChannelCount(); 

                                for (int i = 0; i < channelCount; i++) 
                                { 
                                    DUCE.Channel channel = targetResource.GetChannel(i);
                                    if (!targetResource.GetHandle(channel).IsNull) 
                                    {
                                        animationResource.ReleaseOnChannel(channel);
                                    }
                                } 
                            }
                        } 
                    } 

                    // If this the target is an Animatable we'll need to 
                    // invalidate it so that the animation resource for this
                    // no longer animated property will no longer be passed
                    // across to the UCE.
                    Animatable a = d as Animatable; 

                    if (a != null) 
                    { 
                        a.RegisterForAsyncUpdateResource();
                    } 

                    animatedPropertyMap[_dependencyProperty.GlobalIndex] = DependencyProperty.UnsetValue;

                    if (animatedPropertyMap.Count == 0) 
                    {
                        AnimatedPropertyMapField.ClearValue(d); 
 
                        d.IAnimatable_HasAnimatedProperties = false;
                    } 
                    else
                    {
                        AnimatedPropertyMapField.SetValue(d, animatedPropertyMap);
                    } 

                    // We've removed animation storage for this DP, so if we were storing the local 
                    // base value here then it has to go back to its non-animated storage spot. 
                    if (_baseValue != DependencyProperty.UnsetValue)
                    { 
                        d.SetValue(_dependencyProperty, _baseValue);
                    }
                }
            } 

            // recompute animated value 
            d.InvalidateProperty(_dependencyProperty); 
        }
 

        #endregion

        #region Private 

        private void OnCurrentTimeInvalidated(object sender, EventArgs args) 
        { 
            object target = _dependencyObject.Target;
 
            if (target == null)
            {
                // If the target has been garbage collected, remove this handler
                // from the AnimationClock so that this collection can be 
                // released also.
                DetachAnimationClock((AnimationClock)sender, _removeRequestedHandler); 
            } 
            else
            { 
                // recompute animated value
                try
                {
                    DependencyObject targetDO = ((DependencyObject)target); 

                    // fetch the existing entry 
                    EffectiveValueEntry oldEntry = targetDO.GetValueEntry( 
                            targetDO.LookupEntry(_dependencyProperty.GlobalIndex),
                            _dependencyProperty, 
                            null,
                            RequestFlags.RawEntry);

                    EffectiveValueEntry newEntry; 
                    object value;
 
                    // create a copy of that entry, removing animated & coerced values 

                    if (!oldEntry.HasModifiers) 
                    {
                        // no modifiers; just use it, removing deferred references
                        newEntry = oldEntry;
                        value = newEntry.Value; 
                        if (newEntry.IsDeferredReference)
                        { 
                            value = ((DeferredReference) value).GetValue(newEntry.BaseValueSourceInternal); 
                            newEntry.Value = value;
                            newEntry.IsDeferredReference = false; 
                        }
                    }
                    else
                    { 
                        // else entry has modifiers; preserve expression but throw away
                        // coerced & animated values, since we'll be recomputing an animated value 
                        newEntry = new EffectiveValueEntry(); 
                        newEntry.BaseValueSourceInternal = oldEntry.BaseValueSourceInternal;
                        newEntry.PropertyIndex = oldEntry.PropertyIndex; 
                        newEntry.HasExpressionMarker = oldEntry.HasExpressionMarker;

                        value = oldEntry.ModifiedValue.BaseValue;
                        if (oldEntry.IsDeferredReference) 
                        {
                            DeferredReference dr = value as DeferredReference; 
                            if (dr != null) 
                            {
                                value = dr.GetValue(newEntry.BaseValueSourceInternal); 
                            }
                        }

                        newEntry.Value = value; 

                        if (oldEntry.IsExpression) 
                        { 
                            value = oldEntry.ModifiedValue.ExpressionValue;
                            if (oldEntry.IsDeferredReference) 
                            {
                                DeferredReference dr = value as DeferredReference;
                                if (dr != null)
                                { 
                                    value = dr.GetValue(newEntry.BaseValueSourceInternal);
                                } 
                            } 
                            newEntry.SetExpressionValue(value, newEntry.Value);
                        } 
                    }

                    // compute the new value for the property
 
                    PropertyMetadata metadata = _dependencyProperty.GetMetadata(targetDO.DependencyObjectType);
                    object animatedValue = AnimationStorage.GetCurrentPropertyValue(this, targetDO, _dependencyProperty, metadata, value); 
 
                    // update the new entry to contain the new animated value
                    newEntry.SetAnimatedValue(animatedValue, value); 

                    // call UpdateEffectiveValue to put the new entry in targetDO's effective values table
                    targetDO.UpdateEffectiveValue(
                            targetDO.LookupEntry(_dependencyProperty.GlobalIndex), 
                            _dependencyProperty,
                            metadata, 
                            oldEntry, 
                            ref newEntry,
                            false /* coerceWithDeferredReference */, 
                            OperationType.Unknown);
                }
                catch (Exception e)
                { 
                    // Catch all exceptions thrown during the InvalidateProperty callstack
                    // and wrap them in an AnimationException 
 
                    throw new AnimationException(
                        (AnimationClock)sender, 
                        _dependencyProperty,
                        (IAnimatable)target,
                        SR.Get(
                            SRID.Animation_Exception, 
                            _dependencyProperty.Name,
                            target.GetType().FullName, 
                            ((AnimationClock)sender).Timeline.GetType().FullName), 
                        e);
                } 
            }
        }

        private void OnRemoveRequested(object sender, EventArgs args) 
        {
            Debug.Assert(   _animationClocks != null 
                         && _animationClocks.Count > 0, 
                "An AnimationClock no longer associated with a property should not have a RemoveRequested event handler.");
 
            AnimationClock animationClock = (AnimationClock)sender;

            int index = _animationClocks.IndexOf(animationClock);
 
            Debug.Assert(index > -1,
                "An AnimationClock no longer associated with a property should not have a RemoveRequested event handler."); 
 
            _animationClocks.RemoveAt(index);
 
            if (   _hasStickySnapshotValue
                && index == 0)
            {
                // The first clock is always the one that would have unstuck 
                // the snapshot value. Since it's been removed, instick the
                // snapshot value now. 
 
                _hasStickySnapshotValue = false;
                animationClock.CurrentStateInvalidated -= new EventHandler(OnCurrentStateInvalidated); 
            }

            if (_animationClocks.Count == 0)
            { 
                _animationClocks = null;
                _snapshotValue = DependencyProperty.UnsetValue; 
            } 

            DetachAnimationClock(animationClock, _removeRequestedHandler); 

            WritePostscript();
        }
 
        private void OnCurrentStateInvalidated(object sender, EventArgs args)
        { 
            Debug.Assert(_hasStickySnapshotValue, 
                "_hasStickySnapshotValue should be set to true if OnCurrentStateInvalidated has been called.");
 
            _hasStickySnapshotValue = false;

            ((AnimationClock)sender).CurrentStateInvalidated -= new EventHandler(OnCurrentStateInvalidated);
        } 

        private void ClearAnimations() 
        { 
            if (_animationClocks != null)
            { 
                Debug.Assert(_animationClocks.Count > 0);

                for (int i = 0; i < _animationClocks.Count; i++)
                { 
                    DetachAnimationClock(_animationClocks[i], _removeRequestedHandler);
                } 
 
                _animationClocks = null;
            } 
        }

#endregion
 
        #region Static Helper Methods
 
        internal static void ApplyAnimationClock( 
            DependencyObject d,
            DependencyProperty dp, 
            AnimationClock animationClock,
            HandoffBehavior handoffBehavior)
        {
            if (animationClock == null) 
            {
                BeginAnimation(d, dp, null, handoffBehavior); 
            } 
            else
            { 
                //

                ApplyAnimationClocks(d, dp, new AnimationClock[] { animationClock }, handoffBehavior);
            } 
        }
 
        [FriendAccessAllowed] // Built into Core, also used by Framework. 
        internal static void ApplyAnimationClocks(
            DependencyObject d, 
            DependencyProperty dp,
            IList animationClocks,
            HandoffBehavior handoffBehavior)
        { 
            Debug.Assert(animationClocks != null,
                "The animationClocks parameter should not be passed in as null."); 
            Debug.Assert(animationClocks.Count > 0, 
                "The animationClocks parameter should contain at least one clock.");
            Debug.Assert(!animationClocks.Contains(null), 
                "The animationClocks parameter should not contain a null entry.");
            Debug.Assert(HandoffBehaviorEnum.IsDefined(handoffBehavior),
                "Public API caller of this internal method is responsible for validating that the HandoffBehavior value is valid." );
 
            AnimationStorage storage = GetStorage(d, dp);
 
            // handoffBehavior is SnapshotAndReplace or the situation is such 
            // that it is the equivalent because we have nothing to compose
            // with. 
            if (   handoffBehavior == HandoffBehavior.SnapshotAndReplace
                || storage == null
                || storage._animationClocks == null)
            { 
                if (storage != null)
                { 
                    EventHandler handler = new EventHandler(storage.OnCurrentStateInvalidated); 

                    // If we have a sticky snapshot value, the clock that would have 
                    // unstuck it is being replaced, so we need to remove our event
                    // handler from that clock.
                    if (storage._hasStickySnapshotValue)
                    { 
                        storage._animationClocks[0].CurrentStateInvalidated -= handler;
                    } 
                    // Calculate a snapshot value if we don't already have one 
                    // since the last tick.
                    else 
                    {
                        storage._snapshotValue = d.GetValue(dp);
                    }
 
                    // If we have a new clock in a stopped state, then the snapshot
                    // value will be sticky. 
                    if (animationClocks[0].CurrentState == ClockState.Stopped) 
                    {
                        storage._hasStickySnapshotValue = true; 
                        animationClocks[0].CurrentStateInvalidated += new EventHandler(storage.OnCurrentStateInvalidated);
                    }
                    // Otherwise it won't be sticky.
                    else 
                    {
                        storage._hasStickySnapshotValue = false; 
                    } 

                    storage.ClearAnimations(); 
                }
                else
                {
                    storage = CreateStorage(d, dp); 
                }
 
                // Add and attach new animation. 
                storage._animationClocks = new FrugalObjectList(animationClocks.Count);
 
                for (int i = 0; i < animationClocks.Count; i++)
                {
                    Debug.Assert(animationClocks[i] != null);
 
                    storage._animationClocks.Add(animationClocks[i]);
                    storage.AttachAnimationClock(animationClocks[i], storage._removeRequestedHandler); 
                } 
            }
            else 
            {
                Debug.Assert(handoffBehavior == HandoffBehavior.Compose);
                Debug.Assert(storage != null);
                Debug.Assert(storage._animationClocks != null); 

                FrugalObjectList newClockCollection = new FrugalObjectList(storage._animationClocks.Count + animationClocks.Count); 
 
                for (int i = 0; i < storage._animationClocks.Count; i++)
                { 
                    newClockCollection.Add(storage._animationClocks[i]);
                }

                storage._animationClocks = newClockCollection; 

                for (int i = 0; i < animationClocks.Count; i++) 
                { 
                    newClockCollection.Add(animationClocks[i]);
                    storage.AttachAnimationClock(animationClocks[i], storage._removeRequestedHandler); 
                }
            }

            storage.WritePostscript(); 
        }
 
        ///  
        /// Applies animation clocks to a layer
        ///  
        /// 
        /// 
        /// 
        ///  
        /// 
        [FriendAccessAllowed] 
        internal static void ApplyAnimationClocksToLayer( 
            DependencyObject d,
            DependencyProperty dp, 
            IList animationClocks,
            HandoffBehavior handoffBehavior,
            Int64 propertyTriggerLayerIndex)
        { 
            if( propertyTriggerLayerIndex == 1 )
            { 
                // Layer 1 is a special layer, where it gets treated as if there 
                //  was no layer specification at all.
                ApplyAnimationClocks( d, dp, animationClocks, handoffBehavior ); 
                return;
            }

            Debug.Assert(animationClocks != null); 
            Debug.Assert(!animationClocks.Contains(null));
 
            Debug.Assert(HandoffBehaviorEnum.IsDefined(handoffBehavior), 
                "Public API caller of this internal method is responsible for validating that the HandoffBehavior value is valid.");
 
            AnimationStorage storage = GetStorage(d, dp);

            if (storage == null)
            { 
                storage = CreateStorage(d, dp);
            } 
 
            SortedList propertyTriggerLayers = storage._propertyTriggerLayers;
 
            if (propertyTriggerLayers == null)
            {
                propertyTriggerLayers = new SortedList(1);
                storage._propertyTriggerLayers = propertyTriggerLayers; 
            }
 
            AnimationLayer layer; 

            if (propertyTriggerLayers.ContainsKey(propertyTriggerLayerIndex)) 
            {
                layer = propertyTriggerLayers[propertyTriggerLayerIndex];
            }
            else 
            {
                layer = new AnimationLayer(storage); 
 
                propertyTriggerLayers[propertyTriggerLayerIndex] = layer;
            } 

            object defaultDestinationValue = DependencyProperty.UnsetValue;

            if (handoffBehavior == HandoffBehavior.SnapshotAndReplace) 
            {
                // 
                defaultDestinationValue = ((IAnimatable)d).GetAnimationBaseValue(dp); 

                int count = propertyTriggerLayers.Count; 

                if (count > 1)
                {
                    IList keys = propertyTriggerLayers.Keys; 

                    for (int i = 0; i < count && keys[i] < propertyTriggerLayerIndex; i++) 
                    { 
                        AnimationLayer currentLayer;
 
                        propertyTriggerLayers.TryGetValue(keys[i], out currentLayer);

                        defaultDestinationValue = currentLayer.GetCurrentValue(defaultDestinationValue);
                    } 
                }
            } 
 
            layer.ApplyAnimationClocks(
                animationClocks, 
                handoffBehavior,
                defaultDestinationValue);

            storage.WritePostscript(); 
        }
 
        internal static void BeginAnimation( 
            DependencyObject d,
            DependencyProperty dp, 
            AnimationTimeline animation,
            HandoffBehavior handoffBehavior)
        {
            // Caller should be validating animation. 
            Debug.Assert(animation == null || IsAnimationValid(dp, animation));
            Debug.Assert(IsPropertyAnimatable(d, dp)); 
 
            Debug.Assert(HandoffBehaviorEnum.IsDefined(handoffBehavior),
                "Public API caller of this internal method is responsible for validating that the HandoffBehavior value is valid." ); 

            AnimationStorage storage = GetStorage(d, dp);

            if (animation == null) 
            {
                if (   storage == null 
                    || handoffBehavior == HandoffBehavior.Compose) 
                {
                    // Composing with a null animation is a no-op. 
                    return;
                }
                else
                { 
                    // When the incoming animation is passed in as null and
                    // handoffBehavior == HandoffBehavior.SnapshotAndReplace it means 
                    // that we should stop any and all animation behavior for 
                    // this property.
                    if (storage._hasStickySnapshotValue) 
                    {
                        storage._hasStickySnapshotValue = false;
                        storage._animationClocks[0].CurrentStateInvalidated -= new EventHandler(storage.OnCurrentStateInvalidated);
                    } 

                    storage._snapshotValue = DependencyProperty.UnsetValue; 
                    storage.ClearAnimations(); 
                }
            } 
            else if (animation.BeginTime.HasValue)
            {
                // We have an animation
                AnimationClock animationClock; 
                animationClock = animation.CreateClock();  // note that CreateClock also calls InternalBeginIn
 
                ApplyAnimationClocks(d, dp, new AnimationClock[] { animationClock }, handoffBehavior); 

                // ApplyAnimationClocks has fixed up the storage and called 
                // WritePostscript already so we can just return.
                return;
            }
            else if (storage == null) 
            {
                //  The user gave us an animation with a BeginTime of null which 
                // means snapshot the current value and throw away all animations 
                // for SnapshotAndReplace and means nothing for Compose.
                //  But since we don't have any animations the current we don't 
                // have to do anything for either of these cases.

                return;
            } 
            else if (handoffBehavior == HandoffBehavior.SnapshotAndReplace)
            { 
                // This block handles the case where the user has called 
                // BeginAnimation with an animation that has a null begin time.
                // We handle this by taking a snapshot value and throwing 
                // out the animation, which is the same as keeping it because
                // we know it will never start.
                //
                // If the handoffBehavior is Compose, we ignore the user's call 
                // because it wouldn't mean anything unless they were planning
                // on changing the BeginTime of their animation later, which we 
                // don't support. 

                // If _hasStickySnapshotValue is set, unset it and remove our 
                // event handler from the clock. The current snapshot value
                // will still be used.
                if (storage._hasStickySnapshotValue)
                { 
                    Debug.Assert(storage._animationClocks != null && storage._animationClocks.Count > 0,
                        "If _hasStickySnapshotValue is set we should have at least one animation clock stored in the AnimationStorage."); 
 
                    storage._hasStickySnapshotValue = false;
                    storage._animationClocks[0].CurrentStateInvalidated -= new EventHandler(storage.OnCurrentStateInvalidated); 
                }
                // Otherwise take a new snapshot value.
                else
                { 
                    storage._snapshotValue = d.GetValue(dp);
                } 
 
                storage.ClearAnimations();
            } 

            // If storage were null we would have returned already.
            storage.WritePostscript();
        } 

        internal static AnimationStorage EnsureStorage( 
            DependencyObject d, 
            DependencyProperty dp)
        { 
            FrugalMap animatedPropertyMap = AnimatedPropertyMapField.GetValue(d);
            object currentStorage = animatedPropertyMap[dp.GlobalIndex];

            if (currentStorage == DependencyProperty.UnsetValue) 
            {
                return CreateStorage(d, dp); 
            } 
            else
            { 
                return (AnimationStorage)currentStorage;
            }
        }
 
        /// 
        /// GetCurrentPropertyValue 
        ///  
        /// 
        internal static object GetCurrentPropertyValue( 
            AnimationStorage storage,
            DependencyObject d,
            DependencyProperty dp,
            PropertyMetadata metadata, 
            object baseValue)
        { 
            Debug.Assert(storage != null, 
                "The 'storage' parameter cannot be passed into the GetCurrentPropertyValue method as null.");
 
            // If changes have been made to the snapshot value since the last tick
            // that value represents the current value of the property until the
            // next tick when the flag will be cleared. We will only take one
            // snapshot value between ticks. 
            //
            // Since CurrentTimeInvaliated is raised before CurrentStateInvalidated 
            // we need to check the state of the first clock as well to avoid 
            // potential first frame issues. In this case _hasStickySnapshotValue
            // will be updated to false shortly. 
            if (   storage._hasStickySnapshotValue
                && storage._animationClocks[0].CurrentState == ClockState.Stopped)
            {
                return storage._snapshotValue; 
            }
 
            if (   storage._animationClocks == null 
                && storage._propertyTriggerLayers == null)
            { 
                Debug.Assert(storage._snapshotValue != DependencyProperty.UnsetValue);

                return storage._snapshotValue;
            } 

            object currentPropertyValue = baseValue; 
 
            if (currentPropertyValue == DependencyProperty.UnsetValue)
            { 
                currentPropertyValue = metadata.GetDefaultValue(d, dp);
            }

            Debug.Assert(currentPropertyValue != DependencyProperty.UnsetValue); 

            // 
            // Process property trigger animations. 
            //
 
            if (storage._propertyTriggerLayers != null)
            {
                int count = storage._propertyTriggerLayers.Count;
 
                Debug.Assert(count > 0);
 
                IList layers = storage._propertyTriggerLayers.Values; 

                for (int i = 0; i < count; i++) 
                {
                    currentPropertyValue = layers[i].GetCurrentValue(currentPropertyValue);
                }
            } 

            // 
            // Process local animations 
            //
 
            if (storage._animationClocks != null)
            {
                FrugalObjectList clocks = storage._animationClocks;
                int clocksCount = clocks.Count; 
                bool hasActiveOrFillingClock = false;
 
                // default destination value will be the current property value 
                // calculated by the previous layer.
                object defaultDestinationValue = currentPropertyValue; 
                object currentLayerValue = currentPropertyValue;

                // if we have a snapshot value, then that will be the new
                // initial current property value. 
                if (storage._snapshotValue != DependencyProperty.UnsetValue)
                { 
                    currentLayerValue = storage._snapshotValue; 
                }
 
                Debug.Assert(clocksCount > 0);
                Debug.Assert(defaultDestinationValue != DependencyProperty.UnsetValue);

                for (int i = 0; i < clocksCount; i++) 
                {
                    if (clocks[i].CurrentState != ClockState.Stopped) 
                    { 
                        hasActiveOrFillingClock = true;
 
                        currentLayerValue = clocks[i].GetCurrentValue(currentLayerValue, defaultDestinationValue);

                        // An animation may not return DependencyProperty.UnsetValue as its
                        // current value. 
                        if (currentLayerValue == DependencyProperty.UnsetValue)
                        { 
                            throw new InvalidOperationException(SR.Get( 
                                SRID.Animation_ReturnedUnsetValueInstance,
                                clocks[i].Timeline.GetType().FullName, 
                                dp.Name,
                                d.GetType().FullName));
                        }
                    } 
                }
 
                // The currentLayerValue only applies when there is at least one 
                // active or filling clock.
                if (hasActiveOrFillingClock) 
                {
                    currentPropertyValue = currentLayerValue;
                }
            } 

            // We have a calculated currentPropertyValue, so validate it and 
            // then return it. 
            if (dp.IsValidValue(currentPropertyValue))
            { 
                return currentPropertyValue;
            }
            else
            { 
                // If the animation(s) applied to the property have calculated an
                // invalid value for the property then raise an exception. 
                throw new InvalidOperationException( 
                    SR.Get(
                        SRID.Animation_CalculatedValueIsInvalidForProperty, 
                        dp.Name,
                        (currentPropertyValue == null ? "null" : currentPropertyValue.ToString())));
            }
        } 

        [FriendAccessAllowed] // Built into Core, also used by Framework. 
        internal static bool IsPropertyAnimatable( 
            DependencyObject d,
            DependencyProperty dp) 
        {
            //

 

            if (dp.PropertyType != typeof(Visual3DCollection) && dp.ReadOnly) 
            { 
                return false;
            } 

            UIPropertyMetadata uiMetadata = dp.GetMetadata(d.DependencyObjectType) as UIPropertyMetadata;

            if (   uiMetadata != null 
                && uiMetadata.IsAnimationProhibited)
            { 
                return false; 
            }
 
            return true;
        }

        internal static bool IsAnimationValid( 
            DependencyProperty dp,
            AnimationTimeline animation) 
        { 
            return dp.PropertyType.IsAssignableFrom(animation.TargetPropertyType)
                || (animation.TargetPropertyType == typeof(Object)); 
        }

        [FriendAccessAllowed] // Built into Core, also used by Framework.
        internal static bool IsAnimationClockValid( 
            DependencyProperty dp,
            AnimationClock animation) 
        { 
            return IsAnimationValid(dp, (AnimationTimeline)animation.Timeline);
        } 

        /// 
        /// Returns the list of animated DependencyProperties for a
        /// given DependencyObject. 
        /// 
        ///  
        /// The DependencyObject. 
        /// 
        ///  
        /// The list of animated DependencyProperties if any are animated;
        /// otherwise null.
        /// 
        internal static FrugalMap GetAnimatedPropertiesMap(DependencyObject d) 
        {
            return AnimatedPropertyMapField.GetValue(d); 
        } 

        ///  
        /// Returns the AnimationStorage associated with a given DependencyProperty
        /// on a give DependencyObject.
        /// 
        /// The DependencyObject. 
        /// The DependencyProperty.
        ///  
        /// The AnimationStorage associated with the DependencyProperty if it 
        /// has any; otherwise null.
        ///  
        internal static AnimationStorage GetStorage(DependencyObject d, DependencyProperty dp)
        {
            Debug.Assert(   AnimatedPropertyMapField.GetValue(d)[dp.GlobalIndex] == DependencyProperty.UnsetValue
                         || AnimatedPropertyMapField.GetValue(d)[dp.GlobalIndex] is AnimationStorage); 

            return AnimatedPropertyMapField.GetValue(d)[dp.GlobalIndex] as AnimationStorage; 
        } 

        private static AnimationStorage CreateStorage( 
             DependencyObject d,
             DependencyProperty dp)
        {
            AnimationStorage newStorage; 

            if (dp.GetMetadata(d.DependencyObjectType) is IndependentlyAnimatedPropertyMetadata) 
            { 
                newStorage = CreateIndependentAnimationStorageForType(dp.PropertyType);
            } 
            else
            {
                newStorage = new AnimationStorage();
            } 

            newStorage.Initialize(d, dp); 
 
            return newStorage;
        } 

        private static IndependentAnimationStorage CreateIndependentAnimationStorageForType(Type type)
        {
            if (type == typeof(Double)) 
            {
                return new DoubleIndependentAnimationStorage(); 
            } 
            else if (type == typeof(Color))
            { 
                return new ColorIndependentAnimationStorage();
            }
            else if (type == typeof(Matrix))
            { 
                return new MatrixIndependentAnimationStorage();
            } 
            else if (type == typeof(Point3D)) 
            {
                return new Point3DIndependentAnimationStorage(); 
            }
            else if (type == typeof(Point))
            {
                return new PointIndependentAnimationStorage(); 
            }
            else if (type == typeof(Quaternion)) 
            { 
                return new QuaternionIndependentAnimationStorage();
            } 
            else if (type == typeof(Rect))
            {
                return new RectIndependentAnimationStorage();
            } 
            else if (type == typeof(Size))
            { 
                return new SizeIndependentAnimationStorage(); 
            }
            else 
            {
                Debug.Assert(type == typeof(Vector3D), "Application is trying to create independent animation storage for an unsupported type.");

                return new Vector3DIndependentAnimationStorage(); 
            }
        } 
 
        #endregion
 
        #region Data

        protected WeakReference _dependencyObject;
        protected DependencyProperty _dependencyProperty; 
        protected FrugalObjectList _animationClocks;
        private SortedList _propertyTriggerLayers; 
 
        private EventHandler _currentTimeInvalidatedHandler;
        private EventHandler _removeRequestedHandler; 
        private object _snapshotValue = DependencyProperty.UnsetValue;
        private bool _hasStickySnapshotValue;

        // This can be used by Animatables to store a local value if 
        // they provide a WriteLocalValueOverride but need to spill
        // the local value if they are animated because they also use 
        // the local value storage as a current value cache. 
        internal object _baseValue = DependencyProperty.UnsetValue;
 
        #endregion
    }
}

// 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