Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Base / MS / Internal / ComponentModel / DependencyObjectPropertyDescriptor.cs / 1305600 / 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; using SR=MS.Internal.WindowsBase.SR; using SRID=MS.Internal.WindowsBase.SRID; ////// 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 == 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; } ////// 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(); } _dpType = null; _boolType = null; _attributeType = null; _attachedPropertyBrowsableType = null; } ////// 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) { 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; } ////// 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[] { DpType[0], TypeDescriptor.GetReflectionType(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 = 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; } ////// 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(); } ListnewAttributes = 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 Properties // //------------------------------------------------------ #region Private Properties /// /// Helper to get the reflection version of typeof(AttachedPropertyBrowsableAttribute). This is used by /// the AttachInfo class. /// internal static Type AttachedPropertyBrowsableAttributeType { get { Type attachedPropertyBrowsableType = _attachedPropertyBrowsableType; if (attachedPropertyBrowsableType == null) { attachedPropertyBrowsableType = TypeDescriptor.GetReflectionType(typeof(AttachedPropertyBrowsableAttribute)); _attachedPropertyBrowsableType = attachedPropertyBrowsableType; } return attachedPropertyBrowsableType; } } ////// Helper to get the reflection version of typeof(Attribute) /// private static Type AttributeType { get { Type attributeType = _attributeType; if (attributeType == null) { attributeType = TypeDescriptor.GetReflectionType(typeof(Attribute)); _attributeType = attributeType; } return attributeType; } } ////// Helper to get the reflection version of typeof(bool) /// private static Type BoolType { get { Type boolType = _boolType; if (boolType == null) { boolType = TypeDescriptor.GetReflectionType(typeof(bool)); _boolType = boolType; } return boolType; } } ////// Helper to get and cache the reflection version of a dependency object type array /// 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_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 } } // 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; using SR=MS.Internal.WindowsBase.SR; using SRID=MS.Internal.WindowsBase.SRID; /// /// 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 == 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; } ////// 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(); } _dpType = null; _boolType = null; _attributeType = null; _attachedPropertyBrowsableType = null; } ////// 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) { 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; } ////// 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[] { DpType[0], TypeDescriptor.GetReflectionType(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 = 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; } ////// 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(); } ListnewAttributes = 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 Properties // //------------------------------------------------------ #region Private Properties /// /// Helper to get the reflection version of typeof(AttachedPropertyBrowsableAttribute). This is used by /// the AttachInfo class. /// internal static Type AttachedPropertyBrowsableAttributeType { get { Type attachedPropertyBrowsableType = _attachedPropertyBrowsableType; if (attachedPropertyBrowsableType == null) { attachedPropertyBrowsableType = TypeDescriptor.GetReflectionType(typeof(AttachedPropertyBrowsableAttribute)); _attachedPropertyBrowsableType = attachedPropertyBrowsableType; } return attachedPropertyBrowsableType; } } ////// Helper to get the reflection version of typeof(Attribute) /// private static Type AttributeType { get { Type attributeType = _attributeType; if (attributeType == null) { attributeType = TypeDescriptor.GetReflectionType(typeof(Attribute)); _attributeType = attributeType; } return attributeType; } } ////// Helper to get the reflection version of typeof(bool) /// private static Type BoolType { get { Type boolType = _boolType; if (boolType == null) { boolType = TypeDescriptor.GetReflectionType(typeof(bool)); _boolType = boolType; } return boolType; } } ////// Helper to get and cache the reflection version of a dependency object type array /// 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_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 } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- GridViewColumnHeaderAutomationPeer.cs
- WebDescriptionAttribute.cs
- SocketSettings.cs
- DocumentEventArgs.cs
- ErasingStroke.cs
- CompositeFontParser.cs
- SessionPageStateSection.cs
- ConfigurationLocation.cs
- Currency.cs
- CellParagraph.cs
- CompiledQueryCacheKey.cs
- VisualStyleInformation.cs
- XPathScanner.cs
- HttpModuleAction.cs
- ToolboxItemCollection.cs
- WindowsSpinner.cs
- SecurityRuntime.cs
- MiniAssembly.cs
- ContextStack.cs
- InspectionWorker.cs
- MimeMultiPart.cs
- DeadCharTextComposition.cs
- XmlRawWriter.cs
- ToolStripContentPanel.cs
- MeasureData.cs
- SecurityTimestamp.cs
- Activator.cs
- Figure.cs
- WebPartConnectionsCloseVerb.cs
- SchemaImporterExtensionElementCollection.cs
- TracingConnection.cs
- AdornerDecorator.cs
- FrameworkElement.cs
- SqlFileStream.cs
- OdbcCommand.cs
- WebControl.cs
- CodeFieldReferenceExpression.cs
- Attributes.cs
- OleDbError.cs
- XmlChoiceIdentifierAttribute.cs
- BitmapEffectGroup.cs
- FileInfo.cs
- ComponentChangedEvent.cs
- PolyBezierSegment.cs
- TreeNodeBindingCollection.cs
- LOSFormatter.cs
- SQLMembershipProvider.cs
- RNGCryptoServiceProvider.cs
- AvTraceFormat.cs
- SecurityCriticalDataForSet.cs
- DataTransferEventArgs.cs
- ConfigurationValidatorAttribute.cs
- DocumentPageView.cs
- RelOps.cs
- DataGridDefaultColumnWidthTypeConverter.cs
- FormsIdentity.cs
- Reference.cs
- DataGridViewEditingControlShowingEventArgs.cs
- PingOptions.cs
- CompiledAction.cs
- XmlChoiceIdentifierAttribute.cs
- GraphicsPath.cs
- TemplateBindingExtension.cs
- LabelEditEvent.cs
- FontStretchConverter.cs
- SqlCacheDependencyDatabase.cs
- PropertyGrid.cs
- BindValidator.cs
- HtmlInputReset.cs
- OrderedDictionary.cs
- DocumentXmlWriter.cs
- GridViewSelectEventArgs.cs
- QilDataSource.cs
- PartitionResolver.cs
- DelimitedListTraceListener.cs
- Exception.cs
- CodeChecksumPragma.cs
- IisTraceListener.cs
- AuthenticationConfig.cs
- ToolStripMenuItemDesigner.cs
- CursorEditor.cs
- ADMembershipProvider.cs
- ISAPIRuntime.cs
- SQlBooleanStorage.cs
- XPathScanner.cs
- ReadOnlyPermissionSet.cs
- SimpleLine.cs
- FontConverter.cs
- OutputCacheProfileCollection.cs
- Propagator.cs
- AutomationFocusChangedEventArgs.cs
- SyndicationDeserializer.cs
- DelegatingTypeDescriptionProvider.cs
- VisualStyleRenderer.cs
- BaseTemplateCodeDomTreeGenerator.cs
- WinEventHandler.cs
- ADMembershipProvider.cs
- ModelPerspective.cs
- RequestSecurityTokenForRemoteTokenFactory.cs
- MULTI_QI.cs