Dynamically set readonly attribute of properties in a PropertyGrid
Asked Answered
D

7

8

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 { }
    }
}
Dispensatory answered 28/10, 2013 at 17:3 Comment(1)
How many PropertyGrid do you use in your application? I think your purpose can be achieved if there is only 1 PropertyGrid used at a time, we still have to change the Attribute for type but before selecting an object, we will toggle the ReadOnly accordingly and that should do the trick.Seaddon
D
2

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.

Dispensatory answered 29/10, 2013 at 17:40 Comment(1)
Dead link to CodeProject article.Daugherty
S
0

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.

Snowwhite answered 28/10, 2013 at 17:23 Comment(0)
S
0

Add Readonly

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

Remove readonly

TextBoxID.Attributes.Remove("readonly");
Sly answered 21/6, 2017 at 11:30 Comment(0)
H
0

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);
Hooks answered 19/6, 2020 at 8:19 Comment(0)
M
0
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);
}
Mange answered 10/1, 2021 at 0:2 Comment(0)
R
0

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 );
    }
}
Romanism answered 13/6, 2022 at 8:26 Comment(1)
For me, under ASP.NET Core, <IsReadOnly>k__BackingField was the name not nameof(ReadOnlyAttribute.IsReadOnly).Ssr
F
0

Check this page out:
https://www.codeproject.com/Articles/152945/Enabling-disabling-properties-at-runtime-in-the-Pr

Quoted from above post:

it is important to statically define the ReadOnly attribute of every property of the class to whatever value you want. If not, changing the attribute at runtime that way will wrongly modify the attributes of every property of the class.

Use reflection to modify the "ReadOnly" attribute of the target property during runtime to achieve your goal. The problem that setting one property applies to all is because you need to set all properties in the same PropertyGrid object with ReadOnly attribute explicitly to avoid that problem.

[RefreshProperties(System.ComponentModel.RefreshProperties.All)]
[ReadOnly(false)]
public string Country
{
  get { return mCountry; }
  set
      {
      mCountry = value;
      PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())["State"];
      ReadOnlyAttribute attribute = (ReadOnlyAttribute)
                                    descriptor.Attributes[typeof(ReadOnlyAttribute)];
      FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly", 
                                       System.Reflection.BindingFlags.NonPublic | 
                                       System.Reflection.BindingFlags.Instance);
      fieldToChange.SetValue(attribute, mCountry != "U.S.");
      }
}

[ReadOnly(true)]
public string State
{
   get { return mState; }
   set { mState = value; }
}
Fraenum answered 10/10, 2022 at 6:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.