PropertyGrid readonly property on object-level
Asked Answered
B

2

9

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.

Bremble answered 7/5, 2012 at 12:17 Comment(0)
P
5

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)

Parapodium answered 7/5, 2012 at 12:49 Comment(7)
I've read the entire document but still don't understand your idea. Could you provide me with a small code example to make it a bit clearer? Thanks for your time :)Bremble
See the Dynamic usage paragraph in that article. 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).Parapodium
As I can see in the source code, this requires to use the authors custom PropertyGrid implementation to get it working? In my case I have to use an existing PropertyGrid that is within a DLL (which I cannot modify).Bremble
@Inferno no, everything is done with custom attributes and type descriptors.Parapodium
Figured it out finally. I forgot to put the TypeConverterAttribute at the top of my class. Still very much code to solve such a little problem...but the main thing is that it's working now. Thanks!Bremble
The codeproject article was deleted at 8 Dec 2014 =/Loney
OK (but I admit I have no idea how to start a chat on SO...)Parapodium
K
1

You can wrap the object with a custom type descriptor, but I think that would be overkill, because you have to create a new typedescriptor-derived class.

So, a simplest solution would be to have a flag, something like:

public class Parameter 
{ 
    private string thevalue;

    [Browsable(false)]
    public bool CanEditValue { get; set; }

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

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

    [Description("the value"), ReadOnly(true)] 
    public string Value { 
        get { return this.thevalue; }
        set { if (this.CanEditValue) this.thevalue = value; } 
    }
}
Krakau answered 7/5, 2012 at 12:47 Comment(2)
hello Jaime, your suggestion with doubling properties would be acceptable because it is really simple. I tried this and unfortunately checking within the setter does not disable property editing like ReadOnly(true)...so it's not grayed-out. users will think it is changable but their input will just be discarded. any other less complex ideas? :)Bremble
I am afraid that you unique option is to derive a property descriptor and implement the conditional IsReadOnly property getter.Krakau

© 2022 - 2024 — McMap. All rights reserved.