Esempio n. 1
0
    private static string AccessorName( PropertyPath path, int index )
    {
        object propertyAccessor = path.GetAccessor(index);

        if( propertyAccessor is DependencyProperty )
        {
            return ((DependencyProperty)propertyAccessor).Name;
        }
        else if( propertyAccessor is PropertyInfo )
        {
            return ((PropertyInfo)propertyAccessor).Name;
        }
        else if( propertyAccessor is PropertyDescriptor )
        {
            return ((PropertyDescriptor)propertyAccessor).Name;
        }
        else
        {
            return "[Unknown]";
        }
    }
Esempio n. 2
0
    /// <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 );
    }
Esempio n. 3
0
    /// <summary>
    ///     Function that checks to see if a given PropertyPath (already given
    /// the context object) can be used.
    /// </summary>

    // The rules currently in effect:
    // * The last object in the path must be a DependencyObject
    // * The last property (on that last object) must be a DependencyProperty
    // * Any of these objects may be Freezable objects.  There are two cases for
    //   this.
    //   1) The value of the first property is Frozen.  We might be able to
    //      handle this via the cloning mechanism, so we don't check Frozen-ness
    //      if we see the first property is Frozen.  Whether the cloning
    //      mechanism can be used is verified elsewhere.
    //   2) The value of the first property is not Frozen, or is not a Freezable
    //      at all.  In this case, the cloning code path does not apply, and
    //      thus we must not have any immutable Freezable objects any further
    //      down the line.

    // Another rule not enforced here:
    // * If cloning is required, the first property value must be a Freezable,
    //   which knows how to clone itself.  However, this is only needed in
    //   cases of complex property paths and is checked elsewhere.

    // Things we don't care about:
    // * Whether or not any of the intermediate objects are DependencyObject or
    //   not - this is supposed to work no matter the object type.
    // * Whether or not any of the intermediate properties are DP or not - this
    //   is supposed to work whether it's a CLR property or DependencyProperty.
    // * Whether or not any of the intermediate properties are animatable or not.
    //   Even though they are changing, we're not attaching an animation to clock
    //   to those properties specifically.
    // * By the same token, we don't care if any of them are marked Read-Only.

    // Note that this means: If the intention is to make something fixed, it is
    //  not sufficient to mark an intermediate property read-only and
    //  not-animatable.  In fact, in the current design, it is impossible to
    //  be 100% sure that something will stay put.
    internal static void VerifyPathIsAnimatable(PropertyPath path)
    {
        object    intermediateObject = null;
        object    intermediateProperty = null; // Might be DependencyProperty, PropertyInfo, or PropertyDescriptor
        bool      checkingFrozenState = true;
        Freezable intermediateFreezable = null;

        for( int i=0; i < path.Length; i++ )
        {
            intermediateObject = path.GetItem(i);
            intermediateProperty = path.GetAccessor(i);

            if( intermediateObject == null )
            {
                Debug.Assert( i > 0, "The caller should not have set the PropertyPath context to a null object." );
                throw new InvalidOperationException(SR.Get(SRID.Storyboard_PropertyPathObjectNotFound, AccessorName(path, i-1), path.Path ));
            }

            if( intermediateProperty == null )
            {
                // Would love to throw error with the name of the property we couldn't find,
                //  but that information is not exposed from the PropertyPath class.
                throw new InvalidOperationException(SR.Get(SRID.Storyboard_PropertyPathPropertyNotFound, path.Path ));
            }

            // If the first property value is an immutable Freezable, then turn
            //  off the Frozen state checking - let's hope we can use the cloning
            //  mechanism for that case.
            // Index of zero is the path context object itself, one (that we're
            //  checking here) is the value of the first property.
            // Example: Property path "Background.Opacity" as applied to Button.
            //  Object 0 is the Button, object 1 is the brush.
            if( i == 1 )
            {
                intermediateFreezable = intermediateObject as Freezable;
                if( intermediateFreezable != null && intermediateFreezable.IsFrozen )
                {
                    checkingFrozenState = false;
                }
            }
            // Freezable objects (other than the one returned as the value of
            //  the first property) must not be frozen if the first one isn't.
            else if( checkingFrozenState )
            {
                intermediateFreezable = intermediateObject as Freezable;
                if( intermediateFreezable != null && intermediateFreezable.IsFrozen )
                {
                    if( i > 0 )
                    {
                        throw new InvalidOperationException(SR.Get(SRID.Storyboard_PropertyPathFrozenCheckFailed, AccessorName(path, i-1), path.Path, intermediateFreezable.GetType().ToString() ));
                    }
                    else
                    {
                        // i == 0 means the targeted object itself is a frozen Freezable.
                        //  This need a different error message.
                        throw new InvalidOperationException(SR.Get(SRID.Storyboard_ImmutableTargetNotSupported, path.Path));
                    }
                }
            }

            // The last object + property pairing (the one we're actually going
            //  to stick the clock on) has further requirements.
            if( i == path.Length-1 )
            {
                DependencyObject intermediateDO = intermediateObject as DependencyObject;
                DependencyProperty intermediateDP = intermediateProperty as DependencyProperty;

                if( intermediateDO == null )
                {
                    Debug.Assert( i > 0, "The caller should not have set the PropertyPath context to a non DependencyObject." );
                    throw new InvalidOperationException(SR.Get(SRID.Storyboard_PropertyPathMustPointToDependencyObject, AccessorName(path, i-1), path.Path));
                }

                if( intermediateDP == null )
                {
                    throw new InvalidOperationException(SR.Get(SRID.Storyboard_PropertyPathMustPointToDependencyProperty, path.Path ));
                }

                if( checkingFrozenState && intermediateDO.IsSealed )
                {
                    throw new InvalidOperationException(SR.Get(SRID.Storyboard_PropertyPathSealedCheckFailed, intermediateDP.Name, path.Path, intermediateDO));
                }

                if(!AnimationStorage.IsPropertyAnimatable(intermediateDO, intermediateDP) )
                {
                    throw new InvalidOperationException(SR.Get(SRID.Storyboard_PropertyPathIncludesNonAnimatableProperty, path.Path, intermediateDP.Name));
                }
            }
        }
    }