Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C#

Dynamic Property Attribute Evaluation at Run and Design Time

Rate me:
Please Sign up or sign in to vote.
4.90/5 (12 votes)
21 Mar 20071 min read 69.8K   728   49   8
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


Written By
Web Developer
Italy Italy
Working as consultant for "IT punto COM", italian company situated in Milan.
14 years of professional programming and software designing experience.
Currently interested in VB.NET, C# programming.

Comments and Discussions

 
GeneralProblem with implementaion of this code in ASP Pin
dhonuevo10-Nov-10 23:38
dhonuevo10-Nov-10 23:38 
GeneralNewer solution with greater flexibility Pin
Mizan Rahman23-Feb-09 3:14
Mizan Rahman23-Feb-09 3:14 
QuestionAlready done 2.5 years ago? Pin
wout de zeeuw21-Mar-07 17:38
wout de zeeuw21-Mar-07 17:38 
AnswerRe: Already done 2.5 years ago? Pin
mileni21-Mar-07 23:21
mileni21-Mar-07 23:21 
GeneralRe: Already done 2.5 years ago? Pin
wout de zeeuw22-Mar-07 3:47
wout de zeeuw22-Mar-07 3:47 
GeneralRe: Already done 2.5 years ago? Pin
vince_formica17-Oct-07 5:43
vince_formica17-Oct-07 5:43 
I liked your step-by-step explanation better. I integrated this into my own propertygrid rather quickly. Just had to convert the C# to vb.net, and it worked on first compile.

Thanks!
GeneralRe: Already done 2.5 years ago? Pin
wout de zeeuw17-Oct-07 5:56
wout de zeeuw17-Oct-07 5:56 
GeneralRe: Already done 2.5 years ago? Pin
crabby.net10-Jan-08 10:14
crabby.net10-Jan-08 10:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.