VB.NET学习笔记:自定义控件——扩展DataGridView控件支持全选的CheckBox列

测试环境:windows 7和Microsoft Visual Studio 2015

点击下载本文源码

在《VB.NET学习笔记:自定义控件之扩展DataGridViewColumnHeaderCell类增加CheckBox全选复选框》一文中通过在DataGridView控件的列表头绘制一个CheckBox控件来实现全选功能,实现起来感觉不是很顺手,今天再次仔细研读《开源DataGridView扩展(1) 扩展支持全选的CheckBox列。》博文,该文使用的是C#,我把它翻译成了VB.NET语言,记录下来与大家共勉。
首先新建一个窗体应用程序。
在这里插入图片描述
然后再添加一个类,修改类名为DataGridViewEx.vb。
在这里插入图片描述
后续:没事又来搞鼓,直接新建类库生成dll,发现必须要建一个自定义控件库来存放DataGridViewEx类,要不然会报错,在此记录一下。

在DataGridViewEx.vb代码窗口里添加如下代码:

Imports System.Windows.Forms.VisualStyles
Imports System.Runtime.InteropServices

Namespace DataGridViewEx
    '主要是对DataGridView进行扩展。
    ''' <summary>
    ''' 扩展的DataGridView
    ''' </summary>
    Public Class DataGridViewEx
        Inherits DataGridView

        Public Sub New()
            MyBase.New()
        End Sub

        ''' <summary>
        ''' checkbox的单元格改变事件
        ''' </summary>
        ''' <param name="columnIndex"></param>
        ''' <param name="rowIndex"></param>
        ''' <param name="value"></param>
        Friend Sub OnCheckBoxCellCheckedChange(ByVal columnIndex As Integer, ByVal rowIndex As Integer, ByVal value As Boolean)
            Dim existsChecked As Boolean = False, existsNoChecked As Boolean = False
            Dim cellEx As ColumnEx.DataGridViewCheckBoxCellEx

            For Each row As DataGridViewRow In Me.Rows
                cellEx = TryCast(row.Cells(columnIndex), ColumnEx.DataGridViewCheckBoxCellEx)
                If cellEx Is Nothing Then Return
                existsChecked = existsChecked Or cellEx.Checked
                existsNoChecked = existsNoChecked Or Not cellEx.Checked
            Next

            Dim headerCellEx As ColumnEx.DataGridViewCheckBoxColumnHeaderCellEx = TryCast(Me.Columns(columnIndex).HeaderCell, ColumnEx.DataGridViewCheckBoxColumnHeaderCellEx)
            If headerCellEx Is Nothing Then Return
            Dim oldState As CheckState = headerCellEx.CheckedAllState

            If existsChecked Then
                If existsNoChecked Then
                    headerCellEx.CheckedAllState = CheckState.Indeterminate
                Else
                    headerCellEx.CheckedAllState = CheckState.Checked
                End If
            Else
                headerCellEx.CheckedAllState = CheckState.Unchecked
            End If

            If oldState <> headerCellEx.CheckedAllState Then Me.InvalidateColumn(columnIndex)
        End Sub

        ''' <summary>
        ''' 全选中/取消全选中
        ''' </summary>
        ''' <param name="columnIndex"></param>
        ''' <param name="isCheckedAll"></param>
        Friend Sub OnCheckAllCheckedChange(ByVal columnIndex As Integer, ByVal isCheckedAll As Boolean)
            Me.EndEdit()

            Dim cellEx As ColumnEx.DataGridViewCheckBoxCellEx

            For Each row As DataGridViewRow In Me.Rows
                cellEx = TryCast(row.Cells(columnIndex), ColumnEx.DataGridViewCheckBoxCellEx)
                If cellEx Is Nothing Then Continue For
                cellEx.Checked = isCheckedAll
            Next
        End Sub
    End Class


    Namespace ColumnEx
#Region "扩展列"
        Public Class DataGridViewCheckBoxColumnEx
            Inherits DataGridViewCheckBoxColumn
            ''' <summary>
            ''' 扩展表头属性
            ''' </summary>
            ''' <returns></returns>
            Public ReadOnly Property HeaderCellEx As DataGridViewCheckBoxColumnHeaderCellEx
                Get
                    Return TryCast(Me.HeaderCell, DataGridViewCheckBoxColumnHeaderCellEx)
                End Get
            End Property
            ''' <summary>
            ''' 本列是否全选,方便在Cell中获取
            ''' </summary>
            ''' <returns></returns>
            Public ReadOnly Property IsCheckedAll As Boolean
                Get
                    Return HeaderCellEx.CheckedAllState = CheckState.Checked
                End Get
            End Property

            Public Sub New()
                MyBase.New()
                '建立表头单元格
                Me.HeaderCell = TryCast(New DataGridViewCheckBoxColumnHeaderCellEx(), DataGridViewColumnHeaderCell)
                '建立单元格模板
                Me.CellTemplate = New DataGridViewCheckBoxCellEx()
            End Sub
        End Class
#End Region

#Region "扩展表头单元格"
        Public Class DataGridViewCheckBoxColumnHeaderCellEx
            Inherits DataGridViewColumnHeaderCell
#Region "类型:属性"
            ''' <summary>
            ''' 宿主DataGridView扩展
            ''' </summary>
            ''' <returns></returns>
            Protected ReadOnly Property DataGridViewEx As DataGridViewEx
                Get
                    Return TryCast(DataGridView, DataGridViewEx)
                End Get
            End Property

            Private m_checkedAllState As CheckState = CheckState.Unchecked
            ''' <summary>
            ''' 全选按钮状态
            ''' </summary>
            ''' <returns></returns>
            Public Property CheckedAllState As CheckState
                Get
                    Return m_checkedAllState
                End Get
                Set(ByVal value As CheckState)
                    m_checkedAllState = value
                End Set
            End Property

            ''' <summary>
            ''' 用于标识当前的checkbox状态
            ''' </summary>
            Protected m_checkboxState As CheckBoxState = CheckBoxState.UncheckedNormal

            ''' <summary>
            ''' 是否是鼠标经过的这种hot状态,不是即为normal
            ''' </summary>
            Protected m_isHot As Boolean = False

            ''' <summary>
            ''' checkbox按钮区域
            ''' </summary>
            Protected m_chkboxRegion As Rectangle

            ''' <summary>
            ''' 相对于本cell的位置
            ''' </summary>
            Protected m_absChkboxRegion As Rectangle
#End Region
#Region "类型:重绘"
            Protected Overrides Sub Paint(ByVal graphics As System.Drawing.Graphics, ByVal clipBounds As System.Drawing.Rectangle, ByVal cellBounds As System.Drawing.Rectangle, ByVal rowIndex As Integer, ByVal dataGridViewElementState As DataGridViewElementStates, ByVal value As Object, ByVal formattedValue As Object, ByVal errorText As String, ByVal cellStyle As DataGridViewCellStyle, ByVal advancedBorderStyle As DataGridViewAdvancedBorderStyle, ByVal paintParts As DataGridViewPaintParts)
                MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, "", "", errorText, cellStyle, advancedBorderStyle, paintParts)
                Me.m_chkboxRegion = RectangleCommon.GetSmallRectOfRectangle(cellBounds, CheckBoxRenderer.GetGlyphSize(graphics, CheckBoxState.UncheckedNormal), m_absChkboxRegion)
                Me.RenderCheckBox(graphics)
            End Sub

            Protected Sub RenderCheckBox(ByVal graphics As Graphics)
                If m_isHot Then
                    RenderCheckBoxHover(graphics)
                Else
                    RenderCheckBoxNormal(graphics)
                End If

                CheckBoxRenderer.DrawCheckBox(graphics, m_chkboxRegion.Location, m_checkboxState)
            End Sub

            Protected Sub RenderCheckBoxNormal(ByVal graphics As Graphics)
                Select Case m_checkedAllState
                    Case CheckState.Unchecked
                        Me.m_checkboxState = CheckBoxState.UncheckedNormal
                    Case CheckState.Indeterminate
                        Me.m_checkboxState = CheckBoxState.MixedNormal
                    Case CheckState.Checked
                        Me.m_checkboxState = CheckBoxState.CheckedNormal
                End Select
            End Sub

            Protected Sub RenderCheckBoxHover(ByVal graphics As Graphics)
                Select Case m_checkedAllState
                    Case CheckState.Unchecked
                        Me.m_checkboxState = CheckBoxState.UncheckedHot
                    Case CheckState.Indeterminate
                        Me.m_checkboxState = CheckBoxState.MixedHot
                    Case CheckState.Checked
                        Me.m_checkboxState = CheckBoxState.CheckedHot
                End Select
            End Sub
#End Region

#Region "类型:事件"
            Protected Overrides Sub OnMouseMove(ByVal e As DataGridViewCellMouseEventArgs)
                MyBase.OnMouseMove(e)
                If IsInCheckRegion(e.Location) Then m_isHot = True
                Me.DataGridView.InvalidateCell(Me)
            End Sub

            Protected Overrides Sub OnMouseLeave(ByVal rowIndex As Integer)
                MyBase.OnMouseLeave(rowIndex)
                m_isHot = False
                Me.DataGridView.InvalidateCell(Me)
            End Sub

            Protected Overrides Sub OnMouseDown(ByVal e As DataGridViewCellMouseEventArgs)
                MyBase.OnMouseDown(e)
                m_isHot = IsInCheckRegion(e.Location)
                Me.DataGridView.InvalidateCell(Me)
            End Sub

            Protected Overrides Sub OnMouseClick(ByVal e As DataGridViewCellMouseEventArgs)
                Dim value As Boolean = False

                If IsInCheckRegion(e.Location) Then

                    Select Case m_checkedAllState
                        Case CheckState.Unchecked
                            m_checkedAllState = CheckState.Checked
                            value = True
                        Case CheckState.Indeterminate
                            m_checkedAllState = CheckState.Checked
                            value = True
                        Case CheckState.Checked
                            m_checkedAllState = CheckState.Unchecked
                            value = False
                    End Select

                    Me.Value = value
                    Me.DataGridViewEx.OnCheckAllCheckedChange(e.ColumnIndex, value)
                End If

                MyBase.OnMouseClick(e)
            End Sub

            ''' <summary>
            ''' 是否在checkbox按钮区域
            ''' </summary>
            ''' <param name="p"></param>
            ''' <returns></returns>
            Protected Function IsInCheckRegion(ByVal p As Point) As Boolean
                Return Me.m_absChkboxRegion.Contains(p)
            End Function
#End Region
        End Class
#End Region

        Public Class RectangleCommon
            ''' <summary>
            ''' 获取在大的Rectangle中的小矩形
            ''' </summary>
            ''' <param name="rectangle"></param>
            ''' <param name="smallSize"></param>
            ''' <param name="absRectangle"></param>
            ''' <returns></returns>
            Public Shared Function GetSmallRectOfRectangle(ByVal rectangle As Rectangle, ByVal smallSize As Size, <Out> ByRef absRectangle As Rectangle) As Rectangle
                Dim rect As Rectangle = New Rectangle()
                absRectangle = New Rectangle()
                absRectangle.Size = smallSize
                absRectangle.X = CType(((rectangle.Width - smallSize.Width) / 2), Integer)
                absRectangle.Y = CType((rectangle.Height - smallSize.Height) / 2, Integer)
                rect.Size = smallSize
                rect.X = absRectangle.X + rectangle.X
                rect.Y = absRectangle.Y + rectangle.Y
                Return rect
            End Function
        End Class

#Region "扩展单元格"
        Public Class DataGridViewCheckBoxCellEx
            Inherits DataGridViewCheckBoxCell

            ''' <summary>
            ''' 本单元格是否被选中
            ''' </summary>
            ''' <returns></returns>
            Public Property Checked As Boolean
                Get
                    Return Convert.ToBoolean(Me.Value)
                End Get
                Set(ByVal value As Boolean)
                    Me.Value = value
                End Set
            End Property

            ''' <summary>
            ''' 宿主列扩展
            ''' </summary>
            ''' <returns></returns>
            Friend ReadOnly Property OwningColumnEx As DataGridViewCheckBoxColumnEx
                Get
                    Return TryCast(Me.OwningColumn, DataGridViewCheckBoxColumnEx)
                End Get
            End Property

            ''' <summary>
            ''' 宿主datagridview扩展
            ''' </summary>
            ''' <returns></returns>
            Friend ReadOnly Property DataGridViewEx As DataGridViewEx
                Get
                    Return TryCast(Me.DataGridView, DataGridViewEx)
                End Get
            End Property

            ''' <summary>
            ''' 处理选中与取消选中
            ''' </summary>
            ''' <param name="e"></param>
            Protected Overrides Sub OnMouseClick(ByVal e As DataGridViewCellMouseEventArgs)
                If e.Button <> MouseButtons.Left Then Return
                Me.DataGridViewEx.EndEdit()
                Me.Checked = Not Me.Checked
                Me.DataGridViewEx.OnCheckBoxCellCheckedChange(e.ColumnIndex, e.RowIndex, Me.Checked)
                MyBase.OnMouseClick(e)
            End Sub
        End Class
#End Region
    End Namespace
End Namespace

打开Form1设计窗口,在工具箱里出现了我们自定义的DataGridViewEx控件,双击该控件为Form1添加该控件。
在这里插入图片描述
为DataGridViewEx控件添加列,从类型里可以找到自定义的DataGridViewCheckBoxColumnEx列,把该类型的列添加到DataGridViewEx控件中。
在这里插入图片描述
好了,现在可以测试了。效果如图:
在这里插入图片描述
在这里插入图片描述
不足之处:
仔细看是不是感觉到列头与各行的复选框没有对齐,但我也不知道问题出在哪里?

迷惑之处:
我在C#语言测试时看到是需要导入System、System.Windows.Forms、System.Drawing这些名称空间的,可是在我的VB.NET程序里却提示这些名称空间不是必须的,如图,没导入而且使用了缩写形式也不会报错。
在这里插入图片描述
还有名称空间与类的层次关系也很难弄懂。我想要的层次关系是:根名称空间(就是项目名称WinFormDataGridViewEx)——DataGridViewEx(就是扩展的DataGridView控件名,工具箱上看到的就是这个)——DataGridViewCheckBoxColumnEx(全选复选框列,就是添加列时看到的类型名称),但我总感觉弄出来的不是很对。如图:
在这里插入图片描述
初学,所以问题总是多多,相信在不断的学习过程会慢慢解除迷惑的!

猜你喜欢

转载自blog.csdn.net/zyjq52uys/article/details/86507712