LOGIN    REGISTER    YOUR CART

homepage
VisualHint's blog
2007
May10

How to setup a truly dynamic combobox in the PropertyGrid?

SPG offers some great ways to build dynamic properties. But often this is a mix of declarative code and dynamic calls to the SPG API to alter the content or the behavior of the grid. However in some cases a complete dynamic solution is needed. For example, some classes may be generated by external tools and it’s impossible or not desired to decorate their properties with attributes. In other contexts, enumerations are not welcome because their content is too ... hardcoded. This article proposes a way to setup a property based on a single integer identifier and a collection of strings to display in the combobox. To make it a little more complex, our combobox will be the unit of another numerical property.

The first thing we need is a new TypeConverter to instruct the grid that we have a set of values to display in a combobox. The two involved methods to override are GetStandardValuesSupported() and GetStandardValues(). Of course we will derive our converter from the Int32Converter. Here is the code:

public class MyIntConverter : Int32Converter
{
    public override bool GetStandardValuesSupported(
        ITypeDescriptorContext context)
    {
        return true;
    }

    public override TypeConverter.StandardValuesCollection
        GetStandardValues(ITypeDescriptorContext context)
    {
        return new StandardValuesCollection( ??? );
    }
}

Notice the question marks? This is where you pass the allowed identifiers for your list. There are several ways to get a dynamic list from the TypeConverter. For example you could use the passed context (which in SPG is of type PropertyTypeDescriptorContext) to get the list from the target instance. Here we will create a new attribute that will contain the initial list of integers. The definition of this attribute could be:

public class AllowedIntsAttribute : Attribute
{
    public int[] AllowedInts;

    public AllowedIntsAttribute(params int[] allowedInts)
    {
        AllowedInts = allowedInts;
    }
}

GetStandardValues can then be written like this:

public override TypeConverter.StandardValuesCollection
    GetStandardValues(ITypeDescriptorContext context)
{
    PropertyTypeDescriptorContext c = (PropertyTypeDescriptorContext)context;
    AllowedIntsAttribute attr = (AllowedIntsAttribute)c.OwnerPropertyEnumerator.Property.
        GetValue(PropertyUnitLook.UnitValue).GetAttribute(typeof(AllowedIntsAttribute));
    return new StandardValuesCollection(attr.AllowedInts);
}

And here is how all is connected when the grid is filled:

propEnum = AppendManagedProperty(rootEnum, _id++, "Width", typeof(int), 2, "");
propEnum.Property.Look = new PropertyUnitLook();
propEnum.Property.Feel = GetRegisteredFeel(PropertyGrid.FeelEditUnit);
    
propEnum.Property.AddManagedValue(PropertyUnitLook.UnitValue, typeof(int),
    10, new AllowedIntsAttribute(1,2),
    new TypeConverterAttribute(typeof(MyIntConverter)),
    new PropertyValueDisplayedAsAttribute(new string[2]
        { "feet", "meters" }));

If the list of identifiers and the list of displayed strings need to be modified, this is possible in SPG. Attributes set dynamically can be accessed with PropertyValue.GetAttribute() and modified if their API enable to do so.

I hope you will appreciate this tutorial. If something is unclear or you need more explanations, please leave a comment.