Categories are not shown in PropertyGrid for a collection<T>, when all the properties of <T> are read-only
See the question and my original answer on StackOverflowThis is not a bug, the property grid is designed that way. A component is considered as "immutable" if all its properties are read-only. In this case, it's wrapped into that funky "Value" wrapper property.
One solution is to declare a custom TypeDescriptionProvider on the class (or instance) that poses a problem. This provider will return a custom type descriptor instance which will add a dummy non-browsable (invisible to the property grid) non-readonly property, so the class is not considered "immutable" any more.
This is how you can use it, for example:
public Form1()
{
InitializeComponent();
// add the custom type description provider
var prov = new NeverImmutableProvider(typeof(TestClass3));
TypeDescriptor.AddProvider(prov, typeof(TestClass3));
// run the property grid
var c2 = new TestClass2();
propertyGrid1.SelectedObject = c2;
}
This is how it will look like, as expected:
And here is the code.
public class NeverImmutableProvider : TypeDescriptionProvider
{
public NeverImmutableProvider(Type type)
: base(TypeDescriptor.GetProvider(type))
{
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) => new MyTypeProvider(base.GetTypeDescriptor(objectType, instance));
private class MyTypeProvider : CustomTypeDescriptor
{
public MyTypeProvider(ICustomTypeDescriptor parent)
: base(parent)
{
}
public override PropertyDescriptorCollection GetProperties() => GetProperties(null);
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var props = new List<PropertyDescriptor>(base.GetProperties(attributes).Cast<PropertyDescriptor>());
props.Add(new MyProp());
return new PropertyDescriptorCollection(props.ToArray());
}
}
private class MyProp : PropertyDescriptor
{
public MyProp()
: base("dummy", new Attribute[] { new BrowsableAttribute(false) })
{
}
// this is the important thing, it must not be readonly
public override bool IsReadOnly => false;
public override Type ComponentType => typeof(object);
public override Type PropertyType => typeof(object);
public override bool CanResetValue(object component) => true;
public override object GetValue(object component) => null;
public override void ResetValue(object component) { }
public override void SetValue(object component, object value) { }
public override bool ShouldSerializeValue(object component) => false;
}
}
This solution has the advantage of not requiring any change to the original class. But it can have other implications in your code, so you really want to test it in your context. Also, note you can/should remove the provider once the grid has been closed.