I have a custom generic form
public partial class MyTestForm1 : MyBrowseForm<CONTACTS_BASE>
where CONTACTS_BASE is an EntityFramework entity.
on the parent class I would like to have a property so that when I click on it at designer time from the property grid it opens a form and on that form I would like to have a combobox populated with the fields on the CONTACTS_BASE entity.
I have found this post Marc Gravell's answer helped me to open a new form when clicked on the property at design time and I have also populated the ComboBox with fields of CONTACTS_BASE. but to do this on the form load event I called to function I made to that returns the list of fields and set it to ComboBox's DataSource.
comboBox1.DataSource = EntityBase.BaseGetTableFieldList2<CONTACTS_BASE>();
however what I would like to accomplish is making this generic
so what I would like to do is something like this to populate the ComboBox.
public partial class BdEditorForm <TParentEntity>:Form where TParentEntity:class
{
private void BdEditorForm_Load(object sender, EventArgs e)
{
comboBox1.DataSource = EntityBase.BaseGetTableFieldList2<TParentEntity>();
}
}
is something like this possible? because When I try to do this I need to make make TypeEditor generic too and then when giving attributes to the property I create
[Editor(typeof(BdFormTypeEditor<TParentEntity>), typeof(UITypeEditor))]
[TypeConverter(typeof(ExpandableObjectConverter))]
any help is appreciated thanks and sorry for my bad english
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);
}
}