DependencyObjectPropertyDescriptor.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Base / MS / Internal / ComponentModel / DependencyObjectPropertyDescriptor.cs / 1 / DependencyObjectPropertyDescriptor.cs

                             
namespace MS.Internal.ComponentModel
{
    using System;
    using System.Collections; 
    using System.Collections.Generic;
    using System.ComponentModel; 
    using System.Diagnostics; 
    using System.Globalization;
    using System.Reflection; 
    using System.Security.Permissions;
    using System.Windows;
    using System.Security;
 
    /// 
    ///     An inplementation of a property descriptor for DependencyProperties. 
    ///     This supports both normal and attached properties. 
    /// 
    internal sealed class DependencyObjectPropertyDescriptor : PropertyDescriptor { 

        //------------------------------------------------------
        //
        //  Constructors 
        //
        //----------------------------------------------------- 
 
        #region Constructors
 
        /// 
        ///     Creates a new dependency property descriptor.  A note on perf:  We don't
        ///     pass the property descriptor down as the default member descriptor here.  Doing
        ///     so takes the attributes off of the property descriptor, which can be costly if they 
        ///     haven't been accessed yet.  Instead, we wait until someone needs to access our
        ///     Attributes property and demand create the attributes at that time. 
        ///  
        internal DependencyObjectPropertyDescriptor(PropertyDescriptor property, DependencyProperty dp, Type objectType)
            : base(dp.Name, null) 
        {
            _property = property;
            _dp = dp;
 
            Debug.Assert(property != null && dp != null);
            Debug.Assert(!(property is DependencyObjectPropertyDescriptor), "Wrapping a DP in a DP"); 
 
            _componentType = property.ComponentType;
            _metadata = _dp.GetMetadata(objectType); 
        }

        /// 
        ///     Creates a new dependency property descriptor.  A note on perf:  We don't 
        ///     pass the property descriptor down as the default member descriptor here.  Doing
        ///     so takes the attributes off of the property descriptor, which can be costly if they 
        ///     haven't been accessed yet.  Instead, we wait until someone needs to access our 
        ///     Attributes property and demand create the attributes at that time.
        ///  
        internal DependencyObjectPropertyDescriptor(DependencyProperty dp, Type ownerType)
            : base(string.Concat(dp.OwnerType.Name, ".", dp.Name), null)
        {
            _dp = dp; 
            _componentType = ownerType;
            _metadata = _dp.GetMetadata(ownerType); 
        } 

        #endregion Constructors 


        //-----------------------------------------------------
        // 
        //  Public Methods
        // 
        //----------------------------------------------------- 

        #region Public Methods 

        /// 
        ///     Indicates if this property's value can be reset.
        ///     We map this to true if there is a local value 
        ///     on the DP.
        ///  
        public override bool CanResetValue(object component) 
        {
            // All DPs that have values set can be reset 
            return ShouldSerializeValue(component);
        }

        ///  
        ///     Returns the value for this property.
        ///  
        public override object GetValue(object component) 
        {
            DependencyObject DO = FromObj(component); 
            return DO.GetValue(_dp);
        }

        ///  
        ///     Attempts to reset (or clear) the value in the
        ///     DP. 
        ///  
        public override void ResetValue(object component)
        { 
            if (!_queriedResetMethod)
            {
                _resetMethod = GetSpecialMethod("Reset");
                _queriedResetMethod = true; 
            }
 
            DependencyObject DO = FromObj(component); 

            if (_resetMethod != null) 
            {
                // See if we need to pass parameters to this method.  When
                // _property == null, this is an attached property and
                // the method is static.  When _property != null, this 
                // is a direct property and the method is instanced.
 
                if (_property == null) 
                {
                    _resetMethod.Invoke(null, new object[] {DO}); 
                }
                else
                {
                    _resetMethod.Invoke(DO, null); 
                }
            } 
            else 
            {
                DO.ClearValue(_dp); 
            }
        }

        ///  
        ///     Sets the property value on the given object.
        ///  
        public override void SetValue(object component, object value) 
        {
            DependencyObject DO = FromObj(component); 
            DO.SetValue(_dp, value);
        }

        ///  
        ///     Returns true if the property contains
        ///     a local value that should be serialized. 
        ///  
        public override bool ShouldSerializeValue(object component)
        { 
            DependencyObject DO = FromObj(component);
            bool shouldSerialize = DO.ShouldSerializeProperty(_dp);

            // The of precedence is that a property should be serialized if the ShouldSerializeProperty 
            // method returns true and either ShouldSerializeXXX does not exist or it exists and returns true.
 
            if (shouldSerialize) 
            {
                // If we have a ShouldSerialize method, use it 

                if (!_queriedShouldSerializeMethod)
                {
                    MethodInfo method = GetSpecialMethod("ShouldSerialize"); 

                    if (method != null && method.ReturnType == typeof(bool)) 
                    { 
                        _shouldSerializeMethod = method;
                    } 
                    _queriedShouldSerializeMethod = true;
                }

                if (_shouldSerializeMethod != null) 
                {
                    // See if we need to pass parameters to this method.  When 
                    // _property == null, this is an attached property and 
                    // the method is static.  When _property != null, this
                    // is a direct property and the method is instanced. 

                    if (_property == null)
                    {
                        shouldSerialize = (bool)_shouldSerializeMethod.Invoke(null, new object[] {DO}); 
                    }
                    else 
                    { 
                        shouldSerialize = (bool)_shouldSerializeMethod.Invoke(DO, null);
                    } 
                }
            }

            return shouldSerialize; 
        }
 
        ///  
        ///     Adds a change event handler to this descriptor.
        ///  
        public override void AddValueChanged(object component, EventHandler handler)
        {
            //
 

            DependencyObject DO = FromObj(component); 
            if (_trackers == null) 
            {
                _trackers = new Dictionary(); 
            }

            PropertyChangeTracker tracker;
            if (!_trackers.TryGetValue(DO, out tracker)) 
            {
                tracker = new PropertyChangeTracker(DO, _dp); 
                _trackers.Add(DO, tracker); 
            }
 
            tracker.Changed += handler;
        }

        ///  
        ///     Removes a previously added change handler.
        ///  
        public override void RemoveValueChanged(object component, EventHandler handler) { 
            if (_trackers == null) return;
            PropertyChangeTracker tracker; 
            DependencyObject DO = FromObj(component);

            if (_trackers.TryGetValue(DO, out tracker))
            { 
                tracker.Changed -= handler;
                if (tracker.CanClose) 
                { 
                    tracker.Close();
                    _trackers.Remove(DO); 
                }
            }
        }
 

        #endregion Public Methods 
 
        //------------------------------------------------------
        // 
        //  Public Properties
        //
        //-----------------------------------------------------
 
        #region Public Properties
 
        ///  
        ///     Returns the collection of attributes associated with this property.
        ///  
        public override AttributeCollection Attributes
        {
            get
            { 
                // Windows OS Bugs 1383847.  The Attributes
                // property on PropertyDescriptor is not as thread 
                // safe as it should be.  There are instances when 
                // it can return null during contention.  Our fix is
                // to detect this case and lock.  Note that this isn't 
                // 100% because not all property descriptors are
                // DependencyObjectPropertyDescriptors.

                AttributeCollection attrs = base.Attributes; 
                if (attrs == null)
                { 
                    lock(_attributeSyncLock) 
                    {
                        attrs = base.Attributes; 
                        Debug.Assert(attrs != null);
                    }
                }
 
                return attrs;
            } 
        } 

        ///  
        ///     The type of object this property is describing.
        /// 
        public override Type ComponentType
        { 
            get { return _componentType; }
        } 
 
        /// 
        ///     Returns true if the DP is read only. 
        /// 
        public override bool IsReadOnly
        {
            get { 

                // It is a lot cheaper to get DP metadata 
                // than it is to calculate attributes.  While 
                // the attributes do factor in DP metadata, short
                // circuit for this common case. 
                bool readOnly = _dp.ReadOnly;
                if (!readOnly) {
                    readOnly = Attributes.Contains(ReadOnlyAttribute.Yes);
                } 
                return readOnly;
            } 
        } 

        ///  
        ///     The type of the property.
        /// 
        public override Type PropertyType
        { 
            get { return _dp.PropertyType; }
        } 
 
        /// 
        ///     Returns true if this property descriptor supports change 
        ///     notifications.  All dependency property descriptors do.
        /// 
        public override bool SupportsChangeEvents
        { 
            get { return true; }
        } 
 
        #endregion Public Properties
 

        //------------------------------------------------------
        //
        //  Internal Properties 
        //
        //------------------------------------------------------ 
 
        #region Internal Properties
 
        /// 
        ///     Returns the dependency property we're wrapping.
        /// 
        internal DependencyProperty DependencyProperty 
        {
            get { return _dp; } 
        } 

 
        internal bool IsAttached
        {
             get { return (_property == null); }
        } 

        internal PropertyMetadata Metadata 
        { 
            get { return _metadata; }
        } 

        #endregion Internal Properties

        //----------------------------------------------------- 
        //
        //  Internal Methods 
        // 
        //------------------------------------------------------
 
        #region Internal Methods

        /// 
        ///     This method is called when we should clear our cached state.  The cache 
        ///     may become invalid if someone adds additional type description providers.
        ///  
        internal static void ClearCache() 
        {
            lock (_getMethodCache) 
            {
                _getMethodCache.Clear();
            }
 
            lock(_setMethodCache)
            { 
                _setMethodCache.Clear(); 
            }
        } 

        /// 
        ///     A helper method that returns the static "Get" method for an attached
        ///     property.  The dpType parameter is the data type of the object you 
        ///     want to attach the property to.
        ///  
        internal static MethodInfo GetAttachedPropertyMethod(DependencyProperty dp) 
        {
            // Check the cache.  This property descriptor is cached by the 
            // dependency object provider, but there is a unique property descriptor
            // for each type an attached property can be attached to.  Therefore,
            // caching this method lookup for a DP makes sense.
 
            MethodInfo method;
 
            // TypeDescriptor offers a feature called a "reflection type", which 
            // is an indirection to another type we should reflect on.  Anywhere
            // we rely on raw reflection we should be using GetReflectionType. 
            // Also the returning type may change if someone added or removed
            // a provider, so we need to detect this in our cache invalidation
            // logic.
 
            Type reflectionType = TypeDescriptor.GetReflectionType(dp.OwnerType);
 
            object methodObj = _getMethodCache[dp]; 
            method = methodObj as MethodInfo;
 
            if (methodObj == null || (method != null && !object.ReferenceEquals(method.DeclaringType, reflectionType))) {
                BindingFlags f = BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly;
                string methodName = string.Concat("Get", dp.Name);
                method = reflectionType.GetMethod(methodName, f, _dpBinder, _dpType, null); 

                lock(_getMethodCache) { 
                    _getMethodCache[dp] = (method == null ? _nullMethodSentinel : method); 
                }
            } 

            return method;
        }
 
        #endregion Internal Methods
 
        //----------------------------------------------------- 
        //
        //  Protected Methods 
        //
        //-----------------------------------------------------

        #region Protected Methods 

        ///  
        ///     Overridden to lazily create our attributes. 
        /// 
        protected override AttributeCollection CreateAttributeCollection() 
        {
            MergeAttributes();
            return base.CreateAttributeCollection();
        } 

        #endregion Protected Methods 
 

        //----------------------------------------------------- 
        //
        //  Private Methods
        //
        //------------------------------------------------------ 

        #region Private Methods 
 
        /// 
        ///     Helper method that recovers a dependency object from a value. 
        /// 
        private static DependencyObject FromObj(object value)
        {
            // This indirection is necessary to support 
            // the "association" feature of type descriptor.  This feature
            // alows one object to mimic the API of another. 
            return (DependencyObject)TypeDescriptor.GetAssociation(typeof(DependencyObject), value); 
        }
 
        /// 
        ///     Additional metadata attributes for attached properties
        ///     are taken from the "Get" method.
        ///  
        private AttributeCollection GetAttachedPropertyAttributes()
        { 
            MethodInfo mi = GetAttachedPropertyMethod(_dp); 

            if (mi != null) 
            {
                Attribute[] attrArray = (Attribute[])mi.GetCustomAttributes(typeof(Attribute), true);

                Type propertyReflectionType = TypeDescriptor.GetReflectionType(_dp.PropertyType); 
                Attribute[] typeAttrArray = (Attribute[])propertyReflectionType.GetCustomAttributes(typeof(Attribute), true);
                if (typeAttrArray != null && typeAttrArray.Length > 0) 
                { 
                    // Merge attrArry and typeAttrArray
                    Attribute[] mergedAttrArray = new Attribute[attrArray.Length + typeAttrArray.Length]; 
                    Array.Copy(attrArray, mergedAttrArray, attrArray.Length);
                    Array.Copy(typeAttrArray, 0, mergedAttrArray, attrArray.Length, typeAttrArray.Length);
                    attrArray = mergedAttrArray;
                } 

                // Look for and expand AttributeProvider attributes.  These are attributes 
                // that allow a method to adopt attributes from another location.  This 
                // allows generic properties, such as "public object DataSource {get; set;}",
                // to share a common set of attributes. 

                Attribute[] addAttrs = null;

                foreach(Attribute attr in attrArray) 
                {
                    AttributeProviderAttribute pa = attr as AttributeProviderAttribute; 
                    if (pa != null) 
                    {
                        Type providerType = Type.GetType(pa.TypeName); 
                        if (providerType != null)
                        {
                            Attribute[] paAttrs = null;
                            if (!string.IsNullOrEmpty(pa.PropertyName)) 
                            {
                                MemberInfo[] milist = providerType.GetMember(pa.PropertyName); 
                                if (milist.Length > 0 && milist[0] != null) 
                                {
                                    paAttrs = (Attribute[])milist[0].GetCustomAttributes(typeof(Attribute), true); 
                                }
                            }
                            else {
                                paAttrs = (Attribute[])providerType.GetCustomAttributes(typeof(Attribute), true); 
                            }
 
                            if (paAttrs != null) 
                            {
                                if (addAttrs == null) 
                                {
                                    addAttrs = paAttrs;
                                }
                                else 
                                {
                                    Attribute[] newArray = new Attribute[addAttrs.Length + paAttrs.Length]; 
                                    addAttrs.CopyTo(newArray, 0); 
                                    paAttrs.CopyTo(newArray, addAttrs.Length);
                                    addAttrs = newArray; 

                                }
                            }
                        } 
                    }
                } 
 
                // See if we gathered additional attributes.  These are always lower priority
                // and therefore get tacked onto the end of the list 
                if (addAttrs != null)
                {
                    Attribute[] newArray = new Attribute[addAttrs.Length + attrArray.Length];
                    attrArray.CopyTo(newArray, 0); 
                    addAttrs.CopyTo(newArray, attrArray.Length);
                    attrArray = newArray; 
                } 

                return new AttributeCollection(attrArray); 
            }

            return AttributeCollection.Empty;
        } 

        ///  
        ///     A helper method that returns the static "Get" method for an attached 
        ///     property.  The dpType parameter is the data type of the object you
        ///     want to attach the property to. 
        /// 
        private static MethodInfo GetAttachedPropertySetMethod(DependencyProperty dp)
        {
            // Check the cache.  This property descriptor is cached by the 
            // dependency object provider, but there is a unique property descriptor
            // for each type an attached property can be attached to.  Therefore, 
            // caching this method lookup for a DP makes sense. 

            MethodInfo method; 

            // TypeDescriptor offers a feature called a "reflection type", which
            // is an indirection to another type we should reflect on.  Anywhere
            // we rely on raw reflection we should be using GetReflectionType. 
            // Also the returning type may change if someone added or removed
            // a provider, so we need to detect this in our cache invalidation 
            // logic. 

            Type reflectionType = TypeDescriptor.GetReflectionType(dp.OwnerType); 

            object methodObj = _setMethodCache[dp];
            method = methodObj as MethodInfo;
 
            if (methodObj == null || (method != null && !object.ReferenceEquals(method.DeclaringType, reflectionType))) {
                BindingFlags f = BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly; 
                string methodName = string.Concat("Set", dp.Name); 

                Type[] paramTypes = new Type[] { 
                    typeof(DependencyObject),
                    dp.PropertyType
                };
 
                method = reflectionType.GetMethod(methodName, f, _dpBinder, paramTypes, null);
 
                lock(_setMethodCache) { 
                    _setMethodCache[dp] = (method == null ? _nullMethodSentinel : method);
                } 
            }

            return method;
        } 

        ///  
        ///     Returns one of the "special" property methods for a property descriptor. 
        ///     The property name will be appended to the method prefix.  This method
        ///     is used to return the MethodInfo for ShouldSerialize(property) and 
        ///     Reset(property).
        /// 
        private MethodInfo GetSpecialMethod(string methodPrefix)
        { 
            BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic;
            Type[] types; 
            Type reflectionType; 

            if (_property == null) 
            {
                // Attached property
                types = _dpType;
                flags |= BindingFlags.Static; 

                // TypeDescriptor offers a feature called a "reflection type", which 
                // is an indirection to another type we should reflect on.  Anywyere 
                // we rely on raw reflection we should be using GetReflectionType.
 
                reflectionType = TypeDescriptor.GetReflectionType(_dp.OwnerType);
            }
            else
            { 
                // Direct property
                types = _emptyType; 
                flags |= BindingFlags.Instance; 

                // TypeDescriptor offers a feature called a "reflection type", which 
                // is an indirection to another type we should reflect on.  Anywyere
                // we rely on raw reflection we should be using GetReflectionType.

                reflectionType = TypeDescriptor.GetReflectionType(_property.ComponentType); 
            }
 
            string methodName = string.Concat(methodPrefix, _dp.Name); 

            // According to spec, ShouldSerialize and Reset can be non-public.  So we should 
            // assert ReflectionPermission here, like TypeDescriptor does.  But since every
            // assert is a security risk, we'll take the compatibility hit, and leave it out.

            MethodInfo methodInfo = reflectionType.GetMethod(methodName, flags, _dpBinder, types, null); 

            if (methodInfo != null) 
            { 
                // We don't support non-public ShouldSerialize/ClearValue methods.  We could just look
                // for public methods in the first place, but then authors might get confused as 
                // to why their non-public method didn't get found, especially because the CLR
                // TypeDescriptor does find and use non-public methods.
                if( !methodInfo.IsPublic )
                { 
                    throw new InvalidOperationException(SR.Get(SRID.SpecialMethodMustBePublic, methodInfo.Name));
                } 
            } 

            return methodInfo; 


        }
 
        /// 
        ///     This method is called on demand when we need to get at one or 
        ///     more attributes for this property.  Because obtaining attributes 
        ///     can be costly, we wait until now to do the job.
        ///  
        private void MergeAttributes()
        {
            AttributeCollection baseAttributes;
 
            if (_property != null)
            { 
                baseAttributes = _property.Attributes; 
            }
            else 
            {
                baseAttributes = GetAttachedPropertyAttributes();
            }
 
            List newAttributes = new List(baseAttributes.Count + 1);
 
            bool readOnly = false; 

            foreach (Attribute a in baseAttributes) 
            {
                Attribute attrToAdd = a;
                DefaultValueAttribute defAttr = a as DefaultValueAttribute;
 
                if (defAttr != null)
                { 
                    // DP metadata always overrides CLR metadata for 
                    // default value.
                    attrToAdd = null; 
                }
                else
                {
                    ReadOnlyAttribute roAttr = a as ReadOnlyAttribute; 
                    if (roAttr != null)
                    { 
                        // DP metata is the merge of CLR metadata for 
                        // read only
                        readOnly = roAttr.IsReadOnly; 
                        attrToAdd = null;
                    }
                }
 
                if (attrToAdd != null) newAttributes.Add(attrToAdd);
            } 
 
            // Always include the metadata choice
            readOnly |= _dp.ReadOnly; 

            // If we are an attached property and non-read only, the lack of a
            // set method will make us read only.
            if (_property == null && !readOnly && GetAttachedPropertySetMethod(_dp) == null) { 
                readOnly = true;
            } 
 
            // Add our own DependencyPropertyAttribute
            DependencyPropertyAttribute dpa = new DependencyPropertyAttribute(_dp, (_property == null)); 
            newAttributes.Add(dpa);

            // Add DefaultValueAttribute if the DP has a default
            // value 
            if (_metadata.DefaultValue != DependencyProperty.UnsetValue)
            { 
                newAttributes.Add(new DefaultValueAttribute(_metadata.DefaultValue)); 
            }
 
            // And add a read only attribute if needed
            if (readOnly)
            {
                newAttributes.Add(new ReadOnlyAttribute(true)); 
            }
 
            // Inject these attributes into our attribute array.  There 
            // is a quirk to the way this works.  Attributes as they
            // are returned by the CLR and by AttributeCollection are in 
            // priority order with the attributes at the front of the list
            // taking precidence over those at the end.  Attributes
            // handed to MemberDescriptor's AttributeArray, however, are
            // in reverse priority order so the "last one in wins".  Therefore 
            // we need to reverse the array.
 
            Attribute[] attrArray = newAttributes.ToArray(); 
            for (int idx = 0; idx < attrArray.Length / 2; idx++)
            { 
                int swap = attrArray.Length - idx - 1;
                Attribute t = attrArray[idx];
                attrArray[idx] = attrArray[swap];
                attrArray[swap] = t; 
            }
 
            AttributeArray = attrArray; 
        }
 

        #endregion Private Methods

 
        //-----------------------------------------------------
        // 
        //  Private Fields 
        //
        //------------------------------------------------------ 

        #region Private Fields

        private static Type[] _emptyType = new Type[0]; 
        private static Type[] _dpType = new Type[] {typeof(DependencyObject)};
        private static Binder _dpBinder = new AttachedPropertyMethodSelector(); 
 
        private static object _nullMethodSentinel = new object();
 
        // Synchronized by "_getMethodCache"
        private static Hashtable _getMethodCache = new Hashtable();

        // Synchronized by "_setMethodCache" 
        private static Hashtable _setMethodCache = new Hashtable();
 
        // Synchronization object for Attributes property.  This would be better to be a 
        // member than a static value, but the need for a lock on Attributes is very
        // rare and isn't worth the additional space this would take up as a member 
        private static object _attributeSyncLock = new object();

        private PropertyDescriptor _property;
        private DependencyProperty _dp; 
        private Type _componentType;
        private PropertyMetadata _metadata; 
        private MethodInfo _shouldSerializeMethod; 
        private MethodInfo _resetMethod;
        private bool _queriedShouldSerializeMethod; 
        private bool _queriedResetMethod;
        private Dictionary _trackers;

        #endregion Private Fields 
    }
} 
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
 
namespace MS.Internal.ComponentModel
{
    using System;
    using System.Collections; 
    using System.Collections.Generic;
    using System.ComponentModel; 
    using System.Diagnostics; 
    using System.Globalization;
    using System.Reflection; 
    using System.Security.Permissions;
    using System.Windows;
    using System.Security;
 
    /// 
    ///     An inplementation of a property descriptor for DependencyProperties. 
    ///     This supports both normal and attached properties. 
    /// 
    internal sealed class DependencyObjectPropertyDescriptor : PropertyDescriptor { 

        //------------------------------------------------------
        //
        //  Constructors 
        //
        //----------------------------------------------------- 
 
        #region Constructors
 
        /// 
        ///     Creates a new dependency property descriptor.  A note on perf:  We don't
        ///     pass the property descriptor down as the default member descriptor here.  Doing
        ///     so takes the attributes off of the property descriptor, which can be costly if they 
        ///     haven't been accessed yet.  Instead, we wait until someone needs to access our
        ///     Attributes property and demand create the attributes at that time. 
        ///  
        internal DependencyObjectPropertyDescriptor(PropertyDescriptor property, DependencyProperty dp, Type objectType)
            : base(dp.Name, null) 
        {
            _property = property;
            _dp = dp;
 
            Debug.Assert(property != null && dp != null);
            Debug.Assert(!(property is DependencyObjectPropertyDescriptor), "Wrapping a DP in a DP"); 
 
            _componentType = property.ComponentType;
            _metadata = _dp.GetMetadata(objectType); 
        }

        /// 
        ///     Creates a new dependency property descriptor.  A note on perf:  We don't 
        ///     pass the property descriptor down as the default member descriptor here.  Doing
        ///     so takes the attributes off of the property descriptor, which can be costly if they 
        ///     haven't been accessed yet.  Instead, we wait until someone needs to access our 
        ///     Attributes property and demand create the attributes at that time.
        ///  
        internal DependencyObjectPropertyDescriptor(DependencyProperty dp, Type ownerType)
            : base(string.Concat(dp.OwnerType.Name, ".", dp.Name), null)
        {
            _dp = dp; 
            _componentType = ownerType;
            _metadata = _dp.GetMetadata(ownerType); 
        } 

        #endregion Constructors 


        //-----------------------------------------------------
        // 
        //  Public Methods
        // 
        //----------------------------------------------------- 

        #region Public Methods 

        /// 
        ///     Indicates if this property's value can be reset.
        ///     We map this to true if there is a local value 
        ///     on the DP.
        ///  
        public override bool CanResetValue(object component) 
        {
            // All DPs that have values set can be reset 
            return ShouldSerializeValue(component);
        }

        ///  
        ///     Returns the value for this property.
        ///  
        public override object GetValue(object component) 
        {
            DependencyObject DO = FromObj(component); 
            return DO.GetValue(_dp);
        }

        ///  
        ///     Attempts to reset (or clear) the value in the
        ///     DP. 
        ///  
        public override void ResetValue(object component)
        { 
            if (!_queriedResetMethod)
            {
                _resetMethod = GetSpecialMethod("Reset");
                _queriedResetMethod = true; 
            }
 
            DependencyObject DO = FromObj(component); 

            if (_resetMethod != null) 
            {
                // See if we need to pass parameters to this method.  When
                // _property == null, this is an attached property and
                // the method is static.  When _property != null, this 
                // is a direct property and the method is instanced.
 
                if (_property == null) 
                {
                    _resetMethod.Invoke(null, new object[] {DO}); 
                }
                else
                {
                    _resetMethod.Invoke(DO, null); 
                }
            } 
            else 
            {
                DO.ClearValue(_dp); 
            }
        }

        ///  
        ///     Sets the property value on the given object.
        ///  
        public override void SetValue(object component, object value) 
        {
            DependencyObject DO = FromObj(component); 
            DO.SetValue(_dp, value);
        }

        ///  
        ///     Returns true if the property contains
        ///     a local value that should be serialized. 
        ///  
        public override bool ShouldSerializeValue(object component)
        { 
            DependencyObject DO = FromObj(component);
            bool shouldSerialize = DO.ShouldSerializeProperty(_dp);

            // The of precedence is that a property should be serialized if the ShouldSerializeProperty 
            // method returns true and either ShouldSerializeXXX does not exist or it exists and returns true.
 
            if (shouldSerialize) 
            {
                // If we have a ShouldSerialize method, use it 

                if (!_queriedShouldSerializeMethod)
                {
                    MethodInfo method = GetSpecialMethod("ShouldSerialize"); 

                    if (method != null && method.ReturnType == typeof(bool)) 
                    { 
                        _shouldSerializeMethod = method;
                    } 
                    _queriedShouldSerializeMethod = true;
                }

                if (_shouldSerializeMethod != null) 
                {
                    // See if we need to pass parameters to this method.  When 
                    // _property == null, this is an attached property and 
                    // the method is static.  When _property != null, this
                    // is a direct property and the method is instanced. 

                    if (_property == null)
                    {
                        shouldSerialize = (bool)_shouldSerializeMethod.Invoke(null, new object[] {DO}); 
                    }
                    else 
                    { 
                        shouldSerialize = (bool)_shouldSerializeMethod.Invoke(DO, null);
                    } 
                }
            }

            return shouldSerialize; 
        }
 
        ///  
        ///     Adds a change event handler to this descriptor.
        ///  
        public override void AddValueChanged(object component, EventHandler handler)
        {
            //
 

            DependencyObject DO = FromObj(component); 
            if (_trackers == null) 
            {
                _trackers = new Dictionary(); 
            }

            PropertyChangeTracker tracker;
            if (!_trackers.TryGetValue(DO, out tracker)) 
            {
                tracker = new PropertyChangeTracker(DO, _dp); 
                _trackers.Add(DO, tracker); 
            }
 
            tracker.Changed += handler;
        }

        ///  
        ///     Removes a previously added change handler.
        ///  
        public override void RemoveValueChanged(object component, EventHandler handler) { 
            if (_trackers == null) return;
            PropertyChangeTracker tracker; 
            DependencyObject DO = FromObj(component);

            if (_trackers.TryGetValue(DO, out tracker))
            { 
                tracker.Changed -= handler;
                if (tracker.CanClose) 
                { 
                    tracker.Close();
                    _trackers.Remove(DO); 
                }
            }
        }
 

        #endregion Public Methods 
 
        //------------------------------------------------------
        // 
        //  Public Properties
        //
        //-----------------------------------------------------
 
        #region Public Properties
 
        ///  
        ///     Returns the collection of attributes associated with this property.
        ///  
        public override AttributeCollection Attributes
        {
            get
            { 
                // Windows OS Bugs 1383847.  The Attributes
                // property on PropertyDescriptor is not as thread 
                // safe as it should be.  There are instances when 
                // it can return null during contention.  Our fix is
                // to detect this case and lock.  Note that this isn't 
                // 100% because not all property descriptors are
                // DependencyObjectPropertyDescriptors.

                AttributeCollection attrs = base.Attributes; 
                if (attrs == null)
                { 
                    lock(_attributeSyncLock) 
                    {
                        attrs = base.Attributes; 
                        Debug.Assert(attrs != null);
                    }
                }
 
                return attrs;
            } 
        } 

        ///  
        ///     The type of object this property is describing.
        /// 
        public override Type ComponentType
        { 
            get { return _componentType; }
        } 
 
        /// 
        ///     Returns true if the DP is read only. 
        /// 
        public override bool IsReadOnly
        {
            get { 

                // It is a lot cheaper to get DP metadata 
                // than it is to calculate attributes.  While 
                // the attributes do factor in DP metadata, short
                // circuit for this common case. 
                bool readOnly = _dp.ReadOnly;
                if (!readOnly) {
                    readOnly = Attributes.Contains(ReadOnlyAttribute.Yes);
                } 
                return readOnly;
            } 
        } 

        ///  
        ///     The type of the property.
        /// 
        public override Type PropertyType
        { 
            get { return _dp.PropertyType; }
        } 
 
        /// 
        ///     Returns true if this property descriptor supports change 
        ///     notifications.  All dependency property descriptors do.
        /// 
        public override bool SupportsChangeEvents
        { 
            get { return true; }
        } 
 
        #endregion Public Properties
 

        //------------------------------------------------------
        //
        //  Internal Properties 
        //
        //------------------------------------------------------ 
 
        #region Internal Properties
 
        /// 
        ///     Returns the dependency property we're wrapping.
        /// 
        internal DependencyProperty DependencyProperty 
        {
            get { return _dp; } 
        } 

 
        internal bool IsAttached
        {
             get { return (_property == null); }
        } 

        internal PropertyMetadata Metadata 
        { 
            get { return _metadata; }
        } 

        #endregion Internal Properties

        //----------------------------------------------------- 
        //
        //  Internal Methods 
        // 
        //------------------------------------------------------
 
        #region Internal Methods

        /// 
        ///     This method is called when we should clear our cached state.  The cache 
        ///     may become invalid if someone adds additional type description providers.
        ///  
        internal static void ClearCache() 
        {
            lock (_getMethodCache) 
            {
                _getMethodCache.Clear();
            }
 
            lock(_setMethodCache)
            { 
                _setMethodCache.Clear(); 
            }
        } 

        /// 
        ///     A helper method that returns the static "Get" method for an attached
        ///     property.  The dpType parameter is the data type of the object you 
        ///     want to attach the property to.
        ///  
        internal static MethodInfo GetAttachedPropertyMethod(DependencyProperty dp) 
        {
            // Check the cache.  This property descriptor is cached by the 
            // dependency object provider, but there is a unique property descriptor
            // for each type an attached property can be attached to.  Therefore,
            // caching this method lookup for a DP makes sense.
 
            MethodInfo method;
 
            // TypeDescriptor offers a feature called a "reflection type", which 
            // is an indirection to another type we should reflect on.  Anywhere
            // we rely on raw reflection we should be using GetReflectionType. 
            // Also the returning type may change if someone added or removed
            // a provider, so we need to detect this in our cache invalidation
            // logic.
 
            Type reflectionType = TypeDescriptor.GetReflectionType(dp.OwnerType);
 
            object methodObj = _getMethodCache[dp]; 
            method = methodObj as MethodInfo;
 
            if (methodObj == null || (method != null && !object.ReferenceEquals(method.DeclaringType, reflectionType))) {
                BindingFlags f = BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly;
                string methodName = string.Concat("Get", dp.Name);
                method = reflectionType.GetMethod(methodName, f, _dpBinder, _dpType, null); 

                lock(_getMethodCache) { 
                    _getMethodCache[dp] = (method == null ? _nullMethodSentinel : method); 
                }
            } 

            return method;
        }
 
        #endregion Internal Methods
 
        //----------------------------------------------------- 
        //
        //  Protected Methods 
        //
        //-----------------------------------------------------

        #region Protected Methods 

        ///  
        ///     Overridden to lazily create our attributes. 
        /// 
        protected override AttributeCollection CreateAttributeCollection() 
        {
            MergeAttributes();
            return base.CreateAttributeCollection();
        } 

        #endregion Protected Methods 
 

        //----------------------------------------------------- 
        //
        //  Private Methods
        //
        //------------------------------------------------------ 

        #region Private Methods 
 
        /// 
        ///     Helper method that recovers a dependency object from a value. 
        /// 
        private static DependencyObject FromObj(object value)
        {
            // This indirection is necessary to support 
            // the "association" feature of type descriptor.  This feature
            // alows one object to mimic the API of another. 
            return (DependencyObject)TypeDescriptor.GetAssociation(typeof(DependencyObject), value); 
        }
 
        /// 
        ///     Additional metadata attributes for attached properties
        ///     are taken from the "Get" method.
        ///  
        private AttributeCollection GetAttachedPropertyAttributes()
        { 
            MethodInfo mi = GetAttachedPropertyMethod(_dp); 

            if (mi != null) 
            {
                Attribute[] attrArray = (Attribute[])mi.GetCustomAttributes(typeof(Attribute), true);

                Type propertyReflectionType = TypeDescriptor.GetReflectionType(_dp.PropertyType); 
                Attribute[] typeAttrArray = (Attribute[])propertyReflectionType.GetCustomAttributes(typeof(Attribute), true);
                if (typeAttrArray != null && typeAttrArray.Length > 0) 
                { 
                    // Merge attrArry and typeAttrArray
                    Attribute[] mergedAttrArray = new Attribute[attrArray.Length + typeAttrArray.Length]; 
                    Array.Copy(attrArray, mergedAttrArray, attrArray.Length);
                    Array.Copy(typeAttrArray, 0, mergedAttrArray, attrArray.Length, typeAttrArray.Length);
                    attrArray = mergedAttrArray;
                } 

                // Look for and expand AttributeProvider attributes.  These are attributes 
                // that allow a method to adopt attributes from another location.  This 
                // allows generic properties, such as "public object DataSource {get; set;}",
                // to share a common set of attributes. 

                Attribute[] addAttrs = null;

                foreach(Attribute attr in attrArray) 
                {
                    AttributeProviderAttribute pa = attr as AttributeProviderAttribute; 
                    if (pa != null) 
                    {
                        Type providerType = Type.GetType(pa.TypeName); 
                        if (providerType != null)
                        {
                            Attribute[] paAttrs = null;
                            if (!string.IsNullOrEmpty(pa.PropertyName)) 
                            {
                                MemberInfo[] milist = providerType.GetMember(pa.PropertyName); 
                                if (milist.Length > 0 && milist[0] != null) 
                                {
                                    paAttrs = (Attribute[])milist[0].GetCustomAttributes(typeof(Attribute), true); 
                                }
                            }
                            else {
                                paAttrs = (Attribute[])providerType.GetCustomAttributes(typeof(Attribute), true); 
                            }
 
                            if (paAttrs != null) 
                            {
                                if (addAttrs == null) 
                                {
                                    addAttrs = paAttrs;
                                }
                                else 
                                {
                                    Attribute[] newArray = new Attribute[addAttrs.Length + paAttrs.Length]; 
                                    addAttrs.CopyTo(newArray, 0); 
                                    paAttrs.CopyTo(newArray, addAttrs.Length);
                                    addAttrs = newArray; 

                                }
                            }
                        } 
                    }
                } 
 
                // See if we gathered additional attributes.  These are always lower priority
                // and therefore get tacked onto the end of the list 
                if (addAttrs != null)
                {
                    Attribute[] newArray = new Attribute[addAttrs.Length + attrArray.Length];
                    attrArray.CopyTo(newArray, 0); 
                    addAttrs.CopyTo(newArray, attrArray.Length);
                    attrArray = newArray; 
                } 

                return new AttributeCollection(attrArray); 
            }

            return AttributeCollection.Empty;
        } 

        ///  
        ///     A helper method that returns the static "Get" method for an attached 
        ///     property.  The dpType parameter is the data type of the object you
        ///     want to attach the property to. 
        /// 
        private static MethodInfo GetAttachedPropertySetMethod(DependencyProperty dp)
        {
            // Check the cache.  This property descriptor is cached by the 
            // dependency object provider, but there is a unique property descriptor
            // for each type an attached property can be attached to.  Therefore, 
            // caching this method lookup for a DP makes sense. 

            MethodInfo method; 

            // TypeDescriptor offers a feature called a "reflection type", which
            // is an indirection to another type we should reflect on.  Anywhere
            // we rely on raw reflection we should be using GetReflectionType. 
            // Also the returning type may change if someone added or removed
            // a provider, so we need to detect this in our cache invalidation 
            // logic. 

            Type reflectionType = TypeDescriptor.GetReflectionType(dp.OwnerType); 

            object methodObj = _setMethodCache[dp];
            method = methodObj as MethodInfo;
 
            if (methodObj == null || (method != null && !object.ReferenceEquals(method.DeclaringType, reflectionType))) {
                BindingFlags f = BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly; 
                string methodName = string.Concat("Set", dp.Name); 

                Type[] paramTypes = new Type[] { 
                    typeof(DependencyObject),
                    dp.PropertyType
                };
 
                method = reflectionType.GetMethod(methodName, f, _dpBinder, paramTypes, null);
 
                lock(_setMethodCache) { 
                    _setMethodCache[dp] = (method == null ? _nullMethodSentinel : method);
                } 
            }

            return method;
        } 

        ///  
        ///     Returns one of the "special" property methods for a property descriptor. 
        ///     The property name will be appended to the method prefix.  This method
        ///     is used to return the MethodInfo for ShouldSerialize(property) and 
        ///     Reset(property).
        /// 
        private MethodInfo GetSpecialMethod(string methodPrefix)
        { 
            BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic;
            Type[] types; 
            Type reflectionType; 

            if (_property == null) 
            {
                // Attached property
                types = _dpType;
                flags |= BindingFlags.Static; 

                // TypeDescriptor offers a feature called a "reflection type", which 
                // is an indirection to another type we should reflect on.  Anywyere 
                // we rely on raw reflection we should be using GetReflectionType.
 
                reflectionType = TypeDescriptor.GetReflectionType(_dp.OwnerType);
            }
            else
            { 
                // Direct property
                types = _emptyType; 
                flags |= BindingFlags.Instance; 

                // TypeDescriptor offers a feature called a "reflection type", which 
                // is an indirection to another type we should reflect on.  Anywyere
                // we rely on raw reflection we should be using GetReflectionType.

                reflectionType = TypeDescriptor.GetReflectionType(_property.ComponentType); 
            }
 
            string methodName = string.Concat(methodPrefix, _dp.Name); 

            // According to spec, ShouldSerialize and Reset can be non-public.  So we should 
            // assert ReflectionPermission here, like TypeDescriptor does.  But since every
            // assert is a security risk, we'll take the compatibility hit, and leave it out.

            MethodInfo methodInfo = reflectionType.GetMethod(methodName, flags, _dpBinder, types, null); 

            if (methodInfo != null) 
            { 
                // We don't support non-public ShouldSerialize/ClearValue methods.  We could just look
                // for public methods in the first place, but then authors might get confused as 
                // to why their non-public method didn't get found, especially because the CLR
                // TypeDescriptor does find and use non-public methods.
                if( !methodInfo.IsPublic )
                { 
                    throw new InvalidOperationException(SR.Get(SRID.SpecialMethodMustBePublic, methodInfo.Name));
                } 
            } 

            return methodInfo; 


        }
 
        /// 
        ///     This method is called on demand when we need to get at one or 
        ///     more attributes for this property.  Because obtaining attributes 
        ///     can be costly, we wait until now to do the job.
        ///  
        private void MergeAttributes()
        {
            AttributeCollection baseAttributes;
 
            if (_property != null)
            { 
                baseAttributes = _property.Attributes; 
            }
            else 
            {
                baseAttributes = GetAttachedPropertyAttributes();
            }
 
            List newAttributes = new List(baseAttributes.Count + 1);
 
            bool readOnly = false; 

            foreach (Attribute a in baseAttributes) 
            {
                Attribute attrToAdd = a;
                DefaultValueAttribute defAttr = a as DefaultValueAttribute;
 
                if (defAttr != null)
                { 
                    // DP metadata always overrides CLR metadata for 
                    // default value.
                    attrToAdd = null; 
                }
                else
                {
                    ReadOnlyAttribute roAttr = a as ReadOnlyAttribute; 
                    if (roAttr != null)
                    { 
                        // DP metata is the merge of CLR metadata for 
                        // read only
                        readOnly = roAttr.IsReadOnly; 
                        attrToAdd = null;
                    }
                }
 
                if (attrToAdd != null) newAttributes.Add(attrToAdd);
            } 
 
            // Always include the metadata choice
            readOnly |= _dp.ReadOnly; 

            // If we are an attached property and non-read only, the lack of a
            // set method will make us read only.
            if (_property == null && !readOnly && GetAttachedPropertySetMethod(_dp) == null) { 
                readOnly = true;
            } 
 
            // Add our own DependencyPropertyAttribute
            DependencyPropertyAttribute dpa = new DependencyPropertyAttribute(_dp, (_property == null)); 
            newAttributes.Add(dpa);

            // Add DefaultValueAttribute if the DP has a default
            // value 
            if (_metadata.DefaultValue != DependencyProperty.UnsetValue)
            { 
                newAttributes.Add(new DefaultValueAttribute(_metadata.DefaultValue)); 
            }
 
            // And add a read only attribute if needed
            if (readOnly)
            {
                newAttributes.Add(new ReadOnlyAttribute(true)); 
            }
 
            // Inject these attributes into our attribute array.  There 
            // is a quirk to the way this works.  Attributes as they
            // are returned by the CLR and by AttributeCollection are in 
            // priority order with the attributes at the front of the list
            // taking precidence over those at the end.  Attributes
            // handed to MemberDescriptor's AttributeArray, however, are
            // in reverse priority order so the "last one in wins".  Therefore 
            // we need to reverse the array.
 
            Attribute[] attrArray = newAttributes.ToArray(); 
            for (int idx = 0; idx < attrArray.Length / 2; idx++)
            { 
                int swap = attrArray.Length - idx - 1;
                Attribute t = attrArray[idx];
                attrArray[idx] = attrArray[swap];
                attrArray[swap] = t; 
            }
 
            AttributeArray = attrArray; 
        }
 

        #endregion Private Methods

 
        //-----------------------------------------------------
        // 
        //  Private Fields 
        //
        //------------------------------------------------------ 

        #region Private Fields

        private static Type[] _emptyType = new Type[0]; 
        private static Type[] _dpType = new Type[] {typeof(DependencyObject)};
        private static Binder _dpBinder = new AttachedPropertyMethodSelector(); 
 
        private static object _nullMethodSentinel = new object();
 
        // Synchronized by "_getMethodCache"
        private static Hashtable _getMethodCache = new Hashtable();

        // Synchronized by "_setMethodCache" 
        private static Hashtable _setMethodCache = new Hashtable();
 
        // Synchronization object for Attributes property.  This would be better to be a 
        // member than a static value, but the need for a lock on Attributes is very
        // rare and isn't worth the additional space this would take up as a member 
        private static object _attributeSyncLock = new object();

        private PropertyDescriptor _property;
        private DependencyProperty _dp; 
        private Type _componentType;
        private PropertyMetadata _metadata; 
        private MethodInfo _shouldSerializeMethod; 
        private MethodInfo _resetMethod;
        private bool _queriedShouldSerializeMethod; 
        private bool _queriedResetMethod;
        private Dictionary _trackers;

        #endregion Private Fields 
    }
} 
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK