/// <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);
        }
        //------------------------------------------------------
        //
        //  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));
        }