41

I have a List<> (my custom class). I want to display a specific item in this list in a box on the PropertyGrid control. At the end of the box I would like the [...] button. When clicked, it would open up a form which, among other things, would allow them to pick one of the items from the List. When closed, the PropertyGrid would be updated to reflect the selected value.

Any help appreciated.

esac
  • 24,099
  • 38
  • 122
  • 179

1 Answers1

96

You need to implement a modal UITypeEditor, using the IWindowsFormsEditorService service to display it:

using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System;

class MyType
{
    private Foo foo = new Foo();
    public Foo Foo { get { return foo; } }
}

[Editor(typeof(FooEditor), typeof(UITypeEditor))]
[TypeConverter(typeof(ExpandableObjectConverter))]
class Foo
{
    private string bar;
    public string Bar
    {
        get { return bar; }
        set { bar = value; }
    }
}
class FooEditor : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.Modal;
    }
    public override object EditValue(ITypeDescriptorContext context, System.IServiceProvider provider, object value)
    {
        IWindowsFormsEditorService svc = provider.GetService(typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
        Foo foo = value as Foo;
        if (svc != null && foo != null)
        {            
            using (FooForm form = new FooForm())
            {
                form.Value = foo.Bar;
                if (svc.ShowDialog(form) == DialogResult.OK)
                {
                    foo.Bar = form.Value; // update object
                }
            }
        }
        return value; // can also replace the wrapper object here
    }
}
class FooForm : Form
{
    private TextBox textbox;
    private Button okButton;
    public FooForm() {
        textbox = new TextBox();
        Controls.Add(textbox);
        okButton = new Button();
        okButton.Text = "OK";
        okButton.Dock = DockStyle.Bottom;
        okButton.DialogResult = DialogResult.OK;
        Controls.Add(okButton);
    }
    public string Value
    {
        get { return textbox.Text; }
        set { textbox.Text = value; }
    }
}
static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Form form = new Form();
        PropertyGrid grid = new PropertyGrid();
        grid.Dock = DockStyle.Fill;
        form.Controls.Add(grid);
        grid.SelectedObject = new MyType();
        Application.Run(form);
    }
}

Note: if you need to access something about the context of the property (the parent object etc), that is what the ITypeDescriptorContext (in EditValue) provides; it tells you the PropertyDescriptor and Instance (the MyType) that is involved.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 7
    I wish I could +1 this twice! I upvoted it in the past almost 2 years ago and I came back to the same question again on a different project and still found it to be exactly what I needed. Great answer, thanks! – Paccc Jan 06 '13 at 03:22
  • Everything looks nice, but FooEditor is not used anywhere. It should be set as TypeEditor for some of the properties. – virious Mar 18 '13 at 14:16
  • 1
    @virious type-editors can be specified against either a property or a type; in this case it is applied (via `[Editor(typeof(FooEditor), typeof(UITypeEditor))]`) to the type `Foo`. Thus ***every*** property of type `Foo` uses this editor by default. See `MyType.Foo` in the example. – Marc Gravell Mar 19 '13 at 07:43
  • Thank you for the great example! – Glenn Jun 17 '16 at 14:44
  • Is there a way to access the parent window that the grid resides in? Im asking because it would be nice to launch the new window as CenterOwner – Johan Aspeling Feb 21 '17 at 14:50
  • Is it possible to get some notification in MyType that the value of foo.Bar has changed ? – GuidoG May 16 '17 at 15:54
  • Can anyone tell me how to do this with the WPF Property Grid? – user2109254 Nov 16 '17 at 10:12
  • What is the point of `[TypeConverter(typeof(ExpandableObjectConverter))]` above the property? I have never used that or seen that when creating `UITypeEditor` objects. – Andy Apr 15 '20 at 13:39
  • @Andy otherwise it doesn't offer the sub-properties in the same way – Marc Gravell Apr 15 '20 at 13:45
  • Just tried this approach for a custom `DataGridView.Columns` property, and it worked. However, when use the smart tag option links "Edit Columns" it just shows me the system default column editor. Is there some way to override this behavior too? – gvdvenis Jul 27 '22 at 11:20