1. 程式人生 > >VB.NET學習筆記:WinForm自定義DataGridView分頁組合控制元件

VB.NET學習筆記:WinForm自定義DataGridView分頁組合控制元件

測試環境:windows 7和Microsoft Visual Studio 2015

點選下載本文資源

VB.NET雖然提供了大量控制元件供我們使用,但很多控制元件僅提供最基礎的功能。比如用DataGridView控制元件可以非常方便顯示或操作資料庫資料, 但卻沒有分頁功能。本文介紹通過自定義窗體庫組合VB.NET已有控制元件實現DataGridView控制元件分頁顯示功能。

測試效果如圖:

DataGridView分頁控制元件效果圖
DataGridView控制元件分頁功能效果圖

這其實就是一個組合控制元件(CompositeControls),繼承自UserControl類,將目前現有的控制元件根據需要組合到一起形成一個新的控制元件。具體做法如下:

一、新建Windows窗體控制元件庫專案

如下圖所示,名稱按你喜歡的寫上就行。Windows窗體控制元件庫有視覺化的設計檢視,可以從工具箱新增VB.NET中已有的控制元件。

Windows窗體控制元件庫

如果新建的是Windows窗體應用程式,也可以通過右鍵單擊解決方案資源管理器中的專案名稱,在彈出的右鍵選單中點選“新增”-“新建項”,在彈出的“新建專案”視窗中選擇“使用者控制元件”,這個也是建自定義組合控制元件的,如下圖所示。

和
標題

二、佈局

在設計窗口裡新增如下圖所示的控制元件。

佈局效果圖

佈局技巧分享:

1、按鈕外觀美化:如下圖設定按鈕的FlatAppearance的BorderSize為0,即去除邊框,FlatStyle為Flat,即按鈕外觀為平面顯示,通過Image屬性為按鈕新增圖片。

按鈕Button外觀屬性設定

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

​

本文程式碼借鑑和參考了以下博文:

1、WinForm輕鬆實現自定義分頁

2、Winform 通用分頁控制元件實戰篇(提供原始碼下載) 

學習過程得到網友uruseibest的幫助,表示感謝!