测试环境: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(全选复选框列,就是添加列时看到的类型名称),但我总感觉弄出来的不是很对。如图:
初学,所以问题总是多多,相信在不断的学习过程会慢慢解除迷惑的!