views:

1127

answers:

1

I have a set of custom PropertyDescriptor that I want to add categories too so they display in a more organized fashion in a PropertyGrid. I want each type of PropertyDescriptor to go into a specific Category.

I've tried using TypeDescriptor.AddAttributes() to add attributes to an existing PropertyDescriptor, but the category attribute is not added.

CategoryAttribute intrinsicPropertyCategory = new CategoryAttribute("Intrinsic Properties");
currentDescriptor = new IntrinsicPropertyDescriptor(def);
TypeDescriptor.AddAttributes(currentDescriptor, new Attribute[] { intrinsicPropertyCategory });

I've also tried using TypeDescriptor.AddAttributes() in the constructor for one of my PropertyDescriptors as shown below. But it doesn't work either.

public IntrinsicPropertyDescriptor(IntrinsicPropertyDef propDef): base(propDef.Key, propDef.Attributes)
{
this._type = propDef.Type;
this._key = propDef.Key;
this._readOnly = propDef.ReadOnly;

CategoryAttribute intrinsicPropertyCategory = new CategoryAttribute("Intrinsic Properties");
TypeDescriptor.AddAttributes(this, new Attribute[] { intrinsicPropertyCategory });
}

I'd rather not spend the time going in to detail of why I'm doing what I'm doing. But in the example above IntrinsicPropertyDef is a class that defines a property including a Name, Display Name and Type. So propDef.Attributes includes the DisplayNameAttribute.

An IntrinsicPropertyDef can be displayed with two different custom PropertyDescriptors IntrinsicPropertyDescriptor, and InferedIntrinsicPropertyDescriptor. Every IntrinsicPropertyDescriptor should have a category attribute "Intrinsic Properties", and every InferedIntrinsicPropertyDescriptor should have a category attribute "Inferred Intrinsic Properties".

+3  A: 

I believe you can just override the Category:

public override string Category { get {return "Foo";}}

For other scenarios; in general with a custom PropertyDescriptor, you specify attributes in the constructor. You'll need to expand the Attribute[] argument to include the CategoryAttribute. If you need to do any processing, you can use a static method - untested:

static Attribute[] AddCategory(Attribute[] attributes, string category) {
    Array.Resize(ref attributes, attributes.Length + 1);
    attributes[attributes.Length - 1] = new CategoryAttribute(category);
    return attributes;
}
public IntrinsicPropertyDescriptor(IntrinsicPropertyDef propDef)
     : base(propDef.Key, AddCategory(propDef.Attributes, "Foo"))
{...}


Also - note that for a PropertyDescriptor to be used, the system must find it... the resolution rules are:

  • for PropertyGrid, the TypeConverter provides the properties, defaulting to the properties for an instance (below)
  • for an instance:
    • ICustomTypeDescriptor is checked
    • otherwise it checks for a registered TypeDescriptionProvider for the instance or type
    • otherwise reflection is used
  • for a type:
    • it checks for a registered TypeDescriptionProvider for the type
    • otherwise reflection is used
  • for lists:
    • IListSource is checked and resolved to a list (processing continues)
    • ITypedList is checked
    • otherwise, the list type is checked for a non-object indexer - i.e. public SomeType this[int index] {get;}
      • if such is found, then the properties for the type SomeType are used, as defined above
    • otherwise, if the list is not empty, the properties of the first instance (list[0]) are used, as defined above
    • otherwise, metadata is not available
Marc Gravell
Yep the Category override you suggested worked perfectly. I knew about using the constructor, but didn't think to use a static method to simplify the code I would have to pass as an argument to the constructor. Thanks!
Eric Anastas