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

Expose Columns property of a DataGridView in UserControl and make it editable via Designer

Short description:

I have a UserControl with a DataGridView on it. I want to expose the DataGridView Columns collection to the designer, so I can change the columns on my User Control at design time.

问题:为此我需要哪些设计器属性?

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

我有一个具有以下功能的用户控件:

此用户控件可以自主工作。它有一个函数供父控件使用:

UserControl 引发两个事件:

我必须在多个窗体上显示此用户控件。唯一的区别是 DataGridViewColumn 的集合因表单而异。

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

通常注册一个合适的UITypeEditor using [Editor] attribute. The editor which is used by the DataGridView is DataGridViewColumnCollectionEditor. But in this case, if we use this editor directly, the editor expect the the property belong to a DataGridView and tries to convert value of ITypeDescriptorContext.InstanceDataGridVeiew就足够了,因为我们的编辑Columns属性属于我们的用户控件,我们将收到一个异常:

Unable to cast object of type 'Type of Control' to type 'System.Windows.Forms.DataGridView'.

要解决这个问题,我们需要创建一个自定义UITypeEditor并覆盖EditValue并编辑Columns属性的私有DataGridView字段你的用户控制。

为此,我们创建了一个包含 DataGridViewITypeDescriptorContext 实例,它是 Columns 属性 并将其传递给 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; }
    }
}

编辑器

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

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