测试环境:windows 7和Microsoft Visual Studio 2015
VB.NET虽然提供了大量控件供我们使用,但很多控件仅提供最基础的功能。比如用DataGridView控件可以非常方便显示或操作数据库数据, 但却没有分页功能。本文介绍通过自定义窗体库组合VB.NET已有控件实现DataGridView控件分页显示功能。
测试效果如图:
这其实就是一个组合控件(CompositeControls),继承自UserControl类,将目前现有的控件根据需要组合到一起形成一个新的控件。具体做法如下:
一、新建Windows窗体控件库项目
如下图所示,名称按你喜欢的写上就行。Windows窗体控件库有可视化的设计视图,可以从工具箱添加VB.NET中已有的控件。
如果新建的是Windows窗体应用程序,也可以通过右键单击解决方案资源管理器中的项目名称,在弹出的右键菜单中点选“添加”-“新建项”,在弹出的“新建项目”窗口中选择“用户控件”,这个也是建自定义组合控件的,如下图所示。
二、布局
在设计窗口里添加如下图所示的控件。
布局技巧分享:
1、按钮外观美化:如下图设置按钮的FlatAppearance的BorderSize为0,即去除边框,FlatStyle为Flat,即按钮外观为平面显示,通过Image属性为按钮添加图片。
2、TextBoxt和Lable设置:设置TextAlign可设置文本对齐方式,文本框和标签大小默认根据字号自动调整大小的,是不能通过Size属性来调整大小的,如果要调整Size属性,必须先把AutoSize属性设置为False。
3、控件对齐方式设置:大部分控件都可以通过设置Anchor属性来将控件绑定到容器的边缘,当组合控件大小整体改变时,里面的控件始终与绑定的边缘保持相同距离。
4、为控件设置鼠标划过文本提示:需要先从工具箱添加ToolTip控件,这时每个控件都多了一个ToolTip1上的ToolTip属性,在右侧设置的文本即为提示文本。
三、编写分页控件代码
布局好后就可以编写分页代码了。包括分页控件的属性和事件。
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.Data
Imports System.Text
Imports System.Windows.Forms
Public Class DataGridViewPaging
Inherits UserControl
#Region "构造函数"
Public Sub New()
InitializeComponent()
End Sub
#End Region
#Region "分页字段和属性"
Private _pageIndex As Integer = 1
''' <summary>
''' 当前页面
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Overridable Property PageIndex As Integer
Get
Return _pageIndex
End Get
Set(ByVal value As Integer)
If value > 0 Then
_pageIndex = value
Else
_pageIndex = 1
End If
End Set
End Property
Private _pageSize As Integer = 100
''' <summary>
''' 每页记录数(默认100)
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Overridable Property PageSize As Integer
Get
Return _pageSize
End Get
Set(ByVal value As Integer)
If value > 0 Then
_pageSize = value
Else
_pageSize = 100
End If
End Set
End Property
Private _recordCount As Integer = 0
''' <summary>
''' 总记录数
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Overridable Property RecordCount As Integer
Get
Return _recordCount
End Get
Set(ByVal value As Integer)
If value >= 0 Then
_recordCount = value
Else
_recordCount = 0
End If
End Set
End Property
Private _pageCount As Integer = 0
''' <summary>
''' 总页数
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property PageCount As Integer
Get
If _pageSize >= 0 Then
_pageCount = GetPageCount()
Else
_pageCount = 0
End If
Return _pageCount
End Get
End Property
''' <summary>
''' 计算总页数
''' </summary>
''' <returns>总页数(Integer)</returns>
''' <remarks></remarks>
Private Function GetPageCount() As Integer
Dim pageCount As Integer = 0
If RecordCount > 0 AndAlso PageSize > 0 Then
If RecordCount Mod PageSize = 0 Then
pageCount = CType(RecordCount / PageSize, Integer)
Else
pageCount = CType(RecordCount / PageSize, Integer) + 1
End If
End If
Return pageCount
End Function
Private _CurrentPageFirstRecord As Integer = 0
''' <summary>
''' 当前页面首记录
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property CurrentPageFirstRecord As Integer
Get
If RecordCount > 0 Then
If PageIndex = 1 OrElse PageCount = 1 Then '当前显示为第一页或只有一页
_CurrentPageFirstRecord = 1
Else
_CurrentPageFirstRecord = (PageIndex - 1) * PageSize + 1
End If
End If
Return _CurrentPageFirstRecord
End Get
End Property
Private _CurrentPageLastRecord As Integer = 0
''' <summary>
''' 当前页面末记录
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property CurrentPageLastRecord As Integer
Get
If RecordCount > 0 Then
If PageCount = 1 Then '只有一页
_CurrentPageLastRecord = RecordCount
Else '有多页
If PageIndex = 1 Then '当前显示为第一页
_CurrentPageLastRecord = PageSize
ElseIf PageIndex = PageCount Then '当前显示为最后一页
_CurrentPageLastRecord = RecordCount
Else '中间页
_CurrentPageLastRecord = PageIndex * PageSize
End If
End If
End If
Return _CurrentPageLastRecord
End Get
End Property
Private _jumpText As String = "Go"
''' <summary>
''' 跳转按钮文本
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property JumpText As String
Get
If String.IsNullOrEmpty(_jumpText) Then
_jumpText = "Go"
End If
Return _jumpText
End Get
Set(ByVal value As String)
_jumpText = value
BtnGo.Text = _jumpText
End Set
End Property
#End Region
#Region "页码变化触发事件"
'Public Event OnPageChanged As EventHandler
Public Delegate Sub EventPagingHandler(ByVal e As EventArgs)
Public Event EventPaging As EventPagingHandler
#End Region
#Region "分页及相关事件功能实现"
''' <summary>
''' 外部调用
''' </summary>
''' <param name="count">总记录数</param>
''' <remarks></remarks>
Public Sub DrawControl(ByVal count As Integer)
RecordCount = count
DrawControl(True)
End Sub
''' <summary>
''' 页面控件呈现
''' </summary>
''' <param name="callEvent">是否触发事件,Ture则触发</param>
''' <remarks></remarks>
Private Sub DrawControl(ByVal callEvent As Boolean)
'BtnGo.Text = JumpText
lblCurrentAndCountPage.Text = PageIndex.ToString() & "/" & PageCount.ToString()
LblRecordRegionAndCount.Text = "当前记录:" & CurrentPageFirstRecord.ToString() &
" - " & CurrentPageLastRecord.ToString() & " 总记录:" & RecordCount.ToString()
txtPageSize.Text = PageSize.ToString()
If callEvent Then
'RaiseEvent OnPageChanged(Me, Nothing) '当前分页数字改变时,触发委托事件
RaiseEvent EventPaging(New EventArgs())
End If
If PageIndex > PageCount Then PageIndex = PageCount
'初始化按钮
BtnFirst.Enabled = True
BtnPrev.Enabled = True
BtnNext.Enabled = True
BtnLast.Enabled = True
BtnGo.Enabled = True
If RecordCount = 0 OrElse PageCount = 1 Then '总记录为0或有且仅有一页
BtnFirst.Enabled = False
BtnPrev.Enabled = False
BtnNext.Enabled = False
BtnLast.Enabled = False
BtnGo.Enabled = False
ElseIf PageIndex = 1 Then '第一页
BtnFirst.Enabled = False
BtnPrev.Enabled = False
ElseIf PageIndex = PageCount Then '最后一页
BtnNext.Enabled = False
BtnLast.Enabled = False
End If
End Sub
#End Region
#Region "相关控件事件"
Private Sub BtnFirst_Click(sender As Object, e As EventArgs) Handles BtnFirst.Click
PageIndex = 1
DrawControl(True)
End Sub
Private Sub BtnPrev_Click(sender As Object, e As EventArgs) Handles BtnPrev.Click
PageIndex = Math.Max(1, PageIndex - 1)
DrawControl(True)
End Sub
Private Sub BtnNext_Click(sender As Object, e As EventArgs) Handles BtnNext.Click
PageIndex = Math.Min(PageCount, PageIndex + 1)
DrawControl(True)
End Sub
Private Sub BtnLast_Click(sender As Object, e As EventArgs) Handles BtnLast.Click
PageIndex = PageCount
DrawControl(True)
End Sub
''' <summary>
''' enter键功能
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub txtPageNum_KeyPress(sender As Object, e As KeyPressEventArgs) Handles txtPageNum.KeyPress
btnGo_Click(Nothing, Nothing)
End Sub
''' <summary>
''' 跳转页数限制
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub txtPageNum_TextChanged(sender As Object, e As EventArgs) Handles txtPageNum.TextChanged
Dim num As Integer = 0
If Integer.TryParse(txtPageNum.Text.Trim(), num) AndAlso num > 0 Then
If num > PageCount Then
txtPageNum.Text = PageCount.ToString()
End If
End If
End Sub
''' <summary>
''' 跳转
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub btnGo_Click(ByVal sender As Object, ByVal e As EventArgs) Handles BtnGo.Click
Dim num As Integer = 0
If Integer.TryParse(txtPageNum.Text.Trim(), num) AndAlso num > 0 Then
PageIndex = num
DrawControl(True)
End If
End Sub
''' <summary>
''' 标识每页记录条数是否改变,为True则重绘控件
''' </summary>
Private isTextChanged As Boolean = False
''' <summary>
''' 改变每页记录条数
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub txtPageSize_TextChanged(sender As Object, e As EventArgs) Handles txtPageSize.TextChanged
Dim num As Integer = 0
If Not Integer.TryParse(txtPageSize.Text.Trim(), num) OrElse num <= 0 Then
num = 100
txtPageSize.Text = "100"
Else
isTextChanged = True
End If
PageSize = num
End Sub
''' <summary>
''' 光标离开分页属性
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub txtPageSize_Leave(sender As Object, e As EventArgs) Handles txtPageSize.Leave
If isTextChanged Then
isTextChanged = False
DrawControl(True)
'BtnFirst_Click(Nothing, Nothing)
End If
End Sub
#End Region
End Class
代码重点解读:
1、Windows窗体控件库继承自UserControl类,即:
Public Class DataGridViewPaging
Inherits UserControl
End Class
2、委托事件:
如下定义了分页委托事件。
#Region "页码变化触发事件"
'Public Event OnPageChanged As EventHandler
Public Delegate Sub EventPagingHandler(ByVal e As EventArgs)
Public Event EventPaging As EventPagingHandler
#End Region
然后引发事件。
''' <summary>
''' 页面控件呈现
''' </summary>
''' <param name="callEvent">是否触发事件,Ture则触发</param>
''' <remarks></remarks>
Private Sub DrawControl(ByVal callEvent As Boolean)
If callEvent Then
'RaiseEvent OnPageChanged(Me, Nothing) '当前分页数字改变时,触发委托事件
RaiseEvent EventPaging(New EventArgs())
End If
End Sub
添加好代码后就可以测试了:调试——开始调试,如图所示。
四、调用分页控件
1、为项目添加新项:如图所示,添加一个Windows窗体。
2、从工具箱向Form1.vb中添加一个DataGridView控件和刚才做好的分页控件DataGridViewPaging。
打开工具箱,就会见到分页控件,如图所示,如果没有看到,你需要重新生成或调试一次就可以看到。
3、向Form1.vb编写代码。
窗体加载时订阅事件:AddHandler Me.DataGridViewPaging1.EventPaging, AddressOf DataGridViewPaging1_EventPaging;
绑定方法:Private Sub DataGridViewPaging1_EventPaging(e As EventArgs)
End Sub
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.DataGridView1.AllowUserToAddRows = False
'订阅事件
AddHandler Me.DataGridViewPaging1.EventPaging, AddressOf DataGridViewPaging1_EventPaging
Me.DataGridViewPaging1.DrawControl(0)
End Sub
''' <summary>
''' 关联事件的方法,加载数据库的数据
''' </summary>
''' <param name="e"></param>
Private Sub DataGridViewPaging1_EventPaging(e As EventArgs)
If Me.DataGridView1.Rows.Count > Me.DataGridViewPaging1.PageSize Then
For i As Integer = Me.DataGridView1.Rows.Count - 1 To Me.DataGridViewPaging1.PageSize Step -1
Me.DataGridView1.Rows.Remove(Me.DataGridView1.Rows(i))
Next
ElseIf Me.DataGridView1.Rows.Count < Me.DataGridViewPaging1.PageSize Then
Me.DataGridView1.Rows.Add(Me.DataGridViewPaging1.PageSize - Me.DataGridView1.Rows.Count)
End If
If Me.DataGridViewPaging1.RecordCount = 0 Then Exit Sub
Dim j As Integer = -1
For i As Integer = Me.DataGridViewPaging1.CurrentPageFirstRecord To Me.DataGridViewPaging1.CurrentPageLastRecord
j += 1
Me.DataGridView1.Rows(j).Cells(0).Value = i
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.DataGridViewPaging1.DrawControl(Integer.Parse(Me.TextBox1.Text))
End Sub
End Class
4、设置项目属性。
设置应用程序类型为Windows窗体应用程序,启动对象设置为Form1。
5、开始调试。效果如开头效果图。
调试问题:1、修改每页记录文本框时,页面不能正确显示当前页。如图所示。
2、修改跳转页码文本框的页码大于总页码时,没有点击跳转按钮就会触发事件,也就是代码运行顺序是不是存在一定问题,有待各位拍砖。
3、很迷惑,控件开头的命名空间为何可以不用导入?
'问题:这些命名空间都用不上吗?
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.Data
Imports System.Text
Imports System.Windows.Forms
Public Class DataGridViewPaging
Inherits UserControl
End Class
本文代码借鉴和参考了以下博文:
学习过程得到网友uruseibest的帮助,表示感谢!