Question

I have a PropertyGrid that I used to display the properties in a helper class. I assign the helper class to the PropertyGrid like this:

myPropertyGrid.SelectedObject = mySettingsHelper;

In the helper class I assign the ReadOnlyAttribute at design time like this:

[DisplayName("DisplayExA"),
Description("DescriptionExA"),
ReadOnlyAttribute(true)]
public string PropertyA { get; set; }

[DisplayName("DisplayExB"),
Description("DescriptionExB"),
ReadOnlyAttribute(false)]
public string PropertyB { get; set; }

[DisplayName("DisplayExC"),
Description("DescriptionExC"),
ReadOnlyAttribute(true)]
public string PropertyC { get; set; }

But now I need to be able to change this attribute on individual properties dynamically during runtime. Based on certain criteria some of these properties may need to be read-only or not read-only. How would I make the change dynamically at runtime?

EDIT:

I tried the following code but this sets the ReadOnly attribute for every instance of the object! I want to do it per object. Sometimes one object might have PropertyA read-only while a second object has PropertyA to not be read-only.

public static class PropertyReadOnlyHelper
{
    public static  void SetReadOnly(object container, string name, bool value)
    {
        try
        {
            PropertyDescriptor descriptor = TypeDescriptor.GetProperties(container.GetType())[name];
            ReadOnlyAttribute attribute = (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];
            FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly",
                                                System.Reflection.BindingFlags.NonPublic |
                                                System.Reflection.BindingFlags.Instance);
            fieldToChange.SetValue(attribute, value);
        }
        catch { }
    }
}
Was it helpful?

Solution

I was able to do exactly what I need (object level assignment of the read-only attribute) using the library from this CodeProject article. What is nice is that it enables me to still use the .NET PropertyGrid and just use the custom attributes to handle the dynamic settings.

OTHER TIPS

Use reflection to get the instance reference of the ReadOnlyAttribute class, then toggle the IsReadOnly property on that instance. Finally, re-select the item in the PropertyGrid if needed by settings its SelectedObjects to null and then resetting it. You might be able to do this using the PropertyGrid RefreshTabs method too, I'm not sure.

EDIT:

Unfortunately the IsReadOnly property itself is read only... in this case we'd have to use reflection to change the value of the backing field for the IsReadOnly property.

Add Readonly

TextBoxID.Attributes.Add("readonly","true");

Remove readonly

TextBoxID.Attributes.Remove("readonly");

Dynamically setting browsable or readonly attribute of a property in a PropertyGrid is often needed together and also they are similiar jobs

After a few touches, the great answer of Reza Aghaei about "Hide some properties in PropertyGrid at run-time" is also applicable for manipulating the readonly attribute.

public class CustomObjectWrapper : CustomTypeDescriptor
{
    public object WrappedObject { get; private set; }
    public List<string> BrowsableProperties { get; private set; }
    public List<string> ReadonlyProperties { get; private set; }

    public CustomObjectWrapper(object o)
        : base(TypeDescriptor.GetProvider(o).GetTypeDescriptor(o))
    {
        WrappedObject = o;
        BrowsableProperties = new List<string>() { "Text", "BackColor" };
        ReadonlyProperties = new List<string>() { "Font" };
    }
    public override PropertyDescriptorCollection GetProperties()
    {
        return this.GetProperties(new Attribute[] { });
    }
    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        List<PropertyDescriptor> result = new List<PropertyDescriptor>();

        IEnumerable<PropertyDescriptor> properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
            .Where(p => BrowsableProperties.Contains(p.Name));//unbrowsable filtering

        foreach (var p in properties)
        {
            PropertyDescriptor resultPropertyDescriptor = null;

            //handle being readonly 
            if (ReadonlyProperties.Contains(p.Name))
            {
                List<Attribute> atts = p.Attributes.Cast<Attribute>().ToList();
                atts.RemoveAll(a => a.GetType().Equals(typeof(ReadOnlyAttribute)));//remove any readonly attribute
                atts.Add(new ReadOnlyAttribute(true));//add "readonly=true" attribute

                resultPropertyDescriptor = TypeDescriptor.CreateProperty(WrappedObject.GetType(), p, atts.ToArray());
            }
            else
            {
                resultPropertyDescriptor = TypeDescriptor.CreateProperty(WrappedObject.GetType(), p, p.Attributes.Cast<Attribute>().ToArray());
            }

            if (resultPropertyDescriptor != null)
                result.Add(resultPropertyDescriptor);

        }

        return new PropertyDescriptorCollection(result.ToArray());
    }
}

and the usage:

propertyGrid1.SelectedObject = new CustomObjectWrapper(myobject);
Please try the code below.


  
[CategoryAttribute("2. LINE"), DisplayNameAttribute("Spline Line Tension"),
 DescriptionAttribute("Chart's Spline Line Tension "), ReadOnlyAttribute(false)]
public float _PG_SplineTension
{
    get
    {
        bool lbReadyOnly = true;
        SetPropertyReadOnly("_PG_SplineTension", lbReadyOnly);
        return this.cfSplineTension;
   }
    set { this.cfSplineTension = value; }
}



private void SetPropertyReadOnly(string lsProperty, bool lbIsReadOnly)
{
    PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())[lsProperty];
    ReadOnlyAttribute attribute = (ReadOnlyAttribute)

    descriptor.Attributes[typeof(ReadOnlyAttribute)];
    FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly",
        System.Reflection.BindingFlags.NonPublic |
        System.Reflection.BindingFlags.Instance);
    fieldToChange.SetValue(attribute, lbIsReadOnly);
}

Thanks a lot. Based on the answers I came with the following code that works just fine:

private void SetReadonly ( object o, bool value )
{
  foreach ( PropertyInfo property in o.GetType().GetProperties() )
    if ( property.GetCustomAttribute<ReadOnlyAttribute>() != null )
    {
      Attribute readOnly = TypeDescriptor.GetProperties( o.GetType() )[property.Name].Attributes[typeof( ReadOnlyAttribute )];
      readOnly.GetType().GetField( nameof( ReadOnlyAttribute.IsReadOnly ), BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase ).SetValue( readOnly, value );
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top