Short Answer
To know how to solve the problem, you need to know EditValue
method has a context
parameter which is of type ITypeDescriptorContext
and has an Instance
property which is the owner object of the property that you are editing. Having the owner (the Form) we know the type of the form and therefore we know the generic parameter type and therefore we can create our generic editor form.
Step By Step Example
Above fact is the key point for the answer, but to solve the problem, you need to apply some other tricks as well. For example you should get a generic type and create an instance of it using reflection.
Here I put a project containing the whole source code of the example:
Here are steps of the example which creates a custom model UI Type Editor to show a list of properties of T
when you are editing a specific property of a form which is derived from MyBaseForm<T>
.
Generic Base Form
It's the base form for other forms which contains SomeProperty
, the property which you want to edit using a custom editor.
Add a MyGenericType
property to the class which returns typeof(T)
, the generic type of the form:
public partial class MyBaseForm<T> : Form
{
public MyBaseForm()
{
InitializeComponent();
}
[Editor(typeof(MyUITypeEditor), typeof(UITypeEditor))]
public string SomeProperty { get; set; }
[Browsable(false)]
public Type MyGenericType { get { return typeof(T); } }
}
Derived Form
It's a sample derived form which is derived from the MyBaseForm<T>
. We will edit SomeProperty
of an instance of this class.
public partial class MyDerivedForm : MyBaseForm<MySampleModel>
{
public MyDerivedForm()
{
InitializeComponent();
}
}
Sample Model
It's a sample model which we are going to show its properties in the custom editor window.
public class MySampleModel
{
public int Id { get; set; }
public string Name { get; set; }
public int Price { get; set; }
}
Editor Form
It's the form which UITypeEditor
will show. In the form, we fill comoBox1
with field names of the generic argument.
public partial class MyEditorForm<T> : Form
{
public MyEditorForm()
{
InitializeComponent();
this.StartPosition = FormStartPosition.CenterScreen;
var list = ListBindingHelper.GetListItemProperties(typeof(T))
.Cast<PropertyDescriptor>()
.Select(x => new { Text = x.Name, Value = x }).ToList();
this.comboBox1.DataSource = list;
this.comboBox1.DisplayMember = "Text";
this.comboBox1.ValueMember = "Value";
}
public string SelectedProperty
{
get
{
return comboBox1.GetItemText(comboBox1.SelectedItem);
}
}
private void button1_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
}
}
UI Type Editor
When calling EditValue
method of the UITypeEditor
, the context
parameter is of type System.Windows.Forms.PropertyGridInternal.PropertyDescriptorGridEntry
which has a Component
property which its value is the instance of the form which you are editing, so we know the type of the form and therefore we know the generic parameter type and therefore we can create our generic editor form and use it.
public class MyUITypeEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider, object value)
{
var svc = provider.GetService(typeof(IWindowsFormsEditorService))
as IWindowsFormsEditorService;
var myGenericTypeProperty = context.Instance.GetType()
.GetProperty("MyGenericType");
var genericArgument = (Type)myGenericTypeProperty.GetValue(context.Instance);
var editorFormType = typeof(MyEditorForm<>);
var genericArguments = new[] { genericArgument };
var editorFormInstance = editorFormType.MakeGenericType(genericArguments);
if (svc != null)
{
using (var f = (Form)Activator.CreateInstance(editorFormInstance))
if (svc.ShowDialog(f) == DialogResult.OK)
return ((dynamic)f).SelectedProperty;
}
else
{
using (var f = (Form)Activator.CreateInstance(editorFormInstance))
if (f.ShowDialog() == DialogResult.OK)
return ((dynamic)f).SelectedProperty;
}
return base.EditValue(context, provider, value);
}
}