/// <summary> /// 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. /// </summary> 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); }