Example #1
0
        /// <summary>
        /// Starts an animation for a DependencyProperty. The animation will
        /// begin when the next frame is rendered.
        /// </summary>
        /// <param name="dp">
        /// The DependencyProperty to animate.
        /// </param>
        /// <param name="animation">
        /// <para>The AnimationTimeline to used to animate the property.</para>
        /// <para>If the AnimationTimeline's BeginTime is null, any current animations
        /// will be removed and the current value of the property will be held.</para>
        /// <para>If this value is null, all animations will be removed from the property
        /// and the property value will revert back to its base value.</para>
        /// </param>
        /// <param name="handoffBehavior">
        /// Specifies how the new animation should interact with any current
        /// animations already affecting the property value.
        /// </param>
        public void BeginAnimation(DependencyProperty dp, AnimationTimeline animation, HandoffBehavior handoffBehavior)
        {
            if (dp == null)
            {
                throw new ArgumentNullException("dp");
            }

            if (!AnimationStorage.IsPropertyAnimatable(this, dp))
            {
        #pragma warning disable 56506 // Suppress presharp warning: Parameter 'dp' to this public method must be validated:  A null-dereference can occur here.
                throw new ArgumentException(SR.Get(SRID.Animation_DependencyPropertyIsNotAnimatable, dp.Name, this.GetType()), "dp");
        #pragma warning restore 56506
            }

            if (animation != null &&
                !AnimationStorage.IsAnimationValid(dp, animation))
            {
                throw new ArgumentException(SR.Get(SRID.Animation_AnimationTimelineTypeMismatch, animation.GetType(), dp.Name, dp.PropertyType), "animation");
            }

            if (!HandoffBehaviorEnum.IsDefined(handoffBehavior))
            {
                throw new ArgumentException(SR.Get(SRID.Animation_UnrecognizedHandoffBehavior));
            }

            if (IsSealed)
            {
                throw new InvalidOperationException(SR.Get(SRID.IAnimatable_CantAnimateSealedDO, dp, this.GetType()));
            }

            AnimationStorage.BeginAnimation(this, dp, animation, handoffBehavior);
        }
Example #2
0
        internal AnimationLayer(AnimationStorage ownerStorage)
        {
            Debug.Assert(ownerStorage != null);

            _ownerStorage           = ownerStorage;
            _removeRequestedHandler = new EventHandler(OnRemoveRequested);
        }
Example #3
0
        internal AnimationLayer(AnimationStorage ownerStorage)
        {
            Debug.Assert(ownerStorage != null);

            _ownerStorage = ownerStorage;
            _removeRequestedHandler = new EventHandler(OnRemoveRequested);
        }
Example #4
0
        /// <summary>
        ///     Allows subclasses to participate in property animated value computation
        /// </summary>
        /// <param name="dp"></param>
        /// <param name="metadata"></param>
        /// <param name="entry">EffectiveValueEntry computed by base</param>
        internal sealed override void EvaluateAnimatedValueCore(
            DependencyProperty dp,
            PropertyMetadata metadata,
            ref EffectiveValueEntry entry)
        {
            if (IAnimatable_HasAnimatedProperties)
            {
                AnimationStorage storage = AnimationStorage.GetStorage(this, dp);

                if (storage != null)
                {
                    storage.EvaluateAnimatedValue(metadata, ref entry);
                }
            }
        }
Example #5
0
        internal void PropertyChanged(DependencyProperty dp)
        {
            AnimationStorage            animationStorage            = AnimationStorage.GetStorage(this, dp);
            IndependentAnimationStorage independentAnimationStorage = animationStorage as IndependentAnimationStorage;

            // If this property is independently animated and currently has
            // animations all we need to do is update the animation resource
            // that represents this property value. Otherwise we need to invalidate
            // and and eventually update this whole object.
            if (independentAnimationStorage != null)
            {
                independentAnimationStorage.InvalidateResource();
            }
            else
            {
                RegisterForAsyncUpdateResource();
            }
        }
Example #6
0
        internal static DUCE.ResourceHandle GetResourceHandle(DependencyObject d, DependencyProperty dp, DUCE.Channel channel)
        {
            Debug.Assert(d != null);
            Debug.Assert(dp != null);
            Debug.Assert(d is Animatable ? ((Animatable)d).HasAnimatedProperties : true);

            IndependentAnimationStorage storage = AnimationStorage.GetStorage(d, dp) as IndependentAnimationStorage;

            if (storage == null)
            {
                return(DUCE.ResourceHandle.Null);
            }
            else
            {
                Debug.Assert(storage._duceResource.IsOnChannel(channel));

                return(((DUCE.IResource)storage).GetHandle(channel));
            }
        }
        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);
        }
Example #8
0
        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 >= 0,
                         "An AnimationClock no longer associated with a property should not have a RemoveRequested event handler.");

            if (_hasStickySnapshotValue &&
                index == 0)
            {
                _animationClocks[0].CurrentStateInvalidated -= new EventHandler(OnCurrentStateInvalidated);
                _hasStickySnapshotValue = false;
            }

            _animationClocks.RemoveAt(index);

            _ownerStorage.DetachAnimationClock(animationClock, _removeRequestedHandler);

            AnimationStorage tmpOwnerStorage = _ownerStorage;

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

            // _ownerStorage may be null here.

            tmpOwnerStorage.WritePostscript();
        }
Example #9
0
        internal virtual void ReleaseOnChannelAnimations(DUCE.Channel channel)
        {
            if (IAnimatable_HasAnimatedProperties)
            {
                FrugalMap animatedPropertiesMap = AnimationStorage.GetAnimatedPropertiesMap(this);

                Debug.Assert(animatedPropertiesMap.Count > 0);

                for (int i = 0; i < animatedPropertiesMap.Count; i++)
                {
                    Int32  dpGlobalIndex;
                    Object storageObject;

                    animatedPropertiesMap.GetKeyValuePair(i, out dpGlobalIndex, out storageObject);

                    DUCE.IResource storage = storageObject as DUCE.IResource;

                    if (storage != null)
                    {
                        storage.ReleaseOnChannel(channel);
                    }
                }
            }
        }
Example #10
0
        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 >= 0,
                "An AnimationClock no longer associated with a property should not have a RemoveRequested event handler.");

            if (_hasStickySnapshotValue
                && index == 0)
            {
                _animationClocks[0].CurrentStateInvalidated -= new EventHandler(OnCurrentStateInvalidated);
                _hasStickySnapshotValue = false;
            }

            _animationClocks.RemoveAt(index);

            _ownerStorage.DetachAnimationClock(animationClock, _removeRequestedHandler);

            AnimationStorage tmpOwnerStorage = _ownerStorage;

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

            // _ownerStorage may be null here.

            tmpOwnerStorage.WritePostscript();
        }
        [FriendAccessAllowed] // Built into Core, also used by Framework.
        internal static void ApplyAnimationClocks(
            DependencyObject d,
            DependencyProperty dp,
            IList <AnimationClock> 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 <AnimationClock>(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 <AnimationClock> newClockCollection = new FrugalObjectList <AnimationClock>(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();
        }
        internal static void ApplyAnimationClocksToLayer(
            DependencyObject d,
            DependencyProperty dp,
            IList <AnimationClock> 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 <Int64, AnimationLayer> propertyTriggerLayers = storage._propertyTriggerLayers;

            if (propertyTriggerLayers == null)
            {
                propertyTriggerLayers          = new SortedList <Int64, AnimationLayer>(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 <Int64> 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();
        }
        /// <summary>
        /// GetCurrentPropertyValue
        /// </summary>
        /// <returns></returns>
        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 <AnimationLayer> layers = storage._propertyTriggerLayers.Values;

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

            //
            // Process local animations
            //

            if (storage._animationClocks != null)
            {
                FrugalObjectList <AnimationClock> 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 return it if the type matches.
            if (DependencyProperty.IsValidType(currentPropertyValue, dp.PropertyType))
            {
                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())));
            }
        }
        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;
                        }
                    }
                    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);

                    if (_dependencyProperty.IsValidValueInternal(animatedValue))
                    {
                        // 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 */,
                            false /* coerceWithCurrentValue */,
                            OperationType.Unknown);

                        if (_hadValidationError)
                        {
                            if (TraceAnimation.IsEnabled)
                            {
                                TraceAnimation.TraceActivityItem(
                                    TraceAnimation.AnimateStorageValidationNoLongerFailing,
                                    this,
                                    animatedValue,
                                    target,
                                    _dependencyProperty);

                                _hadValidationError = false;
                            }
                        }
                    }
                    else if (!_hadValidationError)
                    {
                        if (TraceAnimation.IsEnabled)
                        {
                            TraceAnimation.TraceActivityItem(
                                TraceAnimation.AnimateStorageValidationFailed,
                                this,
                                animatedValue,
                                target,
                                _dependencyProperty);
                        }

                        _hadValidationError = true;
                    }
                }
                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);
                }
            }
        }
        /// <summary>
        /// GetCurrentPropertyValue
        /// </summary>
        /// <returns></returns>
        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<AnimationLayer> layers = storage._propertyTriggerLayers.Values;

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

            //
            // Process local animations
            //

            if (storage._animationClocks != null)
            {
                FrugalObjectList<AnimationClock> 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 return it if the type matches.
            if (DependencyProperty.IsValidType(currentPropertyValue, dp.PropertyType))
            {
                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())));
            }
        }
        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;
        }