C# 如何创建用于打开表单的自定义通用PropertyGrid编辑器项?

C# 如何创建用于打开表单的自定义通用PropertyGrid编辑器项?,c#,winforms,generics,propertygrid,uitypeeditor,C#,Winforms,Generics,Propertygrid,Uitypeeditor,我有一个自定义的通用表单 public partial class MyTestForm1 : MyBrowseForm<CONTACTS_BASE> 公共部分类MyTestForm1:MyBrowseForm 其中,CONTACTS_BASE是EntityFramework实体 在父类上,我希望有一个属性,这样当我在设计器时从属性网格中单击它时,它会打开一个窗体,并且在该窗体上,我希望有一个组合框,其中填充了CONTACTS_基本实体上的字段 我找到了这个帖子 帮助我在设计时

我有一个自定义的通用表单

 public partial class MyTestForm1 : MyBrowseForm<CONTACTS_BASE>
公共部分类MyTestForm1:MyBrowseForm
其中,CONTACTS_BASE是EntityFramework实体

在父类上,我希望有一个属性,这样当我在设计器时从属性网格中单击它时,它会打开一个窗体,并且在该窗体上,我希望有一个组合框,其中填充了CONTACTS_基本实体上的字段

我找到了这个帖子 帮助我在设计时单击该属性时打开一个新表单,并且我还用联系人的字段填充了组合框。但为了在表单加载事件中执行此操作,我调用了一个函数,该函数返回字段列表并将其设置为ComboBox的数据源

comboBox1.DataSource = EntityBase.BaseGetTableFieldList2<CONTACTS_BASE>();
comboBox1.DataSource=EntityBase.BaseGetTableFieldList2();

然而,我想完成的是使这个通用

所以我想做的是像这样填充组合框

public partial class BdEditorForm <TParentEntity>:Form where TParentEntity:class
{
    private void BdEditorForm_Load(object sender, EventArgs e)
    {       
     comboBox1.DataSource = EntityBase.BaseGetTableFieldList2<TParentEntity>();
    }
}
public分部类BdEditorForm:Form,其中TParentEntity:class
{
私有void BdEditorForm_加载(对象发送方,事件参数e)
{       
comboBox1.DataSource=EntityBase.BaseGetTableFieldList2();
}
}
这样的事情可能吗?因为当我试着这么做的时候 我需要使TypeEditor也通用,然后在为我创建的属性赋予属性时

[Editor(typeof(BdFormTypeEditor<TParentEntity>), typeof(UITypeEditor))]
[TypeConverter(typeof(ExpandableObjectConverter))]
[编辑器(typeof(BdFormTypeEditor),typeof(UITypeEditor))]
[TypeConverter(typeof(ExpandableObjectConverter))]
上面写着:


感谢您的帮助,并为我糟糕的英语表达歉意

简短的回答

要知道如何解决此问题,您需要知道
EditValue
方法有一个
context
参数,该参数的类型为,并且有一个属性,该属性是您正在编辑的属性的所有者对象。拥有所有者(表单),我们知道表单的类型,因此我们知道泛型参数类型,因此我们可以创建泛型编辑器表单

逐步示例

以上事实是答案的关键,但要解决这个问题,你还需要应用一些其他技巧。例如,您应该获取泛型类型并使用反射创建其实例

在这里,我放置了一个包含示例的全部源代码的项目:

下面是创建自定义模型UI类型编辑器的示例步骤,当您编辑从
MyBaseForm
派生的表单的特定属性时,该编辑器将显示
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); } }
}
样本模型

这是一个示例模型,我们将在自定义编辑器窗口中显示它的属性

public class MySampleModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Price { get; set; }
}
编辑表单

它是
UITypeEditor
将显示的表单。在表单中,我们用泛型参数的字段名填充
combox1

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;
    }
}

作为旁注,请记住,如果您的目标只是选择
T
的一个属性,那么也有更简单的选项。但是我发布了答案和完整的学习示例,其中包含一些有用的技巧,包括使用
上下文
,使用反射创建动态类型的实例,在组合框中显示属性等等:)注意
上下文
是(在属性网格版的上下文中)仅是
GridItem
的一个实例,它是一个公共类:。即使没有此选项,
ITypeScriptorContext
也有一个
实例
属性,它是组件实例。无需使用反射。@SimonMourier谢谢您的评论。关于实例属性,我知道该属性,并且已经使用过它。可能是在编写示例时,由于代码中的错误,我改变了主意。我再次将代码更改为使用
instance
属性。关于类型,是的,您提到的类型是我使用的类型的基础,我使用它只是因为
Component
property。不再使用它了。谢谢你的评论:)@RezaAghaei工作得很好=)我还有一个问题:当我打开BdEditor表单时,我会在列表中输入多个信息。我返回列表,我想做的是将该列表传递到BdEditor,这样,如果列表不是空的,我就可以添加更多的项以从中删除。我该怎么做?这可能吗?你看过我的答案了吗?我想这就是你要找的。
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;
    }
}
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);
    }
}