Set a property to read-only or browsable to false custom attribute at runtime using System.Reflection

We had a problem recently where we wanted to set certain properties in a C# .NET class to read-only at runtime depending on the overarching product accessing the library so that it could be displayed in a property grid.

Our new product XIA Configuration Express would have certain features that would be unavailable due to the nature of the product compared to the main XIA Configuration Server product.

Custom attributes are designed to be assigned in the source code and not modified later so this requires some additional work with reflection to access the fields of the attribute or to add a new attribute to the collection if it doesn't already exist.

NOTE: This changes the read-only attribute for the class not an instance of the class so if you cannot have the property being read-only for a single instance of the class and not of another instance of the class.


   /// <summary>
   /// Sets the readonly attribute value for the specified property.
   /// </summary>
   /// <param name="sourceObject">The object on which the property exists.</param>
   /// <param name="propertyName">The name of the property.</param>
   /// <param name="isReadOnly">The readonly value to set.</param>
   /// <remarks>The read-only attribute must already exist on the property.</remarks>
   public static void SetPropertyReadOnly(Object sourceObject, String propertyName, bool isReadOnly)
   {
       try
       {
           PropertyDescriptor propertyDescriptor = ypeDescriptor.GetProperties(sourceObject.GetType())[propertyName];
           ReadOnlyAttribute readOnlyAttribute = null;
           foreach (Attribute attribute in propertyDescriptor.Attributes)
           {
               if (attribute is ReadOnlyAttribute)             
               {
                   readOnlyAttribute = (ReadOnlyAttribute)attribute;
                   break;
               }
           }
           if (readOnlyAttribute == null)
           {
               FieldInfo collectionField = (FieldInfo)propertyDescriptor.Attributes.GetType().GetField("_attributes", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase);
               Attribute[] attributesArray = (Attribute[])collectionField.GetValue(propertyDescriptor.Attributes);
               Array.Resize(ref attributesArray, attributesArray.Length + 1);
               attributesArray[attributesArray.Length - 1] = new ReadOnlyAttribute(isReadOnly);
               collectionField.SetValue(propertyDescriptor.Attributes, attributesArray);
               return;
           }
           FieldInfo readOnlyField = (FieldInfo)readOnlyAttribute.GetType().GetField(nameof(ReadOnlyAttribute.IsReadOnly), BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase);
           readOnlyField.SetValue(readOnlyAttribute, isReadOnly);
       }
       catch (Exception ex)
       {
           string sourceObjectType = sourceObject == null ? "null" : sourceObject.GetType().Name;
           throw new ReflectionSupportException(String.Format(Resources.ReflectionSupport.SetPropertyReadOnlyException, propertyName, sourceObjectType, ex.Message), ex);
       }
   }

Comments

Popular posts from this blog

Windows Server 2016, 2019, 2022, Windows 10 and Windows 11: Date and time "Some settings are managed by your organization".

TFTPD32 or TFTPD64 reports Bind error 10013 An attempt was made to access a socket in a way forbidden by its access permissions.

Windows Server 2019 desktop icons such as My Computer, Windows cannot access the specified device, path, or file.