VB.NET学习笔记:自定义控件之扩展DataGridViewColumnHeaderCell类增加CheckBox全选复选框
发布日期:2021-06-29 11:32:11 浏览次数:2 分类:技术文章

本文共 11874 字,大约阅读时间需要 39 分钟。

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

VB.NET虽然提供了大量控件供我们使用,但很多控件仅提供最基础的功能。比如用DataGridView控件可以非常方便显示或操作数据库数据,我们可以在首列添加DataGridViewCheckBoxColumn列进行全选或全不选操作,但Datagridview控件并没有提供我们平时用的全选或取消全选的复选框,他的表头就只有这一列的名称,这样会影响用户的使用体验。这就需要我们对DataGridViewCheckBoxColumn列的列头进行扩展,创建符合使用要求的自定义控件。
一、自定义控件介绍
VB.NET中的窗体(WinFrom)自定义控件大致有三种形式:
1、组合控件(CompositeControls):继承自UserControl类,将目前现有的控件根据需要组合到一起形成一个新的控件。
2、扩展控件(ExtendedControls):继承自.NET类库中已有的控件,添加一些新的属性和方法来扩展原有控件。
3、自定义控件(CustomControls):继承自Control类,不提供控件特定的功能或图形界面,控件的绘制全部由用户定义。
二、扩展DataGridViewColumnHeaderCell类
可以在DataGridViewCheckBoxColumn列的列头拉一个CheckBox来实现全选全不选,但CheckBox的位置不好调控。最好的方法是扩展DataGridViewColumnHeaderCell类,在列头绘制一个复选框checkbox控件,通过定义checkbox鼠标单击事件来实现行的全选或取消全选。
效果如下图所示:
全选效果图
单击单元格事件效果图
1、新建一个windows窗体应用程序项目。
新建项目-windows窗体应用程序
为项目添加新项,取名DataGridViewCheckBoxColumnHeaderCellEx,类库。如下图所示整个项目包含一个类库和一个窗体。
项目包含2项
2、用下面代码替换类库DataGridViewCheckBoxColumnHeaderCellEx.vb中自动生成的代码。

Option Strict OnOption Infer OffOption Explicit On'Imports System'Imports System.Collections.Generic'Imports System.Text'Imports System.Windows.Forms'Imports System.DrawingImports System.Windows.Forms.VisualStylesImports System.Runtime.InteropServices#Region "扩展表头单元格"Public Class DataGridViewCheckBoxColumnHeaderCellEx    Inherits DataGridViewColumnHeaderCell    ' 委托处理DataGridViewCheckBoxClickedHandler事件    Public Delegate Sub DataGridViewCheckBoxClickedHandler(ByVal columnIndex As Integer, ByVal isCheckedAll As Boolean)    Public Event OnCheckBoxClicked As DataGridViewCheckBoxClickedHandler#Region "属性"    '''     ''' 标识本列是否全选,方便在Cell中获取    '''     ''' 
Public ReadOnly Property IsCheckedAll As Boolean Get Return CheckedAllState = CheckState.Checked End Get End Property Private m_checkedAllState As CheckState = CheckState.Unchecked ''' ''' 全选按钮状态 ''' '''
Public Property CheckedAllState As CheckState Get Return m_checkedAllState End Get Set(ByVal value As CheckState) m_checkedAllState = value End Set End Property ''' ''' 用于标识当前的checkbox状态 ''' Protected m_checkboxState As CheckBoxState = CheckBoxState.UncheckedNormal ''' ''' 是否是鼠标经过的这种hot状态,不是即为normal ''' Protected m_isHot As Boolean = False ''' ''' checkbox按钮区域 ''' Protected m_chkboxRegion As Rectangle ''' ''' 相对于本Headercell的位置 ''' Protected m_absChkboxRegion As Rectangle#End Region#Region "重绘" ''' ''' 在本Headercell绘制CheckBox控件 ''' ''' ''' ''' ''' ''' ''' ''' ''' ''' ''' ''' Protected Overloads Overrides Sub Paint(ByVal graphics As Graphics, ByVal clipBounds As Rectangle, ByVal cellBounds As 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 = GetSmallRectOfRectangle(cellBounds, CheckBoxRenderer.GetGlyphSize(graphics, CheckBoxState.UncheckedNormal), m_absChkboxRegion) Me.RenderCheckBox(graphics) End Sub ''' ''' 按当前的checkbox状态重绘checkbox ''' ''' 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 ''' ''' 标识checkbox状态(normal) ''' ''' 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 ''' ''' 标识checkbox状态(hot) ''' ''' 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 ''' ''' 获取本Headercell的Rectangle及其中的checkbox控件的Rectangle ''' ''' ''' checkbox控件的Size ''' checkbox控件的Rectangle '''
Protected Shared Function GetSmallRectOfRectangle(ByVal rectangle As Rectangle, ByVal smallSize As Size,
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 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 Overloads 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 '引发事件 RaiseEvent OnCheckBoxClicked(e.ColumnIndex, value) Me.DataGridView.InvalidateCell(Me) End If MyBase.OnMouseClick(e) End Sub '''
''' 是否在checkbox按钮区域 ''' '''
'''
Protected Function IsInCheckRegion(ByVal p As Point) As Boolean Return Me.m_absChkboxRegion.Contains(p) End Function#End Region '''
''' checkbox列的单元格改变事件 ''' '''
Friend Sub OnCheckBoxCellCheckedChange(ByVal columnIndex As Integer) If columnIndex <> Me.ColumnIndex Then Return Dim existsChecked As Boolean = False, existsNoChecked As Boolean = False For Each row As DataGridViewRow In Me.DataGridView.Rows existsChecked = existsChecked Or CType(row.Cells(columnIndex).EditedFormattedValue, Boolean) existsNoChecked = existsNoChecked Or Not CType(row.Cells(columnIndex).EditedFormattedValue, Boolean) Next Dim oldState As CheckState = Me.CheckedAllState If existsChecked Then If existsNoChecked Then Me.CheckedAllState = CheckState.Indeterminate Else Me.CheckedAllState = CheckState.Checked End If Else Me.CheckedAllState = CheckState.Unchecked End If If oldState <> Me.CheckedAllState Then Me.DataGridView.InvalidateCell(Me) End SubEnd Class#End Region

代码重点解读:

(1)、新DataGridViewCheckBoxColumnHeaderCellEx类,继承自DataGridViewColumnHeaderCell。

Public Class DataGridViewCheckBoxColumnHeaderCellEx    Inherits DataGridViewColumnHeaderCellEnd Class

心得:通常组合控件用“用户控件”;;自定义控件用“组件类”或“自定义控件”;;扩展没有单独界面控件用“类”,如本例,DataGridViewColumnHeaderCell类不能单独显示界面,只能在DataGridView控件显示时一同显示,所以扩展该类的DataGridViewCheckBoxColumnHeaderCellEx类只能用“类”;而扩展有单独界面控件需用“组件类”或“自定义控件”。其中奥妙你只要试试就知道了。

自定义控件用到的库

(2)、委托和事件

' 委托处理DataGridViewCheckBoxClickedHandler事件    Public Delegate Sub DataGridViewCheckBoxClickedHandler(ByVal columnIndex As Integer, ByVal isCheckedAll As Boolean)    Public Event OnCheckBoxClicked As DataGridViewCheckBoxClickedHandler

在鼠标单击时触发该事件。

Protected Overloads Overrides Sub OnMouseClick(ByVal e As DataGridViewCellMouseEventArgs)            '引发事件            RaiseEvent OnCheckBoxClicked(e.ColumnIndex, value)    End Sub

在窗体引用DataGridViewCheckBoxColumnHeaderCellEx类时订阅事件。

'订阅事件        AddHandler checkbox1.OnCheckBoxClicked, AddressOf Checkbox_OnCheckboxClicked

(3)、一个函数过程返回2个值。如下代码Return rect返回本Headercell的Rectangle,absRectangle返回绘制的checkbox控件的Rectangle。

'''     ''' 获取本Headercell的Rectangle及其中的checkbox控件的Rectangle    '''     '''     ''' checkbox控件的Size    ''' checkbox控件的Rectangle    ''' 
Protected Shared Function GetSmallRectOfRectangle(ByVal rectangle As Rectangle, ByVal smallSize As Size,
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

三、调用DataGridViewCheckBoxColumnHeaderCellEx类

用如下代码替换Form1.vb里自动生成的代码。

Public Class Form1    '定义一眉头Checkbox1从类DataGridViewCheckBoxColumnHeaderCellEx构造而来    Private checkbox1 As New DataGridViewCheckBoxColumnHeaderCellEx    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load        '新列增加一个控件Checkbox1        Me.DataGridView1.Columns(0).HeaderCell = checkbox1        Me.DataGridView1.Rows.Add(5)        Me.DataGridView1.AllowUserToAddRows = False        '订阅事件        AddHandler checkbox1.OnCheckBoxClicked, AddressOf Checkbox_OnCheckboxClicked    End Sub    Private Sub Checkbox_OnCheckboxClicked(ByVal columnIndex As Integer, ByVal isCheckedAll As Boolean)        Me.DataGridView1.EndEdit() '结束编辑操作        For Each Row As DataGridViewRow In Me.DataGridView1.Rows            Row.Cells(columnIndex).Value = isCheckedAll        Next    End Sub    '''     ''' 单击列单元格的内容事件    '''     '''     '''     Private Sub DataGridView1_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick        Me.DataGridView1.EndEdit() '结束编辑操作        Me.checkbox1.OnCheckBoxCellCheckedChange(e.ColumnIndex)    End SubEnd Class

代码重点解读:

在处理单击列头实现全选或取消全选操作、单击单元格操作时,必须结束单元格的编辑状态,否则无法正确获取修改后的单元格内容,造成处于编辑状态的单元格无法改变勾选状态或在单击单元格时列头的显示与实际不相符。

Me.DataGridView1.EndEdit() '结束编辑操作

处在编辑状态的单元格无法勾选,造成操作错误。

在参考的文章里看到还要新建一个datagridviewCheckboxHeaderEventArgs类,继承自EventArgs类,用在在checkbox单击事件中提供类头checkbox的选择状态。我的项目里没有用上,具体有什么作用不详,望能得到指点。

'定义包含列头checkbox选择状态的参数类Class datagridviewCheckboxHeaderEventArgs    Inherits EventArgs    Private checkedState As Boolean = False    Public Property CheckedState As Boolean        Get            Return checkedState        End Get        Set(ByVal value As Boolean)            checkedState = value        End Set    End PropertyEnd Class

本文参考以下文章:

1、
2、
学习过程得到网友的帮助,表示感谢!

转载地址:https://blog.csdn.net/zyjq52uys/article/details/85272588 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:VB.NET学习笔记:ADO.NET操作ACCESS数据库——在内存中构建数据库(DataSet数据集和DataTable数据表)
下一篇:2021-06-17

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2024年04月15日 06时27分15秒