如何创建打开表单的自定义通用 PropertyGrid 编辑器项目?
How to create custom generic PropertyGrid editor item which opens a form?
我有一个自定义通用表单
public partial class MyTestForm1 : MyBrowseForm<CONTACTS_BASE>
其中 CONTACTS_BASE 是一个 EntityFramework 实体。
在父级 class 上我想要一个 属性 以便当我在设计时从 属性 网格单击它时它会打开一个表单并在该表单上我想要一个组合框,其中填充了 CONTACTS_BASE 实体上的字段。
我找到了这个post
Marc Gravell's answer 帮助我在设计时单击 属性 时打开一个新表单,并且我还使用 CONTACTS_BASE 字段填充了 ComboBox。但是为了在我调用函数的表单加载事件上执行此操作,我对该 returns 字段列表进行了设置,并将其设置为 ComboBox 的数据源。
comboBox1.DataSource = EntityBase.BaseGetTableFieldList2<CONTACTS_BASE>();
然而我想要完成的是使这个通用
所以我想做的就是像这样填充 ComboBox。
public partial class BdEditorForm <TParentEntity>:Form where TParentEntity:class
{
private void BdEditorForm_Load(object sender, EventArgs e)
{
comboBox1.DataSource = EntityBase.BaseGetTableFieldList2<TParentEntity>();
}
}
这样的事情可能吗?因为当我尝试这样做时
我也需要使 TypeEditor 通用,然后在为 属性 提供属性时我创建
[Editor(typeof(BdFormTypeEditor<TParentEntity>), typeof(UITypeEditor))]
[TypeConverter(typeof(ExpandableObjectConverter))]
它说:
感谢任何帮助,谢谢,抱歉我的英语不好
简答
要知道如何解决这个问题,你需要知道 EditValue
方法有一个 context
类型的参数 ITypeDescriptorContext
and has an Instance
属性 是所有者对象您正在编辑的 属性。有了所有者(表单),我们知道表单的类型,因此我们知道通用参数类型,因此我们可以创建我们的通用编辑器表单。
分步示例
以上事实是答案的重点,但要解决问题,还需要应用一些其他技巧。例如,您应该获取泛型类型并使用反射创建它的实例。
这里我放了一个项目,里面包含了示例的全部源代码:
以下是创建自定义模型的示例的步骤 UI 当您编辑表单的特定 属性 时,键入编辑器以显示 T
的属性列表源自 MyBaseForm<T>
。
通用基本形式
它是其他表单的基本表单,其中包含 SomeProperty
,您要使用自定义编辑器编辑的 属性。
在returnstypeof(T)
的class中添加一个MyGenericType
属性,泛型的形式为:
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>
派生的示例派生形式。我们将编辑此 class.
实例的 SomeProperty
public partial class MyDerivedForm : MyBaseForm<MySampleModel>
{
public MyDerivedForm()
{
InitializeComponent();
}
}
示例模型
这是一个示例模型,我们将在自定义编辑器中显示其属性 window。
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);
}
}
我有一个自定义通用表单
public partial class MyTestForm1 : MyBrowseForm<CONTACTS_BASE>
其中 CONTACTS_BASE 是一个 EntityFramework 实体。
在父级 class 上我想要一个 属性 以便当我在设计时从 属性 网格单击它时它会打开一个表单并在该表单上我想要一个组合框,其中填充了 CONTACTS_BASE 实体上的字段。
我找到了这个post Marc Gravell's answer 帮助我在设计时单击 属性 时打开一个新表单,并且我还使用 CONTACTS_BASE 字段填充了 ComboBox。但是为了在我调用函数的表单加载事件上执行此操作,我对该 returns 字段列表进行了设置,并将其设置为 ComboBox 的数据源。
comboBox1.DataSource = EntityBase.BaseGetTableFieldList2<CONTACTS_BASE>();
然而我想要完成的是使这个通用
所以我想做的就是像这样填充 ComboBox。
public partial class BdEditorForm <TParentEntity>:Form where TParentEntity:class
{
private void BdEditorForm_Load(object sender, EventArgs e)
{
comboBox1.DataSource = EntityBase.BaseGetTableFieldList2<TParentEntity>();
}
}
这样的事情可能吗?因为当我尝试这样做时 我也需要使 TypeEditor 通用,然后在为 属性 提供属性时我创建
[Editor(typeof(BdFormTypeEditor<TParentEntity>), typeof(UITypeEditor))]
[TypeConverter(typeof(ExpandableObjectConverter))]
它说:
感谢任何帮助,谢谢,抱歉我的英语不好
简答
要知道如何解决这个问题,你需要知道 EditValue
方法有一个 context
类型的参数 ITypeDescriptorContext
and has an Instance
属性 是所有者对象您正在编辑的 属性。有了所有者(表单),我们知道表单的类型,因此我们知道通用参数类型,因此我们可以创建我们的通用编辑器表单。
分步示例
以上事实是答案的重点,但要解决问题,还需要应用一些其他技巧。例如,您应该获取泛型类型并使用反射创建它的实例。
这里我放了一个项目,里面包含了示例的全部源代码:
以下是创建自定义模型的示例的步骤 UI 当您编辑表单的特定 属性 时,键入编辑器以显示 T
的属性列表源自 MyBaseForm<T>
。
通用基本形式
它是其他表单的基本表单,其中包含 SomeProperty
,您要使用自定义编辑器编辑的 属性。
在returnstypeof(T)
的class中添加一个MyGenericType
属性,泛型的形式为:
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>
派生的示例派生形式。我们将编辑此 class.
SomeProperty
public partial class MyDerivedForm : MyBaseForm<MySampleModel>
{
public MyDerivedForm()
{
InitializeComponent();
}
}
示例模型
这是一个示例模型,我们将在自定义编辑器中显示其属性 window。
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);
}
}