ICustomTypeDescriptor.GetProperties

  • Thread starter Thread starter Christopher Wells
  • Start date Start date
C

Christopher Wells

I want to implement a class derived from ICustomTypeDescriptor.

My current implementation of the overloaded GetProperties methods look like
this:

public PropertyDescriptorCollection GetProperties(Attribute[]
attributes)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(m_thing_with_properties,attributes,true);
return hack_property_display_names(properties);
}

PropertyDescriptorCollection
System.ComponentModel.ICustomTypeDescriptor.GetProperties()
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(m_thing_with_properties,true);
return hack_property_display_names(properties);
}

where "hack_property_display_names" is a private method that alters the
PropertyDescriptorCollection.

My problem with this implementation is that I'm not cacheing/controlling the
PropertyDescriptorCollection. I would like to:

* Construct my PropertyDescriptorCollection in my constructor and save it
as private member data
* Change the GetProperties() methods to return thhis private data.

My problem with doing this is that I don't understand the "Attribute[]
attributes" parameter. The help says only that it acts as a filter. If I
have a PropertyDescriptorCollection, what does it mean to (or, how do I)
apply a "Attribute[] attributes" filter to it?
 
Attributes are a way of categorizing members within a type. They can be system defined attributes or your own custom attributes

Attributes can be applied to members including properties to allow you to add another way of storing information for a member

Eg. [System.Diagnostics.DebuggerStepThrough()] is an attribute.
o
[Serializable] or [NonSerialized

So if you are getting a property and only want those with the Attributes of "NonSerialized" you would load
an array of attributes e

GetProperties(new Attribute[] {YourAttributeObject}

that you want. And when you get those members from the type only get those that matc
the attributes that were specificed.

If you are overriding this you need to check the attributes of each member that you are going to return to see if they were specificed in the Attributes[] array.
 
Mike in Paradise said:
Attributes are a way of categorizing members within a type. They can be
system defined attributes or your own custom attributes.
Attributes can be applied to members including properties to allow you to
add another way of storing information for a member.
Eg. [System.Diagnostics.DebuggerStepThrough()] is an attribute.
or
[Serializable] or [NonSerialized]

So if you are getting a property and only want those with the Attributes
of "NonSerialized" you would load
an array of attributes eg

GetProperties(new Attribute[] {YourAttributeObject} )

that you want. And when you get those members from the type only get those that match
the attributes that were specificed.

If you are overriding this you need to check the attributes of each member
that you are going to return to see if they were specificed in the
Attributes[] array.

If the "Attribute[] attributes" parameter that is passed to
ICustomTypeDescriptor.GetProperties contains more than one attribute, then
is the filter looking for properties which contain *all* of the specified
attributes, or it is looking for properties that contain *any* of the
specified attributes? For example, if "Attribute[] attributes" is the filter
parameter passed to ICustomTypeDescriptor.GetProperties, and if
"AttributeCollection property_attributes" are the attributes of each
property retrieved using "property.Attributes", then should I use "if
(property_attributes.Contains(attributes))" or should I use
"foreach(Attribute attribute in attributes) if
(property_attributes.Contains(attribute))"?

If I get "property.Attributes" for a typical property and look the resulting
AttributesCollection using the debugger QuickWatch, then its Count=2 and its
"attributes" data member contains only two attributes (CLSCompliantAttribute
and ReadOnlyAttribute). If I then call the AttributesCollection.Contains
method, to see whether it contains a BrowsableAttribute, then the Contains
method returns true! This may be because BrowsableAttribute is contained as
one of the attributes in the undocumented defaultAttributes member of
AttributesCollection. If I create a new custom attribute, it doesn't seem to
be added to the undocumented defaultAttributes member of the
AttributesCollection returned by property.Attributes for all properties.
Therefore, it seems to me that the correct implementation is to not use the
apparently-unreliable AttributesCollection.Contains at all, but instead to
test each "public" attribute in the collection, for example as follows:

public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
PropertyDescriptorCollection returned_properties = new
PropertyDescriptorCollection(new PropertyDescriptor[0]);
foreach (PropertyDescriptor property in m_properties)
{
AttributeCollection property_attributes = property.Attributes;
if (property_attributes_match_attributes_filter(attributes,
property_attributes))
{
returned_properties.Add(property);
}
}
return returned_properties;
}

static bool property_attributes_match_attributes_filter(Attribute[]
attributes_filter,AttributeCollection property_attributes)
{
//see whether any attribute in the attributes_filter match an attribute in
the property_attributes collection
bool b_attribute_specified = false;
foreach (Attribute attribute in attributes_filter)
{
//we cannot use property_attributes.Contains because if Contains returns
false
//we don't know whether that's because the attribute was specified but
with the
//wrong value, or whether it's because it wasn't specified at all (not
even
//specified in the undocumented property_attributes.defaultAttributes
member).
// if (property_attributes.Contains(attribute))
// return true;

//Therefore instead test each public (non-default) property attribute
individually.
foreach (Attribute property_attribute in property_attributes)
{
if (property_attribute.TypeId == attribute.TypeId)
b_attribute_specified = true;
if (property_attribute.Match(attribute))
return true;
}
}
if (b_attribute_specified)
{
//an attribute was specified but didn't Match, therefore return false
return false;
}
//else attribute wasn't specified in the property, so return true if the
specified
//attribute is the default value for this type of attribute
foreach (Attribute attribute in attributes_filter)
{
if (attribute.IsDefaultAttribute())
return true;
}
//not specified and not default
return false;
}
 
Back
Top