You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在自定义用户控件中暴露DataGridView的设计时属性(含列编辑箭头)?

解决DataGridView用户控件设计时列编辑功能的编译错误

问题描述

我有一个名为DGVManager的用户控件,内部包含名为DGV的DataGridView及其他控件。想要暴露该DataGridView的设计时属性,尤其是添加/编辑列的小箭头功能。参考相关方案转成VB.NET代码后,出现编译错误:"InitializeComponent"和"Return DGV"提示"引用非共享成员需要对象引用"。

原代码如下:

Public Class MyDataGridView
    Inherits DataGridView

    <Editor(GetType(ExtendedDataGridViewColumnCollectionEditor), GetType(UITypeEditor))>
    Public Overloads ReadOnly Property Columns As DataGridViewColumnCollection
        Get
            Return MyBase.Columns
        End Get
    End Property

    Private Class ExtendedDataGridViewColumnCollectionEditor
        Inherits UITypeEditor

        Private dataGridViewColumnCollectionDialog As Form

        Private Sub New()
        End Sub

        Private Shared Function CreateColumnCollectionDialog(ByVal provider As IServiceProvider) As Form
            Dim myassembly = Assembly.Load(GetType(ControlDesigner).Assembly.ToString())
            Dim type = myassembly.[GetType]("System.Windows.Forms.Design.DataGridViewColumnCollectionDialog")
            Dim ctor = type.GetConstructors(BindingFlags.NonPublic Or BindingFlags.Instance)(0)
            Return CType(ctor.Invoke(New Object() {provider}), Form)
        End Function

        Public Shared Sub SetLiveDataGridView(ByVal form As Form, ByVal dgv As DataGridView)
            Dim method = form.[GetType]().GetMethod("SetLiveDataGridView", BindingFlags.NonPublic Or BindingFlags.Instance)
            method.Invoke(form, New Object() {dgv})
        End Sub

        Public Overrides Function EditValue(ByVal context As ITypeDescriptorContext, ByVal provider As IServiceProvider, ByVal value As Object) As Object
            If provider IsNot Nothing AndAlso context IsNot Nothing Then
                Dim service = CType(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
                If service Is Nothing OrElse context.Instance Is Nothing Then Return value
                Dim host = CType(provider.GetService(GetType(IDesignerHost)), IDesignerHost)
                If host Is Nothing Then Return value
                If dataGridViewColumnCollectionDialog Is Nothing Then dataGridViewColumnCollectionDialog = CreateColumnCollectionDialog(provider)
                Dim dgv = (CType(context.Instance, MyDataGridView))
                Dim oldSite = dgv.Site
                dgv.Site = (CType(context.Instance, MyDataGridView)).Parent?.Site
                SetLiveDataGridView(dataGridViewColumnCollectionDialog, dgv)

                Using transaction = host.CreateTransaction("DataGridViewColumnCollectionTransaction")

                    If service.ShowDialog(dataGridViewColumnCollectionDialog) = DialogResult.OK Then
                        transaction.Commit()
                    Else
                        transaction.Cancel()
                    End If
                End Using

                dgv.Site = oldSite
            End If

            Return value
        End Function

        Public Overrides Function GetEditStyle(ByVal context As ITypeDescriptorContext) As UITypeEditorEditStyle
            Return UITypeEditorEditStyle.Modal
        End Function
    End Class
End Class


Partial Public Class DGVManager
    Inherits UserControl
    Public Sub New()
        InitializeComponent()
    End Sub

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
    Public ReadOnly Property DataGridView As DataGridView
        Get
            Return DGV
        End Get
    End Property
End Class

错误原因

  1. DGVManager是分部类,InitializeComponent方法和DGV成员由VS自动生成在对应的DGVManager.Designer.vb文件中,若当前代码文件未正确关联分部类,编译器无法找到这些非共享成员。
  2. 原代码中MyDataGridView扩展了列编辑器,但用户控件内部的DGV仍是原生DataGridView,未关联扩展功能;同时暴露的DataGridView属性未正确配置设计器元数据,无法触发列编辑对话框。

修正后的代码

1. 扩展DataGridView类(保持原逻辑,确保命名空间一致)

Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Reflection
Imports System.Windows.Forms.Design

Public Class MyDataGridView
    Inherits DataGridView

    <Editor(GetType(ExtendedDataGridViewColumnCollectionEditor), GetType(UITypeEditor))>
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
    Public Overloads ReadOnly Property Columns As DataGridViewColumnCollection
        Get
            Return MyBase.Columns
        End Get
    End Property

    Private Class ExtendedDataGridViewColumnCollectionEditor
        Inherits UITypeEditor

        Private dataGridViewColumnCollectionDialog As Form

        Private Sub New()
        End Sub

        Private Shared Function CreateColumnCollectionDialog(ByVal provider As IServiceProvider) As Form
            Dim myassembly = Assembly.Load(GetType(ControlDesigner).Assembly.ToString())
            Dim type = myassembly.[GetType]("System.Windows.Forms.Design.DataGridViewColumnCollectionDialog")
            Dim ctor = type.GetConstructors(BindingFlags.NonPublic Or BindingFlags.Instance)(0)
            Return CType(ctor.Invoke(New Object() {provider}), Form)
        End Function

        Public Shared Sub SetLiveDataGridView(ByVal form As Form, ByVal dgv As DataGridView)
            Dim method = form.[GetType]().GetMethod("SetLiveDataGridView", BindingFlags.NonPublic Or BindingFlags.Instance)
            method.Invoke(form, New Object() {dgv})
        End Sub

        Public Overrides Function EditValue(ByVal context As ITypeDescriptorContext, ByVal provider As IServiceProvider, ByVal value As Object) As Object
            If provider IsNot Nothing AndAlso context IsNot Nothing Then
                Dim service = CType(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
                If service Is Nothing OrElse context.Instance Is Nothing Then Return value
                Dim host = CType(provider.GetService(GetType(IDesignerHost)), IDesignerHost)
                If host Is Nothing Then Return value
                If dataGridViewColumnCollectionDialog Is Nothing Then dataGridViewColumnCollectionDialog = CreateColumnCollectionDialog(provider)
                Dim dgv = CType(context.Instance, MyDataGridView)
                Dim oldSite = dgv.Site
                dgv.Site = dgv.Parent?.Site
                SetLiveDataGridView(dataGridViewColumnCollectionDialog, dgv)

                Using transaction = host.CreateTransaction("DataGridViewColumnCollectionTransaction")
                    If service.ShowDialog(dataGridViewColumnCollectionDialog) = DialogResult.OK Then
                        transaction.Commit()
                    Else
                        transaction.Cancel()
                    End If
                End Using

                dgv.Site = oldSite
            End If

            Return value
        End Function

        Public Overrides Function GetEditStyle(ByVal context As ITypeDescriptorContext) As UITypeEditorEditStyle
            Return UITypeEditorEditStyle.Modal
        End Function
    End Class
End Class

2. 修正用户控件类

确保DGVManager的分部类完整,且内部DGV控件类型改为MyDataGridView:

' DGVManager.vb
Partial Public Class DGVManager
    Inherits UserControl

    Public Sub New()
        InitializeComponent()
    End Sub

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
    <Browsable(True)>
    Public ReadOnly Property DataGridView As MyDataGridView
        Get
            Return DGV
        End Get
    End Property
End Class

同时检查DGVManager.Designer.vb文件,确保DGV的定义为:

Friend WithEvents DGV As MyDataGridView

关键修正点

  • 将用户控件内部的DataGridView替换为扩展后的MyDataGridView,继承列编辑功能。
  • 为DataGridView属性添加<Browsable(True)>,确保设计器中可见;保留<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>,确保列配置能被序列化。
  • 确保分部类的一致性,DGVManager的两个分部文件(.vb和.Designer.vb)需在同一命名空间下。

内容的提问来源于stack exchange,提问作者ilens

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:17:32