在UserControl中公开DataGridView的Columns属性,并使其可以通过Designer编辑

Har*_*lse 6 c# user-controls datagridview windows-forms-designer winforms

简短的介绍:

我有一个带有DataGridView的UserControl。我想向设计者公开DataGridView Columns集合,因此我可以在设计时更改用户控件上的列。

问:为此,我需要哪些设计师属性?

对于那些对较长版本感兴趣的人:

我有一个具有以下功能的UserControl:

  • 一个DataGridView,它显示集合中项目的“页面”。
  • NumericUpdown控件以选择要显示的页面。
  • 向上翻页/向下翻页按钮,当显示首页/最后一页时将禁用
  • 对显示项目的更改以视觉方式标记
  • 用于保存/放弃更改的按钮。

该用户控件可以自主运行。它具有父控件要使用的一个功能:

  • 显示页面(要显示的项目集合)

UserControl引发两个事件:

  • 事件页面已更改(具有页码)。应该导致加载新页面
  • 事件保存项(带有更改项的集合)

我必须以几种形式显示此用户控件。唯一的区别是每个表格的DataGridViewColumn集合不同。

我可以以编程方式添加列,但是使用设计器创建列会更容易。

Rez*_*aei 7

通常,注册一个合适的UITypeEditorusing [Editor]属性就足够了。它使用的编辑器DataGridViewDataGridViewColumnCollectionEditor。但是在这种情况下,如果我们直接使用此编辑器,则该编辑器期望该属性属于a DataGridView并尝试将to的值转换为ITypeDescriptorContext.InstanceDataGridVeiew并且由于我们的editing Columns属性属于我们的用户控件,因此我们将收到异常:

无法将类型为'的对象强制转换Type of Control'为' System.Windows.Forms.DataGridView'。

为了解决该问题,我们需要创建一个自定义UITypeEditor以及覆盖EditValue和编辑用户控件Columns私有DataGridView字段的属性。

为此,我们创建一个ITypeDescriptorContext包含DataGridViewColumns属性的实例,并将其传递给EditValue编辑器的方法。这样,编辑器将编辑我们的Columns属性。

我们还使用[DesignerSerializationVisibility]属性修饰属性以序列化集合内容。

这是实现。

MyUserControl

我想您DataGridView在设计时向用户控件添加了一个名称,名称为dataGridView1

public partial class MyUserControl : UserControl
{
    public MyUserControl()
    {
        InitializeComponent();
    }

    [Editor(typeof(MyColumnEditor), typeof(UITypeEditor))]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public DataGridViewColumnCollection Columns
    {
        get { return this.dataGridView1.Columns; }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

public class MyColumnEditor : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.Modal;
    }
    public override object EditValue(ITypeDescriptorContext context,
                                     IServiceProvider provider, object value)
    {
        var field = context.Instance.GetType().GetField("dataGridView1",
                       System.Reflection.BindingFlags.NonPublic |
                       System.Reflection.BindingFlags.Instance);

        var dataGridView1 = (DataGridView)field.GetValue(context.Instance);
        dataGridView1.Site = ((Control)context.Instance).Site;
        var columnsProperty = TypeDescriptor.GetProperties(dataGridView1)["Columns"];
        var tdc = new TypeDescriptionContext(dataGridView1, columnsProperty);
        var editor = (UITypeEditor)columnsProperty.GetEditor(typeof(UITypeEditor));
        var result = editor.EditValue(tdc, provider, value);
        dataGridView1.Site = null;
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

ITypeDescriptionContext实现

public class TypeDescriptionContext : ITypeDescriptorContext
{
    private Control editingObject;
    private PropertyDescriptor editingProperty;
    public TypeDescriptionContext(Control obj, PropertyDescriptor property)
    {
        editingObject = obj;
        editingProperty = property;
    }
    public IContainer Container
    {
        get { return editingObject.Container; }
    }
    public object Instance
    {
        get { return editingObject; }
    }
    public void OnComponentChanged()
    {
    }
    public bool OnComponentChanging()
    {
        return true;
    }
    public PropertyDescriptor PropertyDescriptor
    {
        get { return editingProperty; }
    }
    public object GetService(Type serviceType)
    {
        return editingObject.Site.GetService(serviceType);
    }
}
Run Code Online (Sandbox Code Playgroud)