//------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods /// <summary> /// This method returns an attached property descriptor for the given DP and target type. /// </summary> internal static DependencyObjectPropertyDescriptor GetAttachedPropertyDescriptor(DependencyProperty dp, Type targetType) { DependencyObjectPropertyDescriptor dpProp; PropertyKey key = new PropertyKey(targetType, dp); lock (_propertyMap) { if (!_propertyMap.TryGetValue(key, out dpProp)) { dpProp = new DependencyObjectPropertyDescriptor(dp, targetType); _propertyMap[key] = dpProp; } } return(dpProp); }
//------------------------------------------------------ // // Constructors // //------------------------------------------------------ #region Constructors /// <summary> /// The ctor for this class needs to be public because it is created /// by TypeDescriptor using reflection. /// </summary> public DependencyObjectProvider() : base(TypeDescriptor.GetProvider(typeof(DependencyObject))) { // We keep a lot of caches around. When TypeDescriptor gets a refresh // we clear our caches. We only need to do this if the refresh // contains type information, because we only keep static per-type // caches. TypeDescriptor.Refreshed += delegate(RefreshEventArgs args) { if (args.TypeChanged != null && typeof(DependencyObject).IsAssignableFrom(args.TypeChanged)) { ClearCache(); DependencyObjectPropertyDescriptor.ClearCache(); DPCustomTypeDescriptor.ClearCache(); DependencyPropertyDescriptor.ClearCache(); } }; }
/// <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); }
//------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods /// <summary> /// This method returns an attached property descriptor for the given DP and target type. /// </summary> internal static DependencyObjectPropertyDescriptor GetAttachedPropertyDescriptor(DependencyProperty dp, Type targetType) { DependencyObjectPropertyDescriptor dpProp; PropertyKey key = new PropertyKey(targetType, dp); lock(_propertyMap) { if (!_propertyMap.TryGetValue(key, out dpProp)) { dpProp = new DependencyObjectPropertyDescriptor(dp, targetType); _propertyMap[key] = dpProp; } } return dpProp; }
//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods // // Creates the property descriptor collection for this type. The return // value is all properties that are exposed on this type. // private PropertyDescriptorCollection CreateProperties() { PropertyDescriptorCollection baseProps = _parent.GetProperties(); List<PropertyDescriptor> newDescriptors = new List<PropertyDescriptor>(baseProps.Count); for (int idx = 0; idx < baseProps.Count; idx++) { PropertyDescriptor prop = baseProps[idx]; DependencyObjectPropertyDescriptor dpProp; DependencyProperty dp = null; bool inMap; lock(_propertyMap) { inMap = _propertyMap.TryGetValue(prop, out dpProp); } if (inMap && dpProp != null) { // We need to verify that this property descriptor contains the correct DP. // We can get the wrong one if a descendant of the type introducing the // CLR property associates a different DP to itself with the same name. dp = DependencyProperty.FromName(prop.Name, _objectType); if (dp != dpProp.DependencyProperty) { dpProp = null; } else { // We also need to verify that the property metadata for dpProp matches // our object type's metadata if (dpProp.Metadata != dp.GetMetadata(_objectType)) { dpProp = null; } } } if (dpProp == null) { // Either the property wasn't in the map or the one that was in there // can't work for this type. Make a new property if this property is // backed by a DP. Since we only care about direct dependency properties // we can short circuit FromName for all properties on types that do // not derive from DependencyObject. Also, if we already got a DP out of // the map we can skip the dependency object check on the property, since // the fact that we got a dp means that there used to be something in the map // so the component type is already a DependencyObject. if (dp != null || typeof(DependencyObject).IsAssignableFrom(prop.ComponentType)) { if (dp == null) { dp = DependencyProperty.FromName(prop.Name, _objectType); } if (dp != null) { dpProp = new DependencyObjectPropertyDescriptor(prop, dp, _objectType); } } // Now insert the new property in our map. Note that we will // insert a null value into the map if this property descriptor // had no backing DP so we don't go through this work twice. if (!inMap) { lock(_propertyMap) { _propertyMap[prop] = dpProp; } } } // If we found a dependency property desecriptor for this property, // use it as our new property. if (dpProp != null) { prop = dpProp; } newDescriptors.Add(prop); } return new PropertyDescriptorCollection(newDescriptors.ToArray(), true); }
//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods // // Creates the property descriptor collection for this type. The return // value is all properties that are exposed on this type. // private PropertyDescriptorCollection CreateProperties() { PropertyDescriptorCollection baseProps = _parent.GetProperties(); List <PropertyDescriptor> newDescriptors = new List <PropertyDescriptor>(baseProps.Count); for (int idx = 0; idx < baseProps.Count; idx++) { PropertyDescriptor prop = baseProps[idx]; DependencyObjectPropertyDescriptor dpProp; DependencyProperty dp = null; bool inMap; lock (_propertyMap) { inMap = _propertyMap.TryGetValue(prop, out dpProp); } if (inMap && dpProp != null) { // We need to verify that this property descriptor contains the correct DP. // We can get the wrong one if a descendant of the type introducing the // CLR property associates a different DP to itself with the same name. dp = DependencyProperty.FromName(prop.Name, _objectType); if (dp != dpProp.DependencyProperty) { dpProp = null; } else { // We also need to verify that the property metadata for dpProp matches // our object type's metadata if (dpProp.Metadata != dp.GetMetadata(_objectType)) { dpProp = null; } } } if (dpProp == null) { // Either the property wasn't in the map or the one that was in there // can't work for this type. Make a new property if this property is // backed by a DP. Since we only care about direct dependency properties // we can short circuit FromName for all properties on types that do // not derive from DependencyObject. Also, if we already got a DP out of // the map we can skip the dependency object check on the property, since // the fact that we got a dp means that there used to be something in the map // so the component type is already a DependencyObject. if (dp != null || typeof(DependencyObject).IsAssignableFrom(prop.ComponentType)) { if (dp == null) { dp = DependencyProperty.FromName(prop.Name, _objectType); } if (dp != null) { dpProp = new DependencyObjectPropertyDescriptor(prop, dp, _objectType); } } // Now insert the new property in our map. Note that we will // insert a null value into the map if this property descriptor // had no backing DP so we don't go through this work twice. if (!inMap) { lock (_propertyMap) { _propertyMap[prop] = dpProp; } } } // If we found a dependency property desecriptor for this property, // use it as our new property. if (dpProp != null) { prop = dpProp; } newDescriptors.Add(prop); } return(new PropertyDescriptorCollection(newDescriptors.ToArray(), true)); }