/// <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 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(); }
[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(); }