public void BlockOpposite(object value, IPropertySerializationInfo property) { if (property == null || property.Opposite == null) { return; } var pair = new ObjectPropertyPair() { Object = value, Property = property.Opposite }; blockedProperties.Add(pair); }
public bool IsOppositeSet(object instance, IPropertySerializationInfo property) { if (property == null || property.Opposite == null) { return(false); } var pair = new ObjectPropertyPair() { Object = instance, Property = property }; return(blockedProperties.Contains(pair)); }
/// <summary> /// For complex property paths, we need to dig our way down to the /// property and attach the animation clock there. We will not be able to /// actually attach the clocks if the targetProperty points to a frozen /// Freezable. More extensive handling will be required for that case. /// </summary> private void ProcessComplexPath( HybridDictionary clockMappings, DependencyObject targetObject, PropertyPath path, AnimationClock animationClock, HandoffBehavior handoffBehavior, Int64 layer ) { Debug.Assert(path.Length > 1, "This method shouldn't even be called for a simple property path."); // For complex paths, the target object/property differs from the actual // animated object/property. // // Example: // TargetName="Rect1" TargetProperty="(Rectangle.LayoutTransform).(RotateTransform.Angle)" // // The target object is a Rectangle. // The target property is LayoutTransform. // The animated object is a RotateTransform // The animated property is Angle. // Currently unsolved problem: If the LayoutTransform is not a RotateTransform, // we have no way of knowing. We'll merrily set up to animate the Angle // property as an attached property, not knowing that the value will be // completely ignored. DependencyProperty targetProperty = path.GetAccessor(0) as DependencyProperty; // Two different ways to deal with property paths. If the target is // on a frozen Freezable, we'll have to make a clone of the value and // attach the animation on the clone instead. // For all other objects, we attach the animation clock directly on the // specified animating object and property. object targetPropertyValue = targetObject.GetValue(targetProperty); DependencyObject animatedObject = path.LastItem as DependencyObject; DependencyProperty animatedProperty = path.LastAccessor as DependencyProperty; if( animatedObject == null || animatedProperty == null || targetProperty == null ) { throw new InvalidOperationException(SR.Get(SRID.Storyboard_PropertyPathUnresolved, path.Path)); } VerifyAnimationIsValid(animatedProperty, animationClock); if( PropertyCloningRequired( targetPropertyValue ) ) { // Verify that property paths are supported for the specified // object and property. If the property value query (usually in // GetValueCore) doesn't call into Storyboard code, then none of this // will have any effect. (Silently do nothing.) // Throwing here is for user's sake to alert that nothing will happen. VerifyComplexPathSupport( targetObject ); // We need to clone the value of the target, and from here onwards // try to pretend that it is the actual value. Debug.Assert(targetPropertyValue is Freezable, "We shouldn't be trying to clone a type we don't understand. PropertyCloningRequired() has improperly flagged the current value as 'need to clone'."); // To enable animations on frozen Freezable objects, complex // path processing is done on a clone of the value. Freezable clone = ((Freezable)targetPropertyValue).Clone(); SetComplexPathClone( targetObject, targetProperty, targetPropertyValue, clone ); // Promote the clone to the EffectiveValues cache targetObject.InvalidateProperty(targetProperty); // We're supposed to have the animatable clone in place by now. But if // things went sour for whatever reason, halt the app instead of corrupting // the frozen object. if( targetObject.GetValue(targetProperty) != clone ) { throw new InvalidOperationException(SR.Get(SRID.Storyboard_ImmutableTargetNotSupported, path.Path)); } // Now that we have a clone, update the animatedObject and animatedProperty // with references to those on the clone. using(path.SetContext(targetObject)) { animatedObject = path.LastItem as DependencyObject; animatedProperty = path.LastAccessor as DependencyProperty; } // And set up to listen to changes on this clone. ChangeListener.ListenToChangesOnFreezable( targetObject, clone, targetProperty, (Freezable)targetPropertyValue ); } // Apply animation clock on the animated object/animated property. ObjectPropertyPair directApplyTarget = new ObjectPropertyPair( animatedObject, animatedProperty ); UpdateMappings( clockMappings, directApplyTarget, animationClock ); }
/// <summary> /// Given an animation clock, add it to the data structure which tracks /// all the clocks along with their associated target object and property. /// </summary> private static void UpdateMappings( HybridDictionary clockMappings, ObjectPropertyPair mappingKey, AnimationClock animationClock) { object mappedObject = clockMappings[mappingKey]; Debug.Assert( mappedObject == null || mappedObject is AnimationClock || mappedObject is List<AnimationClock>, "Internal error - clockMappings table contains an unexpected object " + ((mappedObject == null ) ? "" : mappedObject.GetType().ToString()) ); if( mappedObject == null ) { // No clock currently in storage, put this clock in that slot. clockMappings[mappingKey] = animationClock; } else if( mappedObject is AnimationClock ) { // One clock currently in storage, up-convert to list and replace in slot. List<AnimationClock> clockList = new List<AnimationClock>(); clockList.Add((AnimationClock)mappedObject); clockList.Add(animationClock); clockMappings[mappingKey] = clockList; } else // mappedObject is List<AnimationClock> { // Add to existing list in storage. List<AnimationClock> clockList = (List<AnimationClock>)mappedObject; clockList.Add(animationClock); } return; }
/// <summary> /// Recursively walks the clock tree and determine the target object /// and property for each clock in the tree. /// </summary> /// <remarks> /// The currently active object and property path are passed in as parameters, /// they will be used unless a target/property specification exists on /// the Timeline object corresponding to the current clock. (So that the /// leaf-most reference wins.) /// /// The active object and property parameters may be null if they have /// never been specified. If we reach a leaf node clock and a needed attribute /// is still null, it is an error condition. Otherwise we keep hoping they'll be found. /// </remarks> private void ClockTreeWalkRecursive( Clock currentClock, /* No two calls will have the same currentClock */ DependencyObject containingObject, /* Remains the same through all the recursive calls */ INameScope nameScope, /* Remains the same through all the recursive calls */ DependencyObject parentObject, string parentObjectName, PropertyPath parentPropertyPath, HandoffBehavior handoffBehavior, /* Remains the same through all the recursive calls */ HybridDictionary clockMappings, Int64 layer /* Remains the same through all the recursive calls */) { Timeline currentTimeline = currentClock.Timeline; DependencyObject targetObject = parentObject; string currentObjectName = parentObjectName; PropertyPath currentPropertyPath = parentPropertyPath; // If we have target object/property information, use it instead of the // parent's information. string nameString = (string)currentTimeline.GetValue(TargetNameProperty); if( nameString != null ) { if( nameScope is Style ) { // We are inside a Style - we don't let people target anything. // They're only allowed to modify the Styled object, which is // already the implicit target. throw new InvalidOperationException(SR.Get(SRID.Storyboard_TargetNameNotAllowedInStyle, nameString)); } currentObjectName = nameString; } // The TargetProperty trumps the TargetName property. DependencyObject localTargetObject = (DependencyObject) currentTimeline.GetValue(TargetProperty); if( localTargetObject != null ) { targetObject = localTargetObject; currentObjectName = null; } PropertyPath propertyPath = (PropertyPath)currentTimeline.GetValue(TargetPropertyProperty); if( propertyPath != null ) { currentPropertyPath = propertyPath; } // Now see if the current clock is an animation clock if( currentClock is AnimationClock ) { DependencyProperty targetProperty = null; AnimationClock animationClock = (AnimationClock)currentClock; if( targetObject == null ) { // Resolve the target object name. If no name specified, use the // containing object. if( currentObjectName != null ) { DependencyObject mentor = Helper.FindMentor(containingObject); targetObject = ResolveTargetName(currentObjectName, nameScope, mentor); } else { // The containing object must be either an FE or FCE. // (Not a Storyboard, as used for "shared clocks" mode.) targetObject = containingObject as FrameworkElement; if(targetObject == null) { targetObject = containingObject as FrameworkContentElement; } if( targetObject == null ) { // The containing object is not an FE or FCE. throw new InvalidOperationException(SR.Get(SRID.Storyboard_NoTarget, currentTimeline.GetType().ToString() )); } } } // See if we have a property name to use. if( currentPropertyPath == null ) { throw new InvalidOperationException(SR.Get(SRID.Storyboard_TargetPropertyRequired, currentTimeline.GetType().ToString() )); } // A property name can be a straightforward property name (like "Angle") // but may be a more complex multi-step property path. The two cases // are handled differently. using(currentPropertyPath.SetContext(targetObject)) { if( currentPropertyPath.Length < 1 ) { throw new InvalidOperationException(SR.Get(SRID.Storyboard_PropertyPathEmpty)); } VerifyPathIsAnimatable(currentPropertyPath); if( currentPropertyPath.Length == 1 ) { // We have a simple single-step property. targetProperty = currentPropertyPath.GetAccessor(0) as DependencyProperty; if( targetProperty == null ) { // Unfortunately it's not a DependencyProperty. throw new InvalidOperationException(SR.Get(SRID.Storyboard_PropertyPathMustPointToDependencyProperty, currentPropertyPath.Path )); } VerifyAnimationIsValid(targetProperty, animationClock); ObjectPropertyPair animatedTarget = new ObjectPropertyPair(targetObject, targetProperty); UpdateMappings(clockMappings, animatedTarget, animationClock); } else // path.Length > 1 { // This is a multi-step property path that requires more extensive // setup. ProcessComplexPath(clockMappings, targetObject, currentPropertyPath, animationClock, handoffBehavior, layer); } } } else if ( currentClock is MediaClock ) // Not an animation clock - maybe a media clock? { // Yes it's a media clock. Try to find the corresponding object and // apply the clock to that object. ApplyMediaClock(nameScope, containingObject, targetObject, currentObjectName, (MediaClock) currentClock); } else { // None of the types we recognize as leaf node clock types - // recursively process child clocks. ClockGroup currentClockGroup = currentClock as ClockGroup; if (currentClockGroup != null) { ClockCollection childrenClocks = currentClockGroup.Children; for( int i = 0; i < childrenClocks.Count; i++ ) { ClockTreeWalkRecursive( childrenClocks[i], containingObject, nameScope, targetObject, currentObjectName, currentPropertyPath, handoffBehavior, clockMappings, layer); } } } }
public bool Equals(ObjectPropertyPair key) { return (_object.Equals(key._object) && (_property == key._property)); }