/// <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); }
internal AnimationLayer(AnimationStorage ownerStorage) { Debug.Assert(ownerStorage != null); _ownerStorage = ownerStorage; _removeRequestedHandler = new EventHandler(OnRemoveRequested); }
/// <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); } } }
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(); } }
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); }
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(); }
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); } } } }
[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; }