File: Base\MS\Internal\ComponentModel\DependencyObjectPropertyDescriptor.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)

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;
    using SR=MS.Internal.WindowsBase.SR;
    using SRID=MS.Internal.WindowsBase.SRID;
 
 
    /// <summary>
    ///     An inplementation of a property descriptor for DependencyProperties.
    ///     This supports both normal and attached properties.
    /// </summary>
    internal sealed class DependencyObjectPropertyDescriptor : PropertyDescriptor {
        
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        ///     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.
        /// </summary>
        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);
        }
 
        /// <summary>
        ///     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.
        /// </summary>
        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
 
        /// <summary>
        ///     Indicates if this property's value can be reset.
        ///     We map this to true if there is a local value
        ///     on the DP.
        /// </summary>
        public override bool CanResetValue(object component) 
        {
            // All DPs that have values set can be reset
            return ShouldSerializeValue(component);
        }
 
        /// <summary>
        ///     Returns the value for this property.
        /// </summary>
        public override object GetValue(object component) 
        {
            DependencyObject DO = FromObj(component);
            return DO.GetValue(_dp);
        }
 
        /// <summary>
        ///     Attempts to reset (or clear) the value in the
        ///     DP.
        /// </summary>
        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);
            }
        }
 
        /// <summary>
        ///     Sets the property value on the given object.
        /// </summary>
        public override void SetValue(object component, object value) 
        {
            DependencyObject DO = FromObj(component);
            DO.SetValue(_dp, value);
        }
 
        /// <summary>
        ///     Returns true if the property contains
        ///     a local value that should be serialized.
        /// </summary>
        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 == BoolType) 
                    {
                        _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;
        }
 
        /// <summary>
        ///     Adds a change event handler to this descriptor.
        /// </summary>
        public override void AddValueChanged(object component, EventHandler handler) 
        {
            // 
 
 
            DependencyObject DO = FromObj(component);
            if (_trackers == null) 
            {
                _trackers = new Dictionary<DependencyObject, PropertyChangeTracker>();
            }
 
            PropertyChangeTracker tracker;
            if (!_trackers.TryGetValue(DO, out tracker)) 
            {
                tracker = new PropertyChangeTracker(DO, _dp);
                _trackers.Add(DO, tracker);
            }
 
            tracker.Changed += handler;
        }
 
        /// <summary>
        ///     Removes a previously added change handler.
        /// </summary>
        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
        
        /// <summary>
        ///     Returns the collection of attributes associated with this property.
        /// </summary>
        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;
            }
        }
 
        /// <summary>
        ///     The type of object this property is describing.
        /// </summary>
        public override Type ComponentType 
        {
            get { return _componentType; }
        }
 
        /// <summary>
        ///     Returns true if the DP is read only.
        /// </summary>
        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;
            }
        }
 
        /// <summary>
        ///     The type of the property.
        /// </summary>
        public override Type PropertyType 
        {
            get { return _dp.PropertyType; }
        }
 
        /// <summary>
        ///     Returns true if this property descriptor supports change
        ///     notifications.  All dependency property descriptors do.
        /// </summary>
        public override bool SupportsChangeEvents 
        {
            get { return true; }
        }
 
        #endregion Public Properties
 
 
        //------------------------------------------------------
        //
        //  Internal Properties
        //
        //------------------------------------------------------
 
        #region Internal Properties
        
        /// <summary>
        ///     Returns the dependency property we're wrapping.
        /// </summary>
        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
        
        /// <summary>
        ///     This method is called when we should clear our cached state.  The cache
        ///     may become invalid if someone adds additional type description providers.
        /// </summary>
        internal static void ClearCache()
        {
            lock (_getMethodCache) 
            {
                _getMethodCache.Clear();
            }
 
            lock(_setMethodCache)
            {
                _setMethodCache.Clear();
            }
 
            _dpType = null;
            _boolType = null;
            _attributeType = null;
            _attachedPropertyBrowsableType = null;
        }
 
        /// <summary>
        ///     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.
        /// </summary>
        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
 
        /// <summary>
        ///     Overridden to lazily create our attributes.
        /// </summary>
        protected override AttributeCollection CreateAttributeCollection() 
        {
            MergeAttributes();
            return base.CreateAttributeCollection();
        }
 
        #endregion Protected Methods
 
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
        
        /// <summary>
        ///     Helper method that recovers a dependency object from a value.
        /// </summary>
        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);
        }
 
        /// <summary>
        ///     Additional metadata attributes for attached properties
        ///     are taken from the "Get" method.
        /// </summary>
        private AttributeCollection GetAttachedPropertyAttributes() 
        {
            MethodInfo mi = GetAttachedPropertyMethod(_dp);
 
            if (mi != null) 
            {
                Type attrType = AttributeType;
                Attribute[] attrArray = (Attribute[])mi.GetCustomAttributes(attrType, true);
 
                Type propertyReflectionType = TypeDescriptor.GetReflectionType(_dp.PropertyType);
                Attribute[] typeAttrArray = (Attribute[])propertyReflectionType.GetCustomAttributes(attrType, 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;
        }
 
        /// <summary>
        ///     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.
        /// </summary>
        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[] {
                    DpType[0],
                    TypeDescriptor.GetReflectionType(dp.PropertyType)
                };
    
                method = reflectionType.GetMethod(methodName, f, _dpBinder, paramTypes, null);
 
                lock(_setMethodCache) {
                    _setMethodCache[dp] = (method == null ? _nullMethodSentinel : method);
                }
            }
 
            return method;
        }
 
        /// <summary>
        ///     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).
        /// </summary>
        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 = Type.EmptyTypes;
                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;
 
            
        }
 
        /// <summary>
        ///     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.
        /// </summary>
        private void MergeAttributes() 
        {
            AttributeCollection baseAttributes;
 
            if (_property != null) 
            {
                baseAttributes = _property.Attributes;
            }
            else 
            {
                baseAttributes = GetAttachedPropertyAttributes();
            }
 
            List<Attribute> newAttributes = new List<Attribute>(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 Properties
        //
        //------------------------------------------------------
 
        #region Private Properties
 
        /// <summary>
        /// Helper to get the reflection version of typeof(AttachedPropertyBrowsableAttribute).  This is used by 
        /// the AttachInfo class.
        /// </summary>
        internal static Type AttachedPropertyBrowsableAttributeType {
            get {
                Type attachedPropertyBrowsableType = _attachedPropertyBrowsableType;
                if (attachedPropertyBrowsableType == null) {
                    attachedPropertyBrowsableType = TypeDescriptor.GetReflectionType(typeof(AttachedPropertyBrowsableAttribute));
                    _attachedPropertyBrowsableType = attachedPropertyBrowsableType;
                }
                return attachedPropertyBrowsableType;
            }
        }
 
        /// <summary>
        /// Helper to get the reflection version of typeof(Attribute)
        /// </summary>
        private static Type AttributeType {
            get {
                Type attributeType = _attributeType;
                if (attributeType == null) {
                    attributeType = TypeDescriptor.GetReflectionType(typeof(Attribute));
                    _attributeType = attributeType;
                }
                return attributeType;
            }
        }
 
        /// <summary>
        /// Helper to get the reflection version of typeof(bool)
        /// </summary>
        private static Type BoolType {
            get {
                Type boolType = _boolType;
                if (boolType == null) {
                    boolType = TypeDescriptor.GetReflectionType(typeof(bool));
                    _boolType = boolType;
                }
                return boolType;
            }
        }
 
        /// <summary>
        /// Helper to get and cache the reflection version of a dependency object type array
        /// </summary>
        private static Type[] DpType {
            get {
                Type[] dpType = _dpType;
                if (dpType == null) {
                    dpType = new Type[] { TypeDescriptor.GetReflectionType(typeof(DependencyObject)) };
                    _dpType = dpType;
                }
 
                return dpType;
            }
        }
 
        #endregion
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
 
        #region Private Fields
        
        private static Binder _dpBinder = new AttachedPropertyMethodSelector();
 
        private static object _nullMethodSentinel = new object();
 
        // Synchronized by "_getMethodCache".  Note these are "reflection"
        // member infos and should only be passed types returned from
        // GetReflectionType.
        private static Hashtable _getMethodCache = new Hashtable();
 
        // Synchronized by "_setMethodCache".  Note these are "reflection"
        // member infos and should only be passed types returned from
        // GetReflectionType.
        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 bool _queriedShouldSerializeMethod;
        private bool _queriedResetMethod;
        private Dictionary<DependencyObject, PropertyChangeTracker> _trackers;
 
        // These are reflection method infos, and should only be passed types
        // returned from GetReflectionType.
        private MethodInfo _shouldSerializeMethod;
        private MethodInfo _resetMethod;
 
        // These are constructed on demand and cleared when our cache is
        // cleared.  They are all reflection types.
        private static Type[] _dpType;
        private static Type _boolType;
        private static Type _attributeType;
        private static Type _attachedPropertyBrowsableType;
 
        #endregion Private Fields
    }
}