PropertyGrid readonly property on object-level

reflective_mind picture reflective_mind · May 7, 2012 · Viewed 14k times · Source

I want to display multiple instances of one class in my PropertyGrid. The class looks like this:

public class Parameter
{
    [Description("the name")]
    public string Name { get; set; }

    [Description("the value"), ReadOnly(true)]
    public string Value { get; set; }

    [Description("the description")]
    public string Description { get; set; }
}

I have many instances of that class in a TreeView. When I select one of them in my TreeView, the properties show up in the PropertyGrid as expected. So far so good, but I want to customise this behaviour in the following way:

For each single instance I want to be able to prevent the user from modifying a specific property. By setting ReadOnly(true) within my class (as you can see in the example above), all Value properties will be disabled on a class-level.

After some research I found the following solution which gives me the opportunity to enable/disable a specific property at runtime:

PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this)["Value"];

ReadOnlyAttribute attr = 
        (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];

FieldInfo isReadOnly = attr.GetType().GetField(
        "isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);

isReadOnly.SetValue(attr, false);

This approach works just fine but unfortunately also on class-level only. This means if I set the Value's isReadOnly to false, all of my Parameter-objects have the Value property writeable. But I want this ONLY on that one particular object (thus object-level). I really don't want to create separate classes for read/write and readonly properties.

As I am running out of ideas, your help is much appreciated :)

Thanks in advance!

EDIT: I need the readonly properties to be grayed-out, so the user can see that it's not allowed or possible to edit them.

Answer

Adriano Repetti picture Adriano Repetti · May 7, 2012

EDIT: Linked article has been removed (I hope just temporary). You can fine a viable alternative in answers to How to add property-level Attribute to the TypeDescriptor at runtime?. Basically you have to add (at run-time) ReadOnlyAttribute through a TypeDescriptor for that property.


Take a look at this old but nice article on CodeProject, it contains a lot of useful tools for the PropertyGrid.

Basically you provide a class or a delegate that will be used to get the attributes of your properties. Because it will be invoked passing the instance of the object you want to get attributes for then you'll be able to return (or not) the ReadOnlyAttribute with a per object basis. Shortly: apply a PropertyAttributesProviderAttribute to your property, write your own provider and replace attributes from the PropertyAttributes collection based on the object itself (and not on the class)