yonan2236
yonan2236

Reputation: 13659

Dispaying and editing a sub property of a property in PropertyGrid

I could no longer find an exact solution to my problem in the internet so I'm asking this question. Hope you may be able to help me.

I have the following classes:

public Item 
{
    public FieldType MyField { get; set; }
    public string Description { get; set; }
    public int Capacity { get; set; }
}

public FieldType 
{
    public string Value { get; set; }
    public string FieldCode { get; set; }
    public string TableCode { get; set; }
}

In my form, I created an instance of Item class. Which contains the following members:

Is it possible to only show the Value member of MyField property in the PropertyGrid?

Below is how I assign the selected object property of the PropertyGrid.

void Form1(object sender, EventArgs e)
{
    propertyGrid1.SelectedObject = new Item();          
}

Upvotes: 0

Views: 1973

Answers (3)

Reza Aghaei
Reza Aghaei

Reputation: 125302

Solution 1 - Add a property

You can add a property to Item class to get and set MyField.Value:

public string Value
{
    get
    {
        if (MyField != null)
            return MyField.Value;
        return null;
    }
    set
    {
        if (MyField != null)
            MyField.Value = value;
    }
}

enter image description here

• Preferably define that property in a partial class.
• Use this option when you have access to codes of the classes. If those classes are not yours, use 3rd solution.

Solution 2 - Use ExpandableObjectConverter

You can decorate the MyField property of Item class with ExpandableObjectConverter. Also decorate FieldType with [Browsable(false)] of FieldType class to hide it in property grid if you want:

[TypeConverter(typeof(ExpandableObjectConverter))]
public FieldType MyField { get; set; }

enter image description here

• To customize the text which is shown in front of MyField, you can override ToString method of FieldType and return Value. Also you can do it using a custom TypeConverter and overriding its ConvertTo method.

Solution 3 - Use a custom TypeDescriptor

It's not as easy as the first solution, but the output is completely like what you get using the first solution. It's suitable for cases that you can not manipulate those classes.

You can use it this way:

var item = new Item() { MyField = new FieldType() { Value = "Some Value" } };
TypeDescriptor.AddProvider(new MyTypeDescriptionProvider(), item);
this.propertyGrid1.SelectedObject = item;

Or by decorating Item class with:

[TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
public class Item

Custom Property Descriptor

public class MyPropertyDescriptor : PropertyDescriptor
{
    private PropertyDescriptor subProperty;
    private PropertyDescriptor parentProperty;
    public MyPropertyDescriptor(PropertyDescriptor parent, PropertyDescriptor sub)
        : base(sub, null)
    {
        subProperty = sub;
        parentProperty = parent;
    }
    public override bool IsReadOnly { get { return subProperty.IsReadOnly; } }
    public override void ResetValue(object component)
    {
        subProperty.ResetValue(parentProperty.GetValue(component));
    }
    public override bool CanResetValue(object component)
    {
        return subProperty.CanResetValue(parentProperty.GetValue(component));
    }
    public override bool ShouldSerializeValue(object component)
    {
        return subProperty.ShouldSerializeValue(parentProperty.GetValue(component));
    }
    public override Type ComponentType { get { return parentProperty.ComponentType; } }
    public override Type PropertyType { get { return subProperty.PropertyType; } }
    public override object GetValue(object component)
    {
        return subProperty.GetValue(parentProperty.GetValue(component));
    }
    public override void SetValue(object component, object value)
    {
        subProperty.SetValue(parentProperty.GetValue(component), value);
        OnValueChanged(component, EventArgs.Empty);
    }
}

Custom type Descriptor

public class MyTypeDescriptor : CustomTypeDescriptor
{
    ICustomTypeDescriptor original;
    public MyTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
        : base(originalDescriptor)
    {
        original = originalDescriptor;
    }
    public override PropertyDescriptorCollection GetProperties()
    {
        return this.GetProperties(new Attribute[] { });
    }
    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        var properties = original.GetProperties().Cast<PropertyDescriptor>().ToList();
        var parent = properties.Where(x => x.Name == "MyField").First();
        var sub = TypeDescriptor.GetProperties(typeof(FieldType))["Value"];
        properties.Remove(parent);
        properties.Add(new MyPropertyDescriptor(parent, sub));
        return new PropertyDescriptorCollection(properties.ToArray());
    }
}

Custom TypeDescriptorProvider

public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
    public MyTypeDescriptionProvider()
        : base(TypeDescriptor.GetProvider(typeof(object))) { }

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
                                                            object instance)
    {
        ICustomTypeDescriptor baseDes = base.GetTypeDescriptor(objectType, instance);
        return new MyTypeDescriptor(baseDes);
    }
}

• Use this option if Item and FieldType are not yours. If those classes are yours and you can change their code, use first solution.

Upvotes: 1

Neil
Neil

Reputation: 666

Im not really sure of what you are looking for but here are 2 answers

1.(as I understood it)
if you want it to show only Value when you try and view the properties of a MyField Instance then all you need to do is add a constructor to the MyField so you can assign the other two values and change the public property to private like so

public FieldType 
{
   public string Value { get; set; }
   private string FieldCode { get; set; }
   private string TableCode { get; set; }
}


2.(this will hide the MyField from your propertyGrid)
Override the ToString() method of FielType like so

public override string ToString()
{
    return Value;
}

then set your MyField to private and encapsulate it. returning the instance as a string. which would use the overridden value. like so

private FieldType MyField;
public string value{ get{return MyField.ToString();}set;}

your MyField will return the overridden ToString value which returns Value.

Upvotes: 0

Charles Bretana
Charles Bretana

Reputation: 146557

Yes, easy:
add a computed read only property to Item

public Item 
{
   public FieldType MyField { get; set; }
   public string MyFieldValue => MyField.Value;
   public string Description { get; set; }
   public int Capacity { get; set; }
}

Upvotes: 2

Related Questions