简答
要知道如何解决这个问题,你需要知道EditValue 方法有一个context 参数,它的类型是ITypeDescriptorContext,并且有一个Instance 属性,它是你所在属性的所有者对象编辑。有了所有者(表单),我们就知道表单的类型,因此我们知道通用参数类型,因此我们可以创建通用编辑器表单。
分步示例
事实是答案的关键,但要解决问题,您还需要应用一些其他技巧。例如,您应该获取一个泛型类型并使用反射创建它的实例。
这里我放了一个项目,里面包含了例子的全部源代码:
以下是创建自定义模型 UI 类型编辑器以在您编辑从 MyBaseForm<T> 派生的表单的特定属性时显示T 属性列表的示例的步骤。
通用基本表单
它是包含 SomeProperty 的其他表单的基本表单,您想使用自定义编辑器编辑该属性。
将MyGenericType 属性添加到返回typeof(T)(表单的泛型类型)的类:
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); } }
}
派生形式
这是从MyBaseForm<T> 派生的示例派生形式。我们将编辑此类实例的SomeProperty。
public partial class MyDerivedForm : MyBaseForm<MySampleModel>
{
public MyDerivedForm()
{
InitializeComponent();
}
}
样本模型
这是一个示例模型,我们将在自定义编辑器窗口中显示其属性。
public class MySampleModel
{
public int Id { get; set; }
public string Name { get; set; }
public int Price { get; set; }
}
编辑表单
这是UITypeEditor 将显示的形式。在表单中,我们用通用参数的字段名称填充comoBox1。
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 类型编辑器
当调用UITypeEditor的EditValue方法时,context参数是System.Windows.Forms.PropertyGridInternal.PropertyDescriptorGridEntry类型,它有一个Component属性,它的值是你正在编辑的表单的实例,所以我们知道表单的类型,因此我们知道通用参数类型,因此我们可以创建通用编辑器表单并使用它。
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);
}
}