4

I'm trying to edit complex objects in a PropertyGrid control. I add the ExpandableObjectConverter (or my own subclass when I need it) as the TypeConverter and it works fine.

The one thing I can't seem to figure out is this. The object itself will have its .ToString() representation next to it in the Grid. Then when I expand the object the attributes have the same. All can be editable. I want to disable editing of the ToString() object field, but keep the attributes editable.

So in the PropertyGrid it would look like this;

+ Color      {(R,G,B,A) = (255,255,255,255)}  --uneditable
     Alpha   255                              --editable
     Blue    255                              --editable
     Green   255                              --editable
     Red     255                              --editable

So far I haven't found a way to do this. If I try to make it ReadOnly the entire object becomes read only. If I specify my own ExpandableObjectConverter and state it cannot convert from a string, if the string is edited in the PropertyGrid it will still try to cast and then fail.

I essentially just want it so I can stop end users from editing the string and forcing them to edit the individual attributes instead, so that I don't have to write a string parser for every single class.

Is this possible, or is there another way of doing this I just haven't thought of?

  • Color is my own class called 'Color4', not the .NET one, sorry. – whalebiologist Nov 07 '11 at 09:06
  • 1
    is it a `class` or a `struct`? (I'm thinking about different options) – Marc Gravell Nov 07 '11 at 09:13
  • Sorry; it's a struct. Ideally I would like the same behaviour for both classes and structs. – whalebiologist Nov 07 '11 at 09:14
  • 1
    k; I had something that worked for class; I'll try and make it work for struct... – Marc Gravell Nov 07 '11 at 09:17
  • What would need to be different for a class? – whalebiologist Nov 07 '11 at 09:44
  • 1
    2 reasons: *firstly*, most structs should be immutable (meaning: no setters) - which *demands* the `CreateInstance` pattern; *secondly*, with a struct the grid ends up editing a disconnected boxed copy of the value otherwise, which means you can make changes that are **never applied** to the actual instance (you can see this by adding setters to the struct and removing the create-instance stuff) – Marc Gravell Nov 07 '11 at 10:12
  • Would you still use CreateInstance if it was a class? – whalebiologist Nov 07 '11 at 10:26
  • could do, but it is slightly less of an issue then – Marc Gravell Nov 07 '11 at 10:31
  • Sorry I'm a bit confused. I'm not sure how you would do it if it was a class. Could you please post what you had that worked for a class? – whalebiologist Nov 07 '11 at 10:33
  • 1
    pretty-much the same, but without the CreateInstance stuff. It is also important that `CanConvertFrom` reports `false` for string (to avoid it making the text version editable), but IIRC that is the default. In particular, when it was a *class*, I let the members be mutable – Marc Gravell Nov 07 '11 at 10:36

1 Answers1

5

This seems to do the trick:

[TypeConverter(typeof (Color.ColorConverter))]
public struct Color
{
    private readonly byte alpha, red, green, blue;
    public Color(byte alpha, byte red, byte green, byte blue)
    {
        this.alpha = alpha;
        this.red = red;
        this.green = green;
        this.blue = blue;
    }
    public byte Alpha { get { return alpha; } }
    public byte Red { get { return red; } }
    public byte Green { get { return green; } }
    public byte Blue { get { return blue; } }
    public override string ToString()
    {
        return string.Format("{{(R,G,B,A) = ({0},{1},{2},{3})}}", Red, Green, Blue, Alpha);
    }
    private class ColorConverter : ExpandableObjectConverter
    {
        public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
        {
            return true;
        }
        public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
        {
            return new Color((byte)propertyValues["Alpha"], (byte)propertyValues["Red"],
                             (byte) propertyValues["Green"], (byte) propertyValues["Blue"]);
        }
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900