1. 程式人生 > >【轉學習筆記】Excel VBA:以員工信息查詢為例,學習操作窗體

【轉學習筆記】Excel VBA:以員工信息查詢為例,學習操作窗體

同名 快速查找 關註 建立 tin startup 由於 var emc

基於“窗體”實現Excel表格內容的查詢與修改。窗體,為使用者提供了較好的交互體驗,使用者可以按照自己的需要實現數據的查詢與修改,類似於數據庫中的視圖,用簡潔明了的界面提供給使用者最關註的數據,屏蔽無用的數據信息。本文用一個查詢員工信息的例子,簡要介紹窗體的使用方法。

員工信息查詢的例子

構造這樣一個實例:某企業員工信息,包含姓名、性別、出生年月、政治面貌、學歷學位等,每個員工的信息組合成一條記錄,所有員工信息整合在一個Excel數據表中。樣式如下:

技術分享圖片

圖中信息均為隨機虛構,請勿對號入座。

該表中只有10名員工信息,且員工信息相對較少,因此直觀上看還是比較簡潔的。但是設想一下,如果該企業有上千名員工,每個員工信息包含幾十甚至上百個字段,那又會是如何一番景象呢?如果去尋找某位員工的某些信息,可能體驗就不是那麽友好了,需要好好擦亮雙眼了。

每位員工應該都有一個唯一的員工號的,作為該員工信息記錄的識別碼(主鍵)。為了方便,我在這個例子中並未添加該字段信息,後續查找也是基於姓名來的,這要求姓名不能重名,否則不能查找到所有同名員工的信息。簡要說明,能認識到這點足夠了,問題很小很容易解決。

想要實現的功能

日常工作中,並不是所有的員工信息都會被經常用到,恰恰相反,經常用到的字段,一般是固定的幾個。當表格信息較多時,去尋找某個記錄的某些字段信息,是比較耗時耗力的。如果可以根據提供的某條記錄的標識符,就可以依據該標識符快速展示出屬於該記錄的字段數據,並且能夠屏蔽其他不關心的數據,那麽體驗就會大幅提升。

在該例中,我們基於窗體實現了快速信息查詢及修改的功能。只需要提供員工姓名,就可快速查找出預設字段的該員工的數據信息。預設字段是我們關註的字段,比如性別、身份證號、政治面貌等。同時,預設字段也可以是表格中不存在的字段,比如年齡,年齡是隨時間不斷變化的,即使表格中有年齡數據,也可能由於更新不及時等原因,造成數據不準確。在窗體中添加年齡項,通過一定的運算,依據表格中的出生年月計算出年齡。這樣,就可以通過姓名,隨時查詢每個員工的最新年齡信息。

查詢示例如下圖所示:

技術分享圖片

通過窗體顯示數據信息,既可以屏蔽部分垃圾信息,使數據顯示簡潔友好,又可以形成固定的計算模板,獲取原表格中不存在的字段信息,無需每次查詢都要重新計算。除可以顯示信息外,窗體也可以作為修改表格數據的渠道,只需要在查詢數據顯示框內,更新最新數據,提交修改即可。與直接在原表修改相比,因為界面簡潔明了,不存在幹擾信息,指向性明顯,很大程度上降低了修改出錯的可能性。

技術分享圖片

新建窗體

下面拋開具體實例,簡要介紹窗體的基本使用方法。

一、新建一個窗體

在“開發工具”中點擊“查看代碼”,在彈出的代碼編輯框中點擊“插入”——“用戶窗體”,可以看到如下界面:

技術分享圖片

其中,UserForm1即新建的窗體,只需要在上面添加窗體控件即可。我們可以在屬性設置中給窗體修改一個名字,只需要將屬性(姓名)設置為需要的名稱,例如“test”,如圖5所示:

技術分享圖片

二、窗體工具箱

默認新建窗體的時候,會同步打開“工具箱”,這裏重點關註工具箱中的五個控件選項,依次是:

選定對象:功能類似於pdf文件中的小手,用來選中某個已建好的控件;

標簽:圖2中“姓名”“性別”等文字標簽;

文字框:圖2中“性別”“民族”等後面緊跟著用於填充具體值的文字框;

復合框:圖2中“姓名”後面緊跟的帶有下拉選項的復合框;

命令按鈕:圖2中“查詢”和“提交修改”按鈕。

三、新建窗體控件

新建一個窗體控件(標簽、文本框、復合框等)的時候,只需要從工具箱中選中對應控件按鈕,然後在窗體上單擊即可,需要對控件進行簡單設置。

  • 新建“標簽”:設置標簽屬性,如圖6:

技術分享圖片

重點修改3個參數值:

Caption:標簽的文字內容,例如“姓名”;

Font:調節標簽文字字體及字號;

TextAlign:字體對齊方式:左對齊、居中、右對齊。

其他參數請自行學習。

  • 新建“文本框”:如圖7,重點修改3個參數值:

技術分享圖片

其中Font和TextAlign分別對應字體字號和文字對齊方式。

屬性(名稱):指定控件的名稱,設置名稱後,引用控件後就可以通過姓名.Value姓名.Name的方式分別引用控件的值和名稱了。

  • 新建“復合框”:復合框的參數設置和文本框類似,重點也是(名稱)、Font和TextAlign三個參數。復合框需要指定下拉選項的可選項值,一般通過VBA代碼的形式,在窗體初始化的時候指定,後續會詳細介紹。
  • 新建“命令按鈕”:同“文本框”,需要設置(名稱)、Font和TextAlign三個參數參數,其中屬性(名稱)在編寫命令提交執行代碼的時候,(名稱)的值即點擊命令按鈕時執行的過程的過程名。

三、運行窗體

在窗體設計界面,直接按快捷鍵F5即可運行。

窗體代碼控制

窗體建好後,如何實現窗體功能呢?自然是通過窗體後面編寫的VBA代碼實現的。

一、窗體初始化

回歸到員工信息查詢的實例。假如我們已經根據自身需要,建立了滿足自身需求的窗體,如圖8所示:

技術分享圖片

窗體建立好後,需要將其運行起來。只需要雙擊窗體空白處,即可跳轉到窗體代碼編輯頁面,會自動生成一個窗體初始化過程:

Private Sub UserForm_Initialize()

End Sub

我們只需要在該過程中填寫相關內容,完成窗體初始化。在員工信息查詢實例中,初始化過程只需要執行一個操作,即復合框可選內容的填充。從功能角度來講,就是讀取所有員工姓名信息,提供姓名下拉框姓名選項。

實現過程非常的簡單,只需要讀取姓名存儲到一個數組中,然後通過“復合框名稱.List=數組”的方式完成。具體實現過程如下:

‘窗體初始化
Private Sub UserForm_Initialize()

    ‘表格已使用區域行數
    Dim num As Integer
    num = ThisWorkbook.Sheets(2).Range("A1").CurrentRegion.Rows.Count

    ‘姓名保存到數組中
    Dim Names As Variant
    Names = ThisWorkbook.Sheets(2).Range("C3:C" & num).Value

    ‘窗體姓名復合框內容
    姓名.List = Names

End Sub

從以上語句中可以知道,員工姓名要保存在C列,且從第3行開始作為第一個員工信息(前兩行是表頭信息)。因為一開始不知道員工人數,因此通過讀取表格總使用行數來間接確定員工人數。具體的實現方式是,新建一個動態數組,將員工姓名保存到動態數組中,然後將該數組賦值給復合框。

運行窗體,我們就可以看到如圖9所示的效果。

技術分享圖片

二、添加控件動作

根據功能設計,窗體中有兩個命令按鈕:查詢和提交修改。查詢用來查找指定員工的信息,提交修改用來修改某位員工的某些字段值。

1.查詢命令按鈕

指定姓名可以通過下拉框選擇,也可以手動輸入,然後點擊查詢按鈕,將該員工信息填充到相應的文本框中。雙擊查詢按鈕,會直接跳轉到窗體代碼編寫頁面,並自動生成一個子過程:

Private Sub SelectForm_Click()

End Sub

該過程的名稱以“命令按鈕名稱”+“_Click()”的方式命名。這裏我把查詢命令按鈕名稱設置為SelectForm.

查詢過程實現的功能:
-- 容錯機制:未指定姓名、姓名不存在等情形下的錯誤提示;
-- 識別各文本框名稱,並獲取對應值。

如果設置ctl為文本框對象(TextBox),則文本框的名稱和值分別通過“ctl.Name”和“ctl.Value”獲取。

在這裏,我構造了一個函數,用來查詢指定姓名在工作簿中的行,具體過程如下:

‘查找指定姓名所在的行
Function FindNameRow()

    ‘已使用行數
    Dim num As Integer
    num = ThisWorkbook.Sheets(2).Range("A1").CurrentRegion.Rows.Count

    ‘依次測試每個單元格的值,如果是指定值,則返回指定值所在的行
    Dim rng As Range, row As Long
    For Each rng In ThisWorkbook.Sheets(2).Range("C3:C" & num)
        If rng.Value = 姓名.Value Then
            row = rng.row
            Exit For
        End If
    Next

    FindNameRow = row

End Function

該函數不需要參數。“姓名.Value”可以直接在函數內部引用。

姓名保存在C列,也就是第3列,這一點是固定的,用作模板要求,不能變更。因為一個員工的所有信息儲存在同一行內,只要獲取到該員工姓名所在的行,也就知道了該員工所有信息所在的行。對於其他字段,同一個員工的信息肯定和姓名在同一行,只需要定位該字段的列,就可以定位出此字段的值在工作表中的位置。因此,我同時定義了一個函數,用來查找指定字段名所在的列,具體如下:

‘查找各字段所在的列(數值,第幾列)
Function FindItemColumn(item)

    ‘已使用區域列數
    Dim num As Integer
    num = ThisWorkbook.Sheets(2).Range("A1").CurrentRegion.Columns.Count

    ‘依次測試包含字段名的單元格區域,如果是指定字段名,則返回所在的列(數值,第幾列)
    Dim rng As Range, col As Integer
    For Each rng In ThisWorkbook.Sheets(2).Range("A2", ThisWorkbook.Sheets(2).Cells(2, num))
        If rng.Value = item Then
            col = rng.column
            Exit For
        End If
    Next

    FindItemColumn = col

End Function

該函數參數為字段名,該字段名通過窗體文本框名稱給定(設置窗體的時候,將文本框名稱設置為該文本框需要顯示的內容對應的字段名稱)。例如顯示性別的文本框名稱設置為“性別”,員工信息工作表中儲存性別信息的字段名稱也為“性別”,這兩個名稱需要保持一致。函數會找到“性別”這個字段所在的列(第幾列),通過人名確定行,由此確定了該員工性別所在的行和列,也就可以提取到對應的值了。

給窗體中“性別”文本框賦值,只需要通過“性別.Value=sht.Cells(row,col).Value”,其中,sht代表保存員工信息的工作表對象,row和col分別是通過上述函數獲取的行和列。

還有最後一個問題,窗體中文本框可能有多個,我們不可能挨個指定文本框名稱去獲取對應的值。因此,這裏我引用了Control對象,通過對窗體對象的遍歷,尋找到所有類型為TextBox(文本框)的控件,獲取該控件的名稱,用該名稱去匹配工作表中所有字段名(前面提到的將文本框名稱和對應字段名設置為一致的原因),找到完全匹配的字段名所在的列,也就是該文本框需要顯示的值所在的列,行通過姓名確定。行和列確定後,該文本框的值就可以直接獲取了。

完整實現代碼如下:

‘點擊“查詢”按鈕後將要執行的操作
Private Sub SelectForm_Click()

    ‘處理未指定姓名的查詢
    If Len(姓名.Value) = 0 Then
        MsgBox ("請先指定需要查詢的人員姓名!")
        Exit Sub
    End If

    ‘以姓名作為查詢條件,獲取指定姓名的人員信息所在的行
    Dim row As Long
    row = FindNameRow()

    ‘處理查詢不存在的情形
    If row < 3 Then
        MsgBox ("無法查詢到姓名為" & """" & 姓名.Value & """" & "的人員信息,請確認後再試!")
        Exit Sub
    End If

    ‘item指定要填充的字段名,col代表查找該字段所在的列(數字,第幾列)
    Dim item As String, col As Integer
    Dim ctl As Control

     ‘判斷窗體中文本框內容是否有更改,如果有更改則更新工作簿對應字段值
    For Each ctl In WorkerInfo.Controls
        ‘如果是窗體文本框
        If TypeName(ctl) = "TextBox" Then
            col = FindItemColumn(ctl.Name)
            ‘如果col <> 0,表示能夠在工作簿中查詢到該字段,能找到才能修改
            If col <> 0 Then
                If ctl.Name <> "所在部門" Then
                    ctl.Value = ThisWorkbook.Sheets(2).Cells(row, col).Value
                Else
                    ctl.Value = ThisWorkbook.Sheets(2).Range(ThisWorkbook.Sheets(2).Cells(row, col), ThisWorkbook.Sheets(2).Cells(row, col)).MergeArea.Cells(1, 1).Value
                End If
            End If

        End If

    Next

    ‘根據出生年月計算並顯示年齡
    Dim date1 As String, date2 As String
    date1 = 出生年月.Value
    date2 = Application.Text(Date, "yyyy/mm/dd")
    年齡.Value = Application.Evaluate("=DATEDIF(""" & date1 & """,""" & date2 & """, ""y"") ")

End Sub

再回頭看一下容錯機制,首先明確一點,“姓名.Value”的類型是String,查詢姓名的時候,有可能姓名沒有輸入,這時候“姓名.Value”的長度為0,只需要檢測其長度,即可實現錯誤識別。還有一種錯誤是,輸入的姓名不在員工信息表中,那麽自定義的函數FindNameRow返回值為0,通過檢測函數返回值,即可識別錯誤。

2.提交修改按鈕

指定姓名點擊查詢後,更改該員工某個字段值,或者直接指定姓名,填寫需要修改的字段值,然後點擊提交修改,都可以完成該功能。

該部分實現起來與查詢有很多相似的地方,基本思想是核對每個文本框值與工作表中對應字段值是否一致,如果不一致,我們認為是進行了修改,只需要將工作表中對應字段值修改。這裏有幾個問題:

-- 容錯機制:如果沒有指定姓名或者姓名不存在,處理方式同查詢;
-- 因窗體文本框值類型為String,對於工作簿中的類型為Date或者Double等類型的數據時,需要統一數據類型後再進行比較,並且修改工作表值的時候,也要按照工作表字段類型進行修改;
-- 如果窗體文本框中的值格式不正確(非精確判斷,提供一種基本的解決思路,只針對明顯錯誤,例如日期填寫為“男”),則進行錯誤提示;
-- 依舊引用Control對象,獲取文本框值和工作表中對應字段值,進行比較,如果有差異,則將文本框值(類型調整後)更新到工作表中。

該部分完整代碼如下:

‘點擊“提交修改”按鈕後執行的操作
Private Sub SubmitModify_Click()


    ‘如果沒有指定員工姓名,則彈窗提醒並退出執行
    If Len(姓名.Value) = 0 Then
        MsgBox "請先指定員工姓名後提交修改!"
        Exit Sub
    End If

    ‘以姓名作為查詢條件,獲取指定姓名的人員信息所在的行
    Dim row As Long
    row = FindNameRow()

    ‘處理查詢不存在的情形
    If row < 3 Then
        MsgBox ("無法查詢到姓名為" & """" & 姓名.Value & """" & "的人員信息,請確認後再試!")
        Exit Sub
    End If

    ‘ctl代表窗體中文本框對象,col代表查找該字段所在的列(數字,第幾列)
    Dim ctl As Control
    Dim col As Integer

    ‘ctl_value用於修正數據類型,msg構造彈窗修改提示信息
    Dim ctl_value As String, msg As String

    ‘判斷窗體中文本框內容是否有更改,如果有更改則更新工作簿對應字段值
    For Each ctl In WorkerInfo.Controls

        ‘如果是窗體文本框
        If TypeName(ctl) = "TextBox" Then
            col = FindItemColumn(ctl.Name)

            ‘如果col <> 0,表示能夠在工作簿中查詢到該字段,能找到才能修改
            If col <> 0 Then

                ‘窗體文本框值的類型均為字符串,遇到其他類型數據需要更改數據類型
                If TypeName(ThisWorkbook.Sheets(2).Cells(row, col).Value) = "Date" Then
                    ‘如果出錯,則報錯
                    On Error GoTo ERROR_1
                    ctl_value = CDate(ctl.Value)
                    ‘如果上句不出錯,跳過報錯語句
                    GoTo CONTINUE_DO
ERROR_1:
                    MsgBox """" & ctl.Name & """" & "日期格式不正確,請輸入參照“2019/01/01”的格式輸入!"
                    Err.Clear
                    GoTo NEXT_DO

                ElseIf TypeName(ThisWorkbook.Sheets(2).Cells(row, col).Value) = "Double" Then
                    ‘如果不是數值型字符串,則執行報錯語句
                    If IsNumeric(ctl.Value) Then
                        ctl_value = Val(ctl.Value)
                        GoTo CONTINUE_DO
                    Else
                        GoTo ERROR_2
                    End If

                    ‘On Error GoTo ERROR_2
                    ‘ctl_value = Val(ctl.Value)
                    ‘GoTo CONTINUE_DO

ERROR_2:
                    MsgBox """" & ctl.Name & """" & "請輸入數字!"
                    Err.Clear
                    GoTo NEXT_DO
                Else
                    ctl_value = ctl.Value
                End If

CONTINUE_DO:
                ‘如果窗體文本框中值有更改(工作簿中的值和窗體文本框值不同)
                If ThisWorkbook.Sheets(2).Cells(row, col).Value <> ctl_value And ctl.Name <> "所在部門" Then
                    ‘彈窗提示是否確認修改
                    msg = "確定將" & """" & ctl.Name & """" & "由" & """" & ThisWorkbook.Sheets(2).Cells(row, col).Value & """" & "修改為" & """" & ctl.Value & """" & "嗎?"
                    If MsgBox(msg, vbYesNo) = vbYes Then
                        ‘不是“所在部門”這個字段(合並單元格需要單獨處理)
                        If ctl.Name <> "所在部門" Then
                            ThisWorkbook.Sheets(2).Cells(row, col).Value = ctl_value
                        Else
                            ThisWorkbook.Sheets(2).Range(ThisWorkbook.Sheets(2).Cells(row, col), ThisWorkbook.Sheets(2).Cells(row, col)).MergeArea.Cells(1, 1).Value = ctl_value
                        End If
                    End If
                End If

            End If

        End If

NEXT_DO:

    Next

    ‘保存更改
    ThisWorkbook.Save
    ‘更新查詢結果(重新執行一次查詢)
    Call SelectForm_Click

End Sub

針對格式不正確的填充數據,糾錯機制借助了好幾處的goto語句實現,比較啰嗦,也許有更好的實現方式,歡迎指正。

到目前為止,窗體的基本設置就完成了,現在可以在窗體設計界面按F5運行查看了。

設置查詢入口

我們並不希望每次運行都要先打開“開發工具”——“查看代碼”,那麽,只需要再添加一個入口按鈕就可以了。

我們在“開發工具”——“查看代碼”——“ThisWorkbook”中添加如下過程:

Public Sub ShowForm()
    With WorkerInfo
        .startupposition = 0
        .Top = 100
        .Left = 200
        .Show
    End With
End Sub

該過程指定了窗體的初始位置。然後將員工信息表保存在工作簿的第二張sheet表中,第一張sheet表設置一個窗體查詢入口,如圖10所示:

技術分享圖片

點擊“開始查詢”就可以彈出窗體了。

【轉學習筆記】Excel VBA:以員工信息查詢為例,學習操作窗體