コード例 #1
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));
                }
            }
        }
    }