Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Dynamic Property Attribute Evaluation at Run and Design Time

4.90/5 (12 votes)
21 Mar 20071 min read 1   731  
Ever wanted to hide, protect properties at design or runtime using other property value? Here is the answer!!!

Screenshot - image1.png

When Formatable is False, only Text is visible

Screenshot - image2.png

But when Formatable is True, Text is protected and Format shows up

Introduction

In my work, very often I have to resolve this kind of problem. So after some reading, copying, Googling, etc... wow, an idea comes up!

Background

Implementing ICustomTypeDescriptor interface on any class, we can provide properties to PropertyGrid control in a dynamic way. So let's code that interface.

C#
public class SomeClass : ICustomTypeDescriptor
{
    public EventDescriptorCollection GetEvents(Attribute[] attributes)
    {
        return TypeDescriptor.GetEvents(this, attributes, true);
    }
    /* ... so we arrive at ... */
    public PropertyDescriptorCollection GetProperties()
    {
        // use new implemented class to get properties
        return DynamicTypeDescriptor.GetProperties(this);
    }
} 

So, what is DynamicTypeDescriptor class here for? This class substitutes standard TypeDescriptor.GetProperties(...) behaviour.

C#
public static PropertyDescriptorCollection GetProperties(object component) 
{
    // hold all properties found
    PropertyInfo[] propsInfo = 
	component.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance);
    ArrayList list = new ArrayList(propsInfo.Length);
    // loop properties        
    foreach(PropertyInfo prop in propsInfo) 
    {
        // get all property attributes and check for "dynamics"
        ArrayList attributeList = new ArrayList();
        foreach(Attribute attrib in prop.GetCustomAttributes(true)) 
        {
            // if it is dynamic one, then evaluate it
            if (attrib is DependsOnPropertyAttribute) 
                attributeList.Add
		(((DependsOnPropertyAttribute)attrib).Evaluate(component));
            else
                attributeList.Add(attrib);
        }
        list.Add(new PropertyInfoDescriptor(prop, 
		(Attribute[])attributeList.ToArray(typeof(Attribute))));
    }
    return new PropertyDescriptorCollection
	((PropertyDescriptor[])list.ToArray(typeof(PropertyDescriptor)));
}

You may say, but what does DependsOnPropertyAttribute stand for?

C#
/// <summary>
/// Base class for all dynamic attributes.
/// Only properties can use this attribute.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public abstract class DependsOnPropertyAttribute : Attribute
{
    /// <summary>
    /// Create new instance of class
    /// </summary>
    /// <param name="expression">Property name</param>
    protected DependsOnPropertyAttribute(string property) : base()
    {
        _property = property;
        _index    = null;
    }
    /// <summary>
    /// Create new instance of class
    /// </summary>
    /// <param name="property">Property name</param>
    /// <param name="index">Property element index</param>
    protected DependsOnPropertyAttribute(string property, int index) 
    {
        _property = property;
        _index    = new object[] {index};
    }
        
    private string   _property;
    private object[] _index;
        
    /// <summary>
    /// Evaluate attribute using property container supplied
    /// </summary>
    /// <param name="container">Object that contains property to evaluate</param>
    /// <returns>Dynamically evaluated attribute</returns>
    public Attribute Evaluate(object container) 
    {
        return OnEvaluateComplete(RuntimeEvaluator.Eval(container, _property, _index));
    }
    /// <summary>
    /// Specific dynamic attribute check implementation
    /// </summary>
    /// <param name="value">Evaluated value</param>
    /// <returns>Dynamically evaluated attribute</returns>
    protected abstract Attribute OnEvaluateComplete(object value);

    private class RuntimeEvaluator 
    {
        public static object Eval(object container, string property, object[] index) 
        {
            PropertyInfo pInfo = container.GetType().GetProperty(property);
            if (pInfo != null)
                return pInfo.GetValue(container, index);
            
            return null;
        }
    }
}

This class defines abstract implementation for all DynamicAttributes. In my example, there are two implemented attributes: DynamicBrowsableAttribute and DynamicReadOnlyAttribute.

A sample implementation for abstract OnEvaluateComplete method is shown below:

C#
protected override Attribute OnEvaluateComplete(object value)
{
    Attribute output;
    try
    {
        // check if value is provided
        if (value == null) 
            value = false; // assume default
        // create attribute
        output = new ReadOnlyAttribute((bool)value);
    }
    catch
    {
        output = new ReadOnlyAttribute(false);
    }
    return output;
}

Using the Code

To use the code, you will have to create some class with public properties. On property that others depends on, insert RefreshProperties(RefreshProperties.All) attribute so property grid will be auto refreshed and will requery your properties. To provide dynamic functionality, implement ICustomTypeDescriptor interface in your class. From now on, you dynamically provide properties to property grid, easy yeah!

Insert DynamicAttribute on dynamic properties like this DynamicBrowsable("property name"), where property name is substituted with name of property that this one depends on. In my case property Format has DynamicBrowsable("Formatable") attribute set.

So now you will have to create an instance of your class and select it in the property grid control.

Points of Interest

You can see how to implement the ICustomTypeDescriptor interface, how to provide new Attributes and how to play with PropertyGrid control.

History

  • 21/03/2007 - First release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here