【问题标题】:How to create custom generic PropertyGrid editor item which opens a form?如何创建打开表单的自定义通用 PropertyGrid 编辑器项?
【发布时间】:2017-10-28 20:16:49
【问题描述】:

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

 public partial class MyTestForm1 : MyBrowseForm<CONTACTS_BASE>

其中 CONTACTS_BASE 是一个 EntityFramework 实体。

在父类上我想要一个属性,这样当我在设计器时从属性网格中单击它时,它会打开一个表单,在该表单上我想要一个组合框,其中填充了 CONTACTS_BASE 上的字段实体。

我找到了这篇文章 Marc Gravell's answer 帮助我在设计时单击该属性时打开了一个新表单,并且我还在 ComboBox 中填充了 CONTACTS_BASE 字段。但是要在表单加载事件上执行此操作,我调用了我创建的函数,该函数返回字段列表并将其设置为 ComboBox 的 DataSource。

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

但是我想要完成的是使这个通用

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

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))]

它说:

感谢任何帮助,感谢我的英语不好

【问题讨论】:

    标签: c# winforms generics propertygrid uitypeeditor


    【解决方案1】:

    简答

    要知道如何解决这个问题,你需要知道EditValue 方法有一个context 参数,它的类型是ITypeDescriptorContext,并且有一个Instance 属性,它是你所在属性的所有者对象编辑。有了所有者(表单),我们就知道表单的类型,因此我们知道通用参数类型,因此我们可以创建通用编辑器表单。

    分步示例

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

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

    以下是创建自定义模型 UI 类型编辑器以在您编辑从 MyBaseForm&lt;T&gt; 派生的表单的特定属性时显示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&lt;T&gt; 派生的示例派生形式。我们将编辑此类实例的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 类型编辑器

    当调用UITypeEditorEditValue方法时,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);
        }
    }
    

    【讨论】:

    • 附带说明,请记住,如果您的目标只是选择T 的属性之一,那么还有更简单的选项。但我发布了用于学习目的的答案和完整的工作示例,其中包含一些有用的技巧,包括context 的使用、使用反射创建动态类型的实例、获取要在组合框中显示的属性等等 :)
    • 注意context 是(在属性网格版本的上下文中)只是GridItem 的一个实例,它是一个公共类:msdn.microsoft.com/en-us/library/…。即使没有这个,ITypeDescriptorContext 也有一个 Instance 属性,它是组件实例。无需使用反射。
    • @SimonMourier 感谢您的评论。关于 Instance 属性,我知道该属性并且我已经使用了它here for this answer。可能在我编写示例时,由于代码中的错误而改变了主意。我将代码更改为再次使用Instnace 属性。关于类型,是的,你提到的类型是我使用的类型的基础,我使用它只是因为 Component 属性。不再使用它没有任何意义。感谢您的评论:)
    • @RezaAghaei 工作起来就像一个魅力=) 我还有一个问题:当我打开 BdEditor 表单时,我在一个列表中进行了多个输入。然后我返回列表,我想做的是将该列表传递给 BdEditor,这样如果列表不为空,我可以添加更多项目或从中删除。我该怎么做呢?这可能吗?
    • 你看我的回答here了吗?我想这就是你要找的。​​span>
    猜你喜欢
    • 2010-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多