File: Base\MS\Internal\ComponentModel\APCustomTypeDescriptor.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.Reflection;
    using System.Windows;
    using System.Windows.Markup;
    using System.Text;
 
 
    /// <summary>
    ///     This class is a custom type descriptor for attached dependency properties.  We
    ///     could just inherit from the CustomTypeDescriptor class, which does most of the forwarding
    ///     work for us, but these are allocated a lot so we want them to be structs.
    /// </summary>
    struct APCustomTypeDescriptor : ICustomTypeDescriptor {
 
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        ///     Creates a new APCustomTypeDescriptor.  We pass in the custom type descriptor of
        ///     our base provider, which provides is with a default implementation of everything
        ///     we don't override.  for us, we want to override only the property mechanism.
        /// </summary>
        internal APCustomTypeDescriptor(ICustomTypeDescriptor parent, object instance) 
        {
            _parent = parent;
            _instance = FromObj(instance);
        }
 
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
 
        #region Public Methods
        
        /// <summary>
        ///     Returns a collection of properties for our object.  We first rely on base
        ///     CLR properties and then we attempt to match these with dependency properties.
        /// </summary>
        public PropertyDescriptorCollection GetProperties() 
        {
            return GetProperties(null);
        }
 
        /// <summary>
        ///     Returns a collection of properties for our object.  We first rely on base
        ///     CLR properties and then we attempt to match these with dependency properties.
        /// </summary>
        public PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
        {
 
            // Because attached properties can come and go at any time,
            // the set of properties we have here always needs to be rebuilt.
 
            // We have two code paths here based on filtered attributes.  An attribute
            // filter is just a notificaiton of a filter, it doesn't actually perform
            // the filter.  Because the default PropertyFilterAttribute is PropertyFilter.All,
            // it acts as a nice "don't care" in later filtering stages that TypeDescriptor
            // may apply.  That means that regardless of the filter value, we don't have
            // to fiddle with adding the attribute to the property descriptor.
 
            PropertyFilterOptions filter = PropertyFilterOptions.Valid | PropertyFilterOptions.SetValues;
 
            if (attributes != null) 
            {
                foreach (Attribute attr in attributes) 
                {
                    PropertyFilterAttribute filterAttr = attr as PropertyFilterAttribute;
                    if (filterAttr != null) 
                    {
                        filter = filterAttr.Filter;
                        break;
                    }
                }
            }
 
            if (filter == PropertyFilterOptions.None) 
            {
                return PropertyDescriptorCollection.Empty;
            }
 
            // First, get the set of all known registered properties in the
            // app domain.  GetRegisteredProperties caches its results and
            // will automatically re-fetch if new properties have been 
            // registered
            DependencyProperty[] registeredProperties = GetRegisteredProperties();
            Type instanceType = _instance.GetType();
 
            // Next, walk through them and see which ones can be attached to this
            // object.  If our filter is specifically SetValues, we can 
            // greatly shortcut the entire process by using the local value
            // enumerator.
 
            List<PropertyDescriptor> filteredProps;
             
            if (filter == PropertyFilterOptions.SetValues) 
            {
                LocalValueEnumerator localEnum = _instance.GetLocalValueEnumerator();
                filteredProps = new List<PropertyDescriptor>(localEnum.Count);
 
                while(localEnum.MoveNext())
                {
                    DependencyProperty dp = localEnum.Current.Property;
                    DependencyPropertyKind kind = DependencyObjectProvider.GetDependencyPropertyKind(dp, instanceType);
 
                    // For locally set values, we just want to exclude direct and internal properties.
                    if (!kind.IsDirect && !kind.IsInternal)
                    {
                        DependencyObjectPropertyDescriptor dpProp = DependencyObjectProvider.GetAttachedPropertyDescriptor(dp, instanceType);
                        filteredProps.Add(dpProp);
                    }
                }
            }
            else
            {
                filteredProps = new List<PropertyDescriptor>(registeredProperties.Length);
 
                foreach (DependencyProperty dp in registeredProperties) 
                {
                    bool addProp = false;
                    DependencyPropertyKind kind = DependencyObjectProvider.GetDependencyPropertyKind(dp, instanceType);
 
                    if (kind.IsAttached)
                    {
 
                        // Check bit combinations that would yield true in 
                        // any case.  For non-attached properties, they're all valid, so if
                        // the valid bit is set, we're done.
 
                        PropertyFilterOptions anySet = PropertyFilterOptions.SetValues | PropertyFilterOptions.UnsetValues;
                        PropertyFilterOptions anyValid = PropertyFilterOptions.Valid | PropertyFilterOptions.Invalid;
 
                        if ((filter & anySet) == anySet || (filter & anyValid) == anyValid) 
                        {
                            addProp = true;
                        }
 
                        if (!addProp && (filter & anyValid) != 0) 
                        {
                            bool canAttach = CanAttachProperty(dp, _instance);
                            addProp = canAttach ^ ((filter & anyValid) == PropertyFilterOptions.Invalid);
                        }
 
 
                        if (!addProp && (filter & anySet) != 0) 
                        {
                            bool shouldSerialize = _instance.ContainsValue(dp);
                            addProp = shouldSerialize ^ ((filter & anySet) == PropertyFilterOptions.UnsetValues);
                        }
                    }
                    else if ((filter & PropertyFilterOptions.SetValues) != 0 && _instance.ContainsValue(dp) && !kind.IsDirect && !kind.IsInternal)
                    {
 
                        // The property is not attached.  However, it isn't an internal DP and the user
                        // has requested set values.  See if the property is set on the object and include
                        // it if it is.
                        addProp = true;
                    }
 
                    if (addProp) 
                    {
                        DependencyObjectPropertyDescriptor dpProp = DependencyObjectProvider.GetAttachedPropertyDescriptor(dp, instanceType);
                        filteredProps.Add(dpProp);
                    }
                }
            }
 
            PropertyDescriptorCollection properties;
            properties = new PropertyDescriptorCollection(filteredProps.ToArray(), true);
            return properties;
        }
 
        // 
        // All methods below simply forward to the parent descriptor.
        //
 
        public AttributeCollection GetAttributes() { return _parent.GetAttributes(); }
        public string GetClassName() { return _parent.GetClassName(); }
        public string GetComponentName() { return _parent.GetComponentName(); }
 
        public TypeConverter GetConverter()
        {
            // We only support public type converters, in order to avoid asserts.
            TypeConverter typeConverter = _parent.GetConverter();
            if( typeConverter.GetType().IsPublic )
            {
                return typeConverter;
            }
            else
            {
                return null;
            }
        }
        public EventDescriptor GetDefaultEvent() { return _parent.GetDefaultEvent(); }
        public PropertyDescriptor GetDefaultProperty() { return _parent.GetDefaultProperty(); }
        public object GetEditor(Type editorBaseType) { return _parent.GetEditor(editorBaseType); }
        public EventDescriptorCollection GetEvents() { return _parent.GetEvents(); }
        public EventDescriptorCollection GetEvents(Attribute[] attributes) { return _parent.GetEvents(attributes); }
        public object GetPropertyOwner(PropertyDescriptor property) { return _parent.GetPropertyOwner(property); }
 
        #endregion Public Methods
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
 
        /// <summary>
        ///     This method determines if the given property can be attached
        ///     to the given instance.
        /// </summary>
        private bool CanAttachProperty(DependencyProperty dp, DependencyObject instance)
        {
            AttachInfo info = DependencyObjectProvider.GetAttachInfo(dp);
            return info.CanAttach(instance);
        }
 
        /// <summary>
        ///     Returns a dependency object for the given 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>
        ///     Returns an array of all registered properties declared in the
        ///     system.  
        /// </summary>
        private DependencyProperty[] GetRegisteredProperties() 
        {
            DependencyProperty[] registeredProperties;
 
            // We keep track of the global dependency property count.
            // Because DPs are never removed, we use this value to 
            // verify if our cache of registered properties is up to date.
            // If the count doesn't match our cached count, we re-fetch
            // all registered properties.
 
            lock(_syncLock) 
            {
                int cacheCnt = _dpCacheCount;
                int currentCnt = DependencyProperty.RegisteredPropertyCount;
 
                if (_dpCacheArray == null || cacheCnt != currentCnt) 
                {
                    List<DependencyProperty> dpList = new List<DependencyProperty>(currentCnt);
                    lock(DependencyProperty.Synchronized) 
                    {
                        foreach(DependencyProperty dp in DependencyProperty.RegisteredProperties) 
                        {
                            dpList.Add(dp);
                        }
 
                        _dpCacheCount = DependencyProperty.RegisteredPropertyCount;
                        _dpCacheArray = dpList.ToArray();
                    }
                }
 
                registeredProperties = _dpCacheArray;
            }
 
            return registeredProperties;
        }
 
        #endregion Private Methods
    
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
 
        #region Private Fields
        
 
        private ICustomTypeDescriptor _parent;
        private DependencyObject _instance;
 
        private static object _syncLock = new object();
 
        // Synchronized by "_syncLock"
        private static int _dpCacheCount = 0;
        
        // Synchronized by "_syncLock"
        private static DependencyProperty[] _dpCacheArray;
 
        #endregion Private Fields
    }
}