I have a user control in .NET with 2 new property
Prop1: Boolean
Prop2: String
I want to make Prop2 READONLY in the property grid when the user set Prop1 to false.
If you want to make the property appearance read-only (gray) at run-time based on some criteria, you need to assign a CustomTypeDescriptor
to your class which provides metadata about your class for property grid.
PropertyGrid
control uses the type descriptor of the object to extract information about its properties to show. The type descriptor, returns a list of PropertyDescriptor
objects as list of properties. Each PropertyDescriptor
contains some methods and properties to return display name, description, and other information about the property. IsReadOnly
property of the PropertyDescriptor
is responsible to inform the PropertyGrid
if the property should be read only.
Example
In the following example, I created a class containing two properties. Editable
and StringProperty
. If the Editable
is true
then StringProperty
is editable, otherwise it would be read-only and will be shown as gray in PropertyGrid
.
MyPropertyDescriptor
It's responsible to provide metadata for property. When implementing this class, for most properties, we will use the trivial implementation which uses original property's implementations, but for IsReadOnly
we will decide based on the value of Editable
property of the owner object:
using System;
using System.ComponentModel;
using System.Linq;
public class MyPropertyDescriptor : PropertyDescriptor
{
PropertyDescriptor p;
SampleClass o;
public MyPropertyDescriptor(PropertyDescriptor originalProperty, SampleClass owenr)
: base(originalProperty) { p = originalProperty; o = owenr; }
public override bool CanResetValue(object component)
{ return p.CanResetValue(component); }
public override object GetValue(object component) { return p.GetValue(component); }
public override void ResetValue(object component) { p.ResetValue(component); }
public override void SetValue(object component, object value)
{ p.SetValue(component, value); }
public override bool ShouldSerializeValue(object component)
{ return p.ShouldSerializeValue(component); }
public override AttributeCollection Attributes { get { return p.Attributes; } }
public override Type ComponentType { get { return p.ComponentType; } }
public override bool IsReadOnly { get { return !o.Editable; } }
public override Type PropertyType { get { return p.PropertyType; } }
}
MyTypeDescriptor
It's responsible to provide a list of properties for the object. For StringProperty
which we are going to change its behavior at run-time, we will return a MyPropertyDescriptor
:
using System;
using System.ComponentModel;
using System.Linq;
public class MyTypeDescriptor : CustomTypeDescriptor
{
ICustomTypeDescriptor d;
SampleClass o;
public MyTypeDescriptor(ICustomTypeDescriptor originalDescriptor, SampleClass owner)
: base(originalDescriptor) { d = originalDescriptor; o = owner; }
public override PropertyDescriptorCollection GetProperties()
{ return this.GetProperties(new Attribute[] { }); }
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
.Select(p => p.Name == "StringProperty" ? new MyPropertyDescriptor(p, o) : p)
.ToArray();
return new PropertyDescriptorCollection(properties);
}
}
MyTypeDescriptionProvider
It's responsible to return a type descriptor for your object, when someone (like property grid) request type description:
using System;
using System.ComponentModel;
public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
public MyTypeDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(object))) { }
public override ICustomTypeDescriptor GetTypeDescriptor(Type type, object o)
{
ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(type, o);
return new MyTypeDescriptor(baseDescriptor, (SampleClass)o);
}
}
SampleClass
At last, the implementation of the class:
using System;
using System.ComponentModel;
[TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
public class SampleClass
{
[RefreshProperties(RefreshProperties.All)]
public bool Editable { get; set; }
string sp;
public string StringProperty
{
get { return sp; }
set
{
if (Editable)
sp = value;
}
}
}
Result
Further reading
You can read about some other solutions in the following post: