Modifying structure property in a PropertyGrid

.net, propertygrid, winforms

Solution

You need a special TypeConverter that overrides TypeConverter.GetCreateInstanceSupported because otherwise copy-by-value/boxing magic happens behind the scene in the way the property grid handles all this.

Here is one that should work for most value types. You declare it like this:

[TypeConverter(typeof(ValueTypeTypeConverter<SomeStruct>))]
public struct SomeStruct
{
    public int StructField { get; set; }
}


public class ValueTypeTypeConverter<T> : ExpandableObjectConverter where T : struct
{
    public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
    {
        return true;
    }

    public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
    {
        if (propertyValues == null)
            throw new ArgumentNullException("propertyValues");

        T ret = default(T);
        object boxed = ret;
        foreach (DictionaryEntry entry in propertyValues)
        {
            PropertyInfo pi = ret.GetType().GetProperty(entry.Key.ToString());
            if (pi != null && pi.CanWrite)
            {
                pi.SetValue(boxed, Convert.ChangeType(entry.Value, pi.PropertyType), null);
            }
        }
        return (T)boxed;
    }
}

Note it doesn't support pure field-only structs, only the one with properties, but the ExpandableObjectConverter doesn't support these either, it would require more code to do it.

Problem

Why `SomeClass.ClassField.StructField` property doesn't change in a `propertyGrid`? It seems, `propertyGrid` doesn't call `SomeClass.ClassField.set` after `SomeStruct` instance has been changed. But same code works well with `Point` instead of `SomeStruct`. ``` [TypeConverter(typeof(ExpandableObjectConverter))] public struct SomeStruct { private int structField; public int StructField { get { return structField; } set { structField = value; } } public override string ToString() { return "StructField: " + StructField; } } [TypeConverter(typeof(ExpandableObjectConverter))] public sealed class SomeClass { public SomeStruct ClassField { get; set; } } ... var someClass = new SomeClass { ClassField = new SomeStruct { StructField = 42 } }; propertyGrid.SelectedObject = someClass; ```

Original source