You are on page 1of 346

免費下載-分享--Excel VBA 應用教程—

目錄
一、VBA 語言基礎...................................................................................................................1
第一節識別字...................................................................................................................................1
第二節運算子...................................................................................................................................1
第三節資料類型...............................................................................................................................1
第四節變數與常量............................................................................................................................1
第五節陣列.......................................................................................................................................2
第六節注釋和設定陳述式....................................................................................................................2
第七節書寫規範...............................................................................................................................2
第八節判斷語句...............................................................................................................................2
第九節迴圈語句...............................................................................................................................3
第十節其他類語句和錯誤語句處理................................................................................................4
第十一節過程和函數........................................................................................................................4
一.Sub 過程................................................................. 4
二.Function 函數............................................................ 5
三.Property 屬性過程和 Event 事件程序......................................... 5
第十二節內建函式.............................................................................................................................5
一.測試函數................................................................ 5
二.數學函數................................................................ 5
三.字串函數.............................................................. 5
四.轉換函數................................................................ 6
五.時間函數................................................................ 6
第十三節檔操作............................................................................................................................6
文件........................................................................ 6
刪除........................................................................ 6
打開........................................................................ 6
讀入........................................................................ 7
寫入........................................................................ 7
關閉........................................................................ 7
其他檔函數................................................................ 7
二、VISUAL BASIC 程式設計網路教學.....................................................................................1
第一課 VBA 是什麼.................................................................................................................1
1.1 VBA 是什麼...................................................................................................................................1
1.2 EXCEL 環境中基於應用程式自動化的優點................................................................................1
1.3 錄製簡單的宏..............................................................................................................................1
1.4 執行宏..........................................................................................................................................2
1.5 查看錄製的代碼..........................................................................................................................2
1.6 編輯錄製的代碼..........................................................................................................................3
1.7 錄製巨集的局限性..........................................................................................................................3
1.8 小結..............................................................................................................................................3
第二課處理錄製的宏............................................................................................................3
2.1 為宏指定快速鍵..........................................................................................................................3
2.2 決定宏保存的位置......................................................................................................................4
2.3 個人宏工作簿..............................................................................................................................4
2.3.1 保存巨集到個人巨集工作簿.................................................. 4
2.3.2 使用並編輯個人巨集工作簿中的巨集.......................................... 4
2.4 將巨集指定給按鈕..........................................................................................................................4
2.5 將巨集指定給圖片或其他物件......................................................................................................5
2.6 小結..............................................................................................................................................5
第三課學習控制項...................................................................................................................5
3.1 EXCEL 開發過程簡介....................................................................................................................5
3.2 認識不同的控制項..........................................................................................................................5
3.3 向工作表添加控制項......................................................................................................................6
3.4 設置控制項的特性..........................................................................................................................6
3.5 給控制項命名.................................................................................................................................6
3.6 使用使用者表單..............................................................................................................................6
3.7 疑難排解.....................................................................................................................................7
第四課理解變數和變數的作用..............................................................................................7
4.1 代碼存在的位置:模組..............................................................................................................7
4.2 對模組的概覽..............................................................................................................................7
4.2.1 創建過程.............................................................. 8
4.2.2 運行宏................................................................ 9
4.3 保存對模組所做的改變..............................................................................................................9
4.4 變數..............................................................................................................................................9
4.4.1 變數的資料類型........................................................ 9
4.4.2 用 Dim 語句創建變數(聲明變數) .......................................... 10
4.4.3 變數命名的慣例....................................................... 10
4.4.4 使用陣列............................................................. 10
4.4.5 變數賦值............................................................. 11
第五課利用 VBA 設置工作表使用權限...................................................................................11
1.使用 WITH 語句。..........................................................................................
2.使用物件變數。.........................................................................................
方法 3:減少物件的啟動和選擇....................................................................
方法 4:關閉螢幕更新....................................................................................
第六課提高 EXCEL 中 VBA 的效率............................................................................................12
方法 1:儘量使用 VBA 原有的屬性、方法和 WORKSHEET 函數............................................................12
方法 2:儘量減少使用物件引用,尤其在迴圈中.........................................................................12
1.使用 With 語句。.......................................................... 12
2.使用物件變數。.......................................................... 12
3.在迴圈中要儘量減少對象的訪問。.......................................... 13
方法 3:減少物件的啟動和選擇....................................................................................................13
方法 4:關閉螢幕更新....................................................................................................................13
第七課如何在 EXCEL 裡使用計時器.......................................................................................13
三、學習微軟 EXCEL 2002 VBA 程式設計和 XML,ASP 技術...........................................................15
第一章試算表自動化簡介和瞭解巨集命令...........................................................................15
1 瞭解宏...........................................................................................................................................15
2 巨集命令的普通應用........................................................................................................................15
3 寫宏之前的計畫............................................................................................................................16
4 錄製巨集...........................................................................................................................................17
5 運行宏...........................................................................................................................................18
6 修改宏代碼...................................................................................................................................19
7 添加注釋.......................................................................................................................................21
8 分析宏代碼...................................................................................................................................22
9 清除宏代碼...................................................................................................................................23
10 測試修改好的宏..........................................................................................................................24
11 兩個層面運行宏的方法..............................................................................................................24
12 完善你的宏代碼..........................................................................................................................25
13 重新命名宏.................................................................................................................................27
14 運行宏的其它方法......................................................................................................................27
15 使用鍵盤快速鍵運行巨集..............................................................................................................27
16 通過功能表運行巨集..........................................................................................................................28
17 通過工具列按鈕運行巨集..............................................................................................................30
18 通過工作表裡面的按鈕運行巨集..................................................................................................31
19 保存宏..........................................................................................................................................32
20 列印宏..........................................................................................................................................32
21 保存巨集在個人巨集工作簿..............................................................................................................32
22 打開含有宏的工作簿..................................................................................................................34
23VB 編輯視窗..................................................................................................................................35
24 瞭解工程流覽視窗......................................................................................................................35
25 瞭解屬性視窗..............................................................................................................................36
26 瞭解代碼視窗..............................................................................................................................36
27 VB 編輯器裡的其它視窗..............................................................................................................38
28 接下來…… .................................................................................................................................39
第二章 VBA 第一步..............................................................................................................39
1 瞭解指令,模組和過程................................................................................................................39
2 VBA 工程命名.................................................................................................................................39
3 模組重命名...................................................................................................................................40
4 從其它工程調用過程....................................................................................................................41
5 瞭解物件,屬性和方法................................................................................................................42
6 學習物件,屬性和方法................................................................................................................43
7 句法和文法...................................................................................................................................45
8 打斷很長的 VBA 語句......................................................................................................................47
9 瞭解 VBA 錯誤.................................................................................................................................47
10 查找幫助.....................................................................................................................................49
11 語法和程式設計快捷助手..................................................................................................................50
12 屬性/方法清單............................................................................................................................51
13 常數列表.....................................................................................................................................51
14 參數資訊.....................................................................................................................................52
15 快速資訊.....................................................................................................................................52
16 自動完成關鍵字..........................................................................................................................52
17 縮進/凸出...................................................................................................................................53
18 設置注釋塊/解除注釋塊............................................................................................................53
19 使用物件流覽器..........................................................................................................................53
20 使用 VBA 物件程式庫............................................................................................................................58
21 用物件流覽器來定位過程..........................................................................................................59
22 使用立即視窗..............................................................................................................................59
23 獲取立即視窗裡的資訊..............................................................................................................61
24 學習對象.....................................................................................................................................62
25 試算表儲存格操作..................................................................................................................62
26 使用 RANGE 屬性..............................................................................................................................62
27 使用 CELLS 屬性..............................................................................................................................62
28 使用 OFFSET 屬性............................................................................................................................63
29 選擇儲存格的其它方法..............................................................................................................64
30 選擇行和列.................................................................................................................................64
31 獲取工作表資訊..........................................................................................................................65
32 往工作表輸入資料......................................................................................................................65
33 返回工作表中的資訊..................................................................................................................65
34 單元格格式.................................................................................................................................66
35 移動,複製和刪除儲存格..........................................................................................................66
36 操作工作簿和工作表..................................................................................................................67
37 操作視窗(WINDOWS)...................................................................................................................67
38 管理 EXCEL 應用程式......................................................................................................................68
39 接下來…… .................................................................................................................................68
第三章瞭解變數,資料類型和常量.....................................................................................69
1 保存 VBA 語句的結果......................................................................................................................69
2 變數是什麼...................................................................................................................................69
3 資料類型........................................................................................................................................69
4 如何產生變數...............................................................................................................................70
5 如何聲明變數...............................................................................................................................71
6 明確變數的資料類型....................................................................................................................72
7 變數賦值........................................................................................................................................73
8 強制聲明變數...............................................................................................................................75
9 瞭解變數範圍...............................................................................................................................76
10 過程級別(當地)變數..............................................................................................................76
11 模組層級別變數..............................................................................................................................77
12 工程級別變數..............................................................................................................................77
13 變數的存活期..............................................................................................................................78
14 瞭解和使用靜態變數..................................................................................................................78
15 聲明和使用物件變數..................................................................................................................79
16 使用明確的物件變數..................................................................................................................80
17 查找變數定義..............................................................................................................................80
18 在 VB 過程裡面使用常量..............................................................................................................80
19 內置常量.....................................................................................................................................81
20 接下來…… .................................................................................................................................82
第四章 VBA 過程:副程式和函數..........................................................................................82
1.關於函數過程...............................................................................................................................82
2.創建函數過程...............................................................................................................................82
3.執行函數過程...............................................................................................................................84
4.從工作表裡運行函數過程...........................................................................................................84
5.從另外一個 VBA 過程裡運行函數過程.........................................................................................85
6.傳遞參數.......................................................................................................................................86
7.明確參數類型...............................................................................................................................87
8.按地址和按值傳遞參數...............................................................................................................88
9.使用可選的參數...........................................................................................................................88
10.定位內置函數.............................................................................................................................89
11.使用 MSGBOX 函數...........................................................................................................................90
12.MSGBOX 函數的運行值...................................................................................................................94
13.使用 INPUTBOX 函數........................................................................................................................95
14.資料類型轉變.............................................................................................................................96
15.使用 INPUTBOX 方法........................................................................................................................97
16.使用主過程和子過程...............................................................................................................100
17.接下來…… ...............................................................................................................................102
第五章基於 VBA 做決定......................................................................................................102
1.關係和邏輯運算子.....................................................................................................................102
2.IF…THEN 語句...............................................................................................................................103
3.基於多於一個條件的決定.........................................................................................................105
4.THE IF…THEN…ELSE 語句.............................................................................................................106
5.IF…THEN…ELSEIF 語句................................................................................................................108
6.嵌套的 IF…THEN 語句...................................................................................................................110
7.SELECT CASE 語句...........................................................................................................................110
8.和 CASE 子句一起使用 IS ...............................................................................................................112
9.確定 CASE 子句裡數值的範圍......................................................................................................113
10.在 CASE 子句裡確定多個運算式.................................................................................................114
11.接下來… ..................................................................................................................................114
第六章在 VBA 中重複操作...................................................................................................114
1.DO LOOPS: DO…WHILE 和 DO…UNTIL................................................................................................114
2.觀察過程執行.............................................................................................................................117
3.WHILE…WEND 迴圈..........................................................................................................................118
4.FOR…NEXT 迴圈...........................................................................................................................119
5.FOR EACH…NEXT 迴圈.....................................................................................................................120
7.提前跳出迴圈.............................................................................................................................121
8.迴圈嵌套.....................................................................................................................................122
9.接下來… .....................................................................................................................................122
第七章利用 VBA 陣列管理資料清單和表格..........................................................................122
1.瞭解陣列.....................................................................................................................................123
2.聲明陣列.....................................................................................................................................124
3.陣列的上界和下界.....................................................................................................................124
4.在 VBA 過程裡使用陣列...............................................................................................................124
5.陣列和迴圈語句.........................................................................................................................125
6.使用二維陣列.............................................................................................................................127
7.靜態和動態陣列.........................................................................................................................128
8.陣列函數.....................................................................................................................................129
9.ARRAY 函數.....................................................................................................................................130
10.ISARRAY 函數...............................................................................................................................130
11.ERASE 函數..................................................................................................................................131
12.LBOUND 函數和 UBOUND 函數..........................................................................................................131
13.陣列中的錯誤...........................................................................................................................132
14.陣列作為參數...........................................................................................................................134
15.接下來… ..................................................................................................................................134
第八章利用 VBA 操縱檔和資料夾.....................................................................................134
1.獲取當前資料夾的名稱(CURDIR 函數) ...................................................................................135
2.更改檔或資料夾名稱(NAME 函數).......................................................................................135
3.檢查檔或資料夾是否存在(DIR 函數) ................................................................................136
4.獲得檔修改的日期和時間(FILEDATETIME 函數) .................................................................137
5.獲得檔大小(FILELEN 函數) .................................................................................................138
6.返回和設置檔案屬性(GETATTR 函數和 SETATTR 函數) .............................................................138
7.更改缺省資料夾或驅動器(CHDIR 語句和 CHDRIVE 語句).........................................................139
8.創建和刪除資料夾(MKDIR 語句和 RMDIR 語句)........................................................................140
9.複製檔(FILECOPY 語句)........................................................................................................140
10.刪除檔(KILL 語句) ............................................................................................................142
11.從檔讀取和寫入資料(INPUT/OUTPUT)...............................................................................142
12.檔訪問類型...........................................................................................................................142
13.使用順序檔...........................................................................................................................143
14.讀取儲存於順序檔裡的資料...............................................................................................143
15.逐行讀取文件...........................................................................................................................143
16.從順序檔中讀取字元...........................................................................................................144
17.讀取分隔文字檔...................................................................................................................145
18.往順序檔裡寫資料...............................................................................................................146
19.使用 WRITE # 和 PRINT # 語句....................................................................................................147
20.操作隨機檔...........................................................................................................................148
21.創建用戶定義的資料類型.......................................................................................................148
22.操作二進位檔案.......................................................................................................................152
23.操作檔和資料夾的時髦方法...............................................................................................153
24.使用 WSH 獲取檔資訊.............................................................................................................155
25.FILESYSTEMOBJEC 的方法和屬性..................................................................................................156
26.物件 FILE 的屬性........................................................................................................................160
27.資料夾物件屬性.......................................................................................................................161
28.驅動器物件屬性.......................................................................................................................161
29.使用 WSH 創建文字檔.............................................................................................................162
30.使用 WSH 進行其它操作.............................................................................................................164
31.運行其它應用程式...................................................................................................................164
32.創建快捷方式...........................................................................................................................165
33.接下來…… ...............................................................................................................................166
第九章利用 VBA 控制其它應用程式.....................................................................................167
1.啟動應用程式.............................................................................................................................167
2.在應用程式之間切換.................................................................................................................169
3.控制其它應用程式.....................................................................................................................170
4.控制應用程式的其它方法.........................................................................................................171
5.瞭解自動控制.............................................................................................................................172
6.瞭解連結和嵌入.........................................................................................................................172
7.使用 VBA 進行連結和嵌入...........................................................................................................173
8.COM 和自動控制...........................................................................................................................174
9.瞭解綁定.....................................................................................................................................174
10.後期綁定..................................................................................................................................174
11.早期繫結..................................................................................................................................174
12.建立到物件程式庫的引用...............................................................................................................175
13.創建自動控制對象...................................................................................................................176
14.使用 CREATEOBJECT 函數...............................................................................................................176
15.使用自動控制創建一個新的 WORD 文檔.....................................................................................177
16.使用 GETOBJECT 函數....................................................................................................................177
17.打開存在的 WORD 文檔................................................................................................................178
18.使用關鍵字 NEW..........................................................................................................................179
19.使用自動控制訪問 MICROSOFT OUTLOOK .......................................................................................180
20.接下來…… ...............................................................................................................................181
第十章對話方塊和自訂表單..............................................................................................181
1.文件打開和另存為對話方塊.........................................................................................................183
2.GETOPENFILENAME 和 GETSAVEASFILENAME 方法...................................................................................187
3.創建表單.....................................................................................................................................188
4.創建使用者表單的工具.................................................................................................................190
5.標籤.............................................................................................................................................191
6.文字框.........................................................................................................................................191
7.框架.............................................................................................................................................191
8.選項按鈕.....................................................................................................................................191
9.核取方塊.........................................................................................................................................192
10.切換按鈕..................................................................................................................................192
11.清單方塊.......................................................................................................................................192
12.複合框.......................................................................................................................................192
13.捲軸.......................................................................................................................................192
14.旋轉按鈕..................................................................................................................................192
15.圖像...........................................................................................................................................192
16.多頁控制項..................................................................................................................................192
17.TABSTRIP 控制項.............................................................................................................................193
18.REFEDIT 控制項...............................................................................................................................193
19.在表單上放置控制項...................................................................................................................193
20.應用程式示例 1:資訊調查....................................................................................................193
21.在表單上添加按鈕、選項框和其它控制項...............................................................................194
22.更改控制項名稱...........................................................................................................................197
23.設置其它控制項屬性...................................................................................................................197
24.準備工作表以儲存表單資料...................................................................................................198
25.顯示自訂表單.......................................................................................................................199
26.設置 TAB 順序..............................................................................................................................199
27.瞭解表單和控制項事件...............................................................................................................200
28.編寫 VBA 過程對表單和控制項事件反應.....................................................................................201
29.編寫過程來初始化表單...........................................................................................................201
30.編寫過程填充清單方塊控制項.......................................................................................................203
31.編寫程序控制選項按鈕...........................................................................................................203
32.編寫過程同步文字框和旋轉按鈕...........................................................................................204
33.編寫過程關閉使用者表單...........................................................................................................204
34.轉移表單資料到工作表...........................................................................................................205
35.使用 INFO SURVEY 應用程式.........................................................................................................206
36.應用程式示例 2:學生和考試................................................................................................206
37.使用多頁和 TABSTRIP 控制項..........................................................................................................206
38.給表單 STUDENTS AND EXAMS 自訂表單編寫 VBA 過程................................................................208
39.使用自訂表單 STUDENTS AND EXAMS .........................................................................................212
40.接下來…… ...............................................................................................................................214
第十一章自訂集合和類別模組..........................................................................................214
1.使用集合.....................................................................................................................................214
2.聲明自訂集合.........................................................................................................................215
3.給自訂集合添加物件.............................................................................................................215
4.從自訂集合移出物件.............................................................................................................216
5.創建自訂對象.........................................................................................................................217
6.創建類.........................................................................................................................................218
7.變數聲明.....................................................................................................................................218
8.定義類的屬性.............................................................................................................................218
9.創建 PROPERTY GET 過程.................................................................................................................219
10.創建 PROPERTY LET 過程...............................................................................................................219
11.創建類方法...............................................................................................................................220
12.創建類的示例...........................................................................................................................220
13.類別模組裡的事件程序...............................................................................................................221
14.創建使用者介面...........................................................................................................................221
15.觀察 VBA 過程的執行.................................................................................................................229
16.接下來…… ...............................................................................................................................231
第十二章使用 VBA 創建自訂功能表和工具列.......................................................................231
1.工具列.........................................................................................................................................232
2.創建自訂工具列.....................................................................................................................233
3.刪除自訂工具列.....................................................................................................................235
4.使用 COMMANDBAR 的屬性................................................................................................................235
5.使用 COMMANDBAR 控制項....................................................................................................................235
6.理解和使用控制項屬性.................................................................................................................237
7.控制項方法.....................................................................................................................................239
8.使用功能表.....................................................................................................................................240
9.功能表程式設計.....................................................................................................................................241
10.創建子功能表...............................................................................................................................243
11.修改內置快顯功能表...................................................................................................................244
12.創建快顯功能表...........................................................................................................................247
13.接下來…… ...............................................................................................................................249
第十三章調試 VBA 過程和處理錯誤.....................................................................................249
1.測試 VBA 過程...............................................................................................................................249
2.終止過程....................................................................................................................................249
3.使用中斷點.....................................................................................................................................250
4.在中斷模式下使用立即視窗.....................................................................................................253
5.使用 STOP 語句..............................................................................................................................254
6.添加監視運算式.........................................................................................................................254
7.清除監視運算式.........................................................................................................................256
8.使用快速監視.............................................................................................................................256
9.使用本地視窗和調用堆疊對話方塊.............................................................................................257
10.逐句運行 VBA 過程.....................................................................................................................258
11.逐句運行過程...........................................................................................................................259
12.逐過程執行過程.......................................................................................................................259
13.設置下一條語句.......................................................................................................................260
14.顯示下一條語句.......................................................................................................................260
15.終止和重新設置 VBA 過程.........................................................................................................260
16.瞭解和使用條件編譯...............................................................................................................260
17.操縱書簽..................................................................................................................................262
18.捕捉錯誤..................................................................................................................................262
17.接下來…… ...............................................................................................................................266
第十四章微軟 EXCEL 2002 中的事件程式設計...........................................................................266
1.事件程序介紹.............................................................................................................................266
2.啟動和失活事件.........................................................................................................................267
3.事件次序.....................................................................................................................................268
4.工作表事件................................................................................................................................268
5.工作簿事件................................................................................................................................272
6.圖表事件.....................................................................................................................................282
7.內嵌圖表事件.............................................................................................................................284
8.可為應用軟體物件識別的事件.................................................................................................285
9.查詢表時間................................................................................................................................288
10.接下來…… ...............................................................................................................................289
第十五章在 EXCEL 裡使用 ACCESS ........................................................................................289
1.物件程式庫.........................................................................................................................................289
2.建立對物件程式庫的引用.................................................................................................................292
3.連結到 ACCESS ...............................................................................................................................293
4.使用 AUTOMATION 連結到 ACCESS 資料庫...........................................................................................293
5.使用 DAO 連結到 ACCESS 資料庫.....................................................................................................295
6.使用 ADO 連結到 ACCESS 資料庫.....................................................................................................295
7.從 EXCEL 執行 ACCESS 任務...............................................................................................................296
8.創建新 ACCESS 資料庫...................................................................................................................296
9.打開 ACCESS 表單...........................................................................................................................298
10.打開 ACCESS 報表.........................................................................................................................300
11.運行 ACCESS 查詢.........................................................................................................................301
12.運行選擇查詢...........................................................................................................................302
13.運行參數查詢...........................................................................................................................303
14.調用 ACCESS 函數.........................................................................................................................304
15.獲取 ACCESS 資料到 EXCEL 工作表.................................................................................................304
16.使用 GETROWS 方法獲取資料.......................................................................................................304
17.使用 COPYFROMRECORDSET 方法獲取資料.......................................................................................305
18.使用 TRANSFERSPREADSHEET 方法獲取資料....................................................................................306
19.使用 OPENDATABASE 方法...............................................................................................................307
20.從 ACCESS 資料創建文字檔.....................................................................................................309
21.從 ACCESS 資料創建查詢表.........................................................................................................310
22.在 EXCEL 裡使用 ACCESS 資料.........................................................................................................311
23.用 ACCESS 資料創建內嵌圖表.....................................................................................................311
24.傳輸 EXCEL 試算表到 ACCESS 資料庫.........................................................................................313
25.將 EXCEL 試算表連結到 ACCESS 資料庫.....................................................................................313
26.將 EXCEL 試算表導入 ACCESS 資料庫.........................................................................................314
27.放置 EXCEL 數據到 ACCESS 表中.....................................................................................................314
28.接下來…… ...............................................................................................................................316
VBA 語言基礎
第一節識別字
一.定義
一、VBA 語言基礎
橄欖樹整理
識別字是一種標識變數、常量、過程、函數、類等語言構成單位的符號,利用它可以完成
對變數、常量、過程、函數、類等的引用。
二.命名規則
1) 字母打頭,由字母、數位和底線組成,如 A987b_23Abc
2) 字元長度小於 40,(Excel2002 以上中文版等,可以用漢字且長度可達 254 個字元)
3) 不能與 VB 保留字重名,如 public, private, dim, goto, next, with, integer, single

第二節運算子
定義:運算子是代表 VB 某種運算功能的符號。
1)設定運算子=
2)數學運算子&、+ (字元連接子)、+(加)、-(減)、Mod(取餘)、\(整除)、*(乘)、/
(除)、-(負號)、^(指數)
3)邏輯運算子 Not(非)、And(與)、Or(或)、Xor(異或)、Eqv(相等)、Imp(隱含)
4)關係運算子= (相同)、<>(不等)、>(大於)、<(小於)、>=(不小於)、<=(不大
於)、Like、Is
5)位元運算子 Not(邏輯非)、And(邏輯與)、Or(邏輯或)、Xor(邏輯異或)、Eqv(邏輯
等)、Imp(隱含)
第三節資料類型
VBA 共有 12 種資料類型,具體見下表,此外使用者還可以根據以下類型用 Type 自訂資料
類型。
資料類型
字串型 String
位元組型 Byte
布林型 Boolean
整數型 Integer
長整數型 Long
單精確度型 Single
雙精度型 Double
日期型 Date
貨幣型 Currency
小數點型 Decimal
變體型 Variant
對象型 Object
第四節變數與常量
類型識別字
$


%
&
!
#

@



位元組
字元長度(0-65400)
1
2
2
4
4
8
8 西元 100/1/1-99/12/31
8
14
以上任意類型,可變
4
1)VBA 允許使用未定義的變數,預設是變體變數。
2)在模組通用說明部份,加入 Option Explicit 語句可以強迫使用者進行變數定義。
3)變數定義語句及變數作用域
Dim 變數 as 類型'定義為區域變數,如 Dim xyz as integer
Private 變數 as 類型'定義為私有變數,如 Private xyz as byte
Public 變數 as 類型'定義為公有變數,如 Public xyz as single
Global 變數 as 類型'定義為全域變數,如 Globlal xyz as date
Static 變數 as 類型'定義為靜態變數,如 Static xyz as double
一般變數作用域的原則是,那部份定義就在那部份起作用,模組中定義則在該模組那作用。
4)常量為變數的一種特例,用 Const 定義,且定義時賦值,程式中不能改變值,作用域也如同
1
VBA 語言基礎
變數作用域。如下定義:Const Pi=3.1415926 as single
第五節陣列
橄欖樹整理
陣列是包含相同資料類型的一組變數的集合,對陣列中的單個變數引用通過陣列索引下標
進行。在記憶體中表現為一個連續的區塊,必須用 Global 或 Dim 語句來定義。定義規則如下:
Dim 陣列名稱([lower to ]upper [, [lower to ]upper, ….]) as type ;Lower 缺省值為 0。二
維陣列是按行列排列,如 XYZ(行,列)。
除了以上固定陣列外,VBA 還有一種功能強大的動態陣列,定義時無大小維數聲明;在程
序中再利用 Redim 語句來重新改變陣列大小,原來陣列內容可以通過加 preserve 關鍵字來保留。
如下例:
Dim array1() as double : Redim array1(5) : array1(3)=250 : Redim preserve array1(5,10)
第六節注釋和設定陳述式
1)注釋語句是用來說明程式中某些語句的功能和作用;VBA 中有兩種方法標識為注釋語句。
 單引號’ ;如:’定義全域變數;可以位於別的語句之尾,也可單獨一行
 Rem ;如:Rem 定義全域變數;只能單獨一行
2)設定陳述式是進行對變數或物件屬性賦值的語句,採用賦值號=,如 X=123:
Form1.caption=”
我的窗口”
對物件的賦值採用:set myobject=object 或 myobject:=object
第七節書寫規範
1)VBA 不區分識別字的字母大小寫,一律認為是小寫字母;
2)一行可以書寫多條語句,各語句之間以冒號: 分開;
3)一條語句可以多行書寫,以空格加底線_ 來標識下行為續行;
4)識別字最好能簡潔明瞭,不造成歧義。
第八節判斷語句
1)If…Then…Else 語句
If condition Then [statements][Else elsestatements]
如 1:If A>B And C<D Then A=B+2 Else A=C+2
如 2:If x>250 Then x=x-100
或者,可以使用塊形式的語法:
If condition Then
[statements]
[ElseIf condition-n Then
[elseifstatements] ...
[Else
[elsestatements]]
End If
如 1:
If Number < 10 Then
Digits = 1
ElseIf Number < 100Then
Digits = 2
Else
Digits = 3
End If
2)Select Case…Case…End Case 語句
如 1:
Select Case Pid
Case “A101”
Price=200
Case “A102”
Price=300
2
VBA 語言基礎
……
Case Else
Price=900
End Case
3)Choose 函數
橄欖樹整理
choose(index, choce-1,choice-2,…,choice-n),可以用來選擇引數串列中的一個值,
並將其返回,index 必要參數,數值運算式或欄位,它的運算結果是一個數值,且界於 1 和可
選擇的項目數之間。choice 必要參數,Variant 運算式,包含可選擇專案的其中之一。如:
GetChoice = Choose(Ind, "Speedy", "United", "Federal")

4)Switch 函數
Switch(expr-1, value-1[, expr-2, value-2 _ [, expr-n,value-n]])

switch 函數和 Choose 函數類似,但它是以兩個一組的方式返回所要的值,在串列中,最先為


TRUE 的值會被返回。expr 必要參數,要加以計算的 Variant 運算式。value 必要參數。如
果相關的運算式為 True,則返回此部分的數值或運算式,沒有一個運算式為 True,Switch 會
返回一個 Null 值。
第九節迴圈語句
1)For Next 語句以指定次數來重複執行一組語句
For counter = start To end [Step step]
[statements]
[Exit For]
[statements]
Next [counter]
如 1:
For Words = 10 To 1 Step -1
For Chars = 0 To 9
' step 缺省值為 1
' 建立 10 次迴圈
' 建立 10 次迴圈
MyString = MyString & Chars ' 將數位添加到字串中
Next Chars
MyString = MyString & " "
Next Words
' Increment counter
' 添加一個空格
2)For Each…Next 語句主要功能是對一個陣列或集合物件進行,讓所有元素重複執行一次
語句
For Each element In group
Statements
[Exit for]
Statements
Next [element]
如 1:
For Each rang2 In range1
With range2.interior
.colorindex=6
.pattern=xlSolid
End with
Next
這上面一例中用到了 With…End With 語句,目的是省去物件多次調用,加快速度;語法
為:
With object
[statements]
End With
3)Do…loop 語句在條件為 true 時,重複執行區塊命令
Do {while |until} condition' while 為當型迴圈,until 為直到型迴圈,顧名思義,不多說

Statements
Exit do
3
VBA 語言基礎
Statements
Loop
或者使用下面語法
Do
Statements
Exit do
Statements
' 先 do 再判斷,即不論如何先幹一次再說
橄欖樹整理
Loop {while |until} condition

第十節其他類語句和錯誤語句處理
一.其他迴圈語句
結構化程式使用以上判斷和迴圈語句已經足夠,建議不要輕易使用下面的語句,雖然 VBA 還支
持。
1) Goto line 該語句為跳轉到 line 語句行
2) On expression gosub destinatioinlist 或者 on expression goto destinationlist 語
句為根據 exprssion 運算式值來跳轉到所要的行號或行標記
3) Gosub line…line…Return 語句, Return 返回到 Gosub line 行,如下例:
Sub gosubtry()
Dim num
Num=inputbox(“輸入一個數字,此值將會被判斷迴圈”)
If num>0 then Gosub Routine1 :Debug.print num:Exit sub
Routine1:
Num=num/5
Return
End sub
4) while…wend 語句,只要條件為 TRUE,迴圈就執行,這是以前 VB 老語法保留下來的,如下
例:
while condition‘while I<50
[statements] ‘I=I+1
wend ‘Wend
二.錯誤語句處理
執行階段有時會有錯誤的情況發生,利用 On Error 語句來處理錯誤,啟動一個錯誤的處理常式。
語法如下:
On Error Goto Line ‘當錯誤發生時,會立刻轉移到 line 行去
On Error Resume Next‘當錯誤發生時,會立刻轉移到發生錯誤的下一行去
On Erro Goto 0
第十一節過程和函數
‘當錯誤發生時,會立刻停止過程中任何錯誤處理過程
過程是構成程式的一個模組,往往用來完成一個相對獨立的功能。過程可以使程式更清晰、
更具結構性。VBA 具有四種過程:Sub 過程、Function 函數、Property 屬性過程和 Event 事件
過程。
一.Sub 過程
Sub 過程的參數有兩種傳遞方式:按值傳遞(ByVal)和按地址傳遞(ByRef)。如下例:
Sub password (ByVal x as integer, ByRef y as integer)
If y=100 then y=x+y else y=x-y
x=x+100
End sub
Sub call_password ()
Dim x1 as integer
Dim y1 as integer
x1=12
y1=100
Call password (x1,y1) ‘調用過程方式:1. Call 過程名(參數 1, 參數 2…) ; 2. 過
程名參數 1, 參數 2…
4
VBA 語言基礎橄欖樹整理
debug.print x1,y1 ‘結果是 12、112,y1 按位址傳遞改變了值,而 x1 按值傳遞,未
改變原值
End sub
二.Function 函數
函數實際是實現一種映射,它通過一定的映射規則,完成運算並返回結果。參數傳遞也兩
種:按值傳遞(ByVal)和按地址傳遞(ByRef)。如下例:
Function password(ByVal x as integer, byref y as integer) as boolean

If y=100 then y=x+y else y=x-y


x=x+100
if y=150 then password=true else password=false

End Function
Sub call_password ()
Dim x1 as integer
Dim y1 as integer
x1=12
y1=100
if password then‘調用函數:1. 作為一個運算式放在=右端; 2. 作為參數使用
debug.print x1
end if
End sub
三.Property 屬性過程和 Event 事件程序
這是 VB 在物件功能上添加的兩個過程,與物件特徵密切相關,也是 VBA 比較重要組成,技
術比較複雜,可以參考相關書籍。
第十二節內建函式
在 VBA 程式語言中有許多內置函數,可以説明程式碼設計和減少代碼的編寫工作。
一.測試函數
IsNumeric(x) ‘是否為數字, 返回 Boolean 結果,True or False
IsDate(x) ‘是否是日期, 返回 Boolean 結果,True or False
IsEmpty(x) ‘是否為 Empty, 返回 Boolean 結果,True or False
IsArray(x) ‘指出變數是否為一個陣列。
IsError(expression) ‘指出運算式是否為一個錯誤值
IsNull(expression) ‘指出運算式是否不包含任何有效資料(Null)。
IsObject(identifier)‘指出識別字是否表示物件變數
二.數學函數
Sin(X)、Cos(X)、Tan(X)、Atan(x) 三角函數,單位為弧度
Log(x) 返回 x 的自然對數
Exp(x)返回 e x

Abs(x) 返回絕對值
Int(number)、Fix(number) 都返回參數的整數部分,區別:Int 將-8.4 轉換成-9,而 Fix 將
-8.4 轉換成-8
Sgn(number) 返回一個 Variant (Integer),指出參數的正負號
Sqr(number) 返回一個 Double,指定參數的平方根
VarType(varname) 返回一個 Integer,指出變數的子類型
Rnd(x)返回 0-1 之間的單精確度資料,x 為隨機種子
三.字串函數
Trim(string)
Ltrim(string)
Rtrim(string)
Len(string)
Left(string, x)
Right(string, x)
去掉 string 左右兩端空白
去掉 string 左端空白
去掉 string 右端空白
計算 string 長度
取 string 左段 x 個字元組成的字串
取 string 右段 x 個字元組成的字串
Mid(string, start,x) 取 string 從 start 位元開始的 x 個字元組成的字串
Ucase(string) 轉換為大寫
5
VBA 語言基礎
Lcase(string)
Space(x)
轉換為小寫
返回 x 個空白的字串
橄欖樹整理
Asc(string)
Chr(charcode)
四.轉換函數
CBool(expression)
返回一個 integer,代表字串中首字母的字元代碼
返回 string,其中包含有與指定的字元代碼相關的字元
轉換為 Boolean 型
CByte(expression) 轉換為 Byte 型
CCur(expression) 轉換為 Currency 型
CDate(expression) 轉換為 Date 型
CDbl(expression)
CDec(expression)
CInt(expression)
CLng(expression)
CSng(expression)
CStr(expression)
CVar(expression)
Val(string)
Str(number)
五.時間函數
轉換為 Double 型
轉換為 Decemal 型
轉換為 Integer 型
轉換為 Long 型
轉換為 Single 型
轉換為 String 型
轉換為 Variant 型
轉換為資料型
轉換為 String
Now 返回一個 Variant (Date),根據電腦系統設置的日期和時間來指定日期和時
間。
Date 返回包含系統日期的 Variant (Date)。
Time 返回一個指明當前系統時間的 Variant (Date)。
Timer 返回一個 Single,代表從午夜開始到現在經過的秒數。
TimeSerial(hour, minute, second) 返回一個 Variant (Date),包含具有具體時、分、秒的
時間。
DateDiff(interval, date1, date2[, firstdayofweek[, firstweekofyear]]) 返回 Variant
(Long) 的值,表示兩個指定日期間的時間間隔數目
Second(time) 返回一個 Variant (Integer),其值為 0 到 59 之間的整數,表示一分鐘之中
的某個秒
Minute(time) 返回一個 Variant (Integer),其值為 0 到 59 之間的整數,表示一小時中的
某分鐘
Hour(time) 返回一個 Variant (Integer),其值為 0 到 23 之間的整數,表示一天之中的某
一鐘點
Day(date) 返回一個 Variant (Integer),其值為 1 到 31 之間的整數,表示一個月中的某
一日
Month(date) 返回一個 Variant (Integer),其值為 1 到 12 之間的整數,表示一年中的某月
Year(date) 返回 Variant (Integer),包含表示年份的整數。
Weekday(date, [firstdayofweek]) 返回一個 Variant (Integer),包含一個整數,代表某個
日期是星期幾
第十三節檔操作
文件
Dir[(pathname[, attributes])] ;pathname 可選參數,用來指定檔案名的字串運算式,可
能包含目錄或資料夾、以及驅動器。如果沒有找到 pathname,則會返回零長度字串("");
attributes 可選參數。常數或數值運算式,其總和用來指定檔案屬性。如果省略,則會返回匹
配 pathname 但不包含屬性的檔。
刪除
Kill pathname 從磁片中刪除檔, pathname 參數是用來指定一個檔案名
RmDir pathname 從磁片中刪除目錄,pathname 參數是用來指定一個資料夾
打開
Open pathname For mode [Access access] [lock] As [#]filenumber [Len=reclength] 能夠
對檔輸入/輸出(I/O)。
pathname 必要。字串運算式,指定檔案名,該檔案名可能還包括目錄、資料夾及驅動器。
6
VBA 語言基礎橄欖樹整理
mode 必要。關鍵字,指定檔方式,有 Append、Binary、Input、Output、或 Random 方式。
如果未指定方式,則以 Random 訪問方式打開檔。
access 可選。關鍵字,說明打開的檔可以進行的操作,有 Read、Write、或 Read Write 操
作。
lock 可選。關鍵字,說明限定於其它進程打開的檔的操作,有 Shared、Lock Read、Lock
Write、
和 Lock Read Write 操作。
filenumber 必要。一個有效的檔號,範圍在 1 到 511 之間。使用 FreeFile 函數可得到下
一個可用的檔號。reclength 可選。小於或等於 32,767(位元組)的一個數。對於用隨機訪
問方式打開的檔,該值就是記錄長度。對於順序檔,該值就是緩衝字元數。
說明對檔做任何 I/O 操作之前都必須先打開檔。Open 語句分配一個緩衝區供檔進行
I/O 之用,並決定緩衝區所使用的訪問方式。如果 pathname 指定的檔不存在,那麼,在用
Append、Binary、Output、或 Random 方式打開檔時,可以建立這一檔。如果檔已由其
它進程打開,而且不允許指定的訪問類型,則 Open 操作失敗,而且會有錯誤發生。如果 mode
是 Binary 方式,則 Len 子句會被忽略掉。
重要在 Binary、Input 和 Random 方式下可以用不同的檔號打開同一檔,而不必先將該
文件關閉。在 Append 和 Output 方式下,如果要用不同的檔號打開同一檔,則必須在打
開檔之前先關閉該檔。
讀入
Input #filenumber, varlist 從已打開的順序檔中讀出資料並將資料指定給變數
Get [#]filenumber, [recnumber], varname 將一個已打開的磁片檔讀入一個變數之中。
寫入
Write #filenumber, [outputlist] 將資料寫入順序檔
Print #filenumber, [outputlist] 將格式化顯示的資料寫入順序檔中
Put [#]filenumber, [recnumber], varname 將一個變數的資料寫入磁片檔中。
關閉
Close [filenumberlist] 關閉 Open 語句所打開的輸入/輸出(I/O) 檔
注意如果今後想用 Input # 語句讀出檔的資料,就要用 Write # 語句而不用 Print # 語
句將資料寫入檔。因為在使用 Write # 時,將資料欄分界就可確保每個資料欄的完整性,因
此可用 Input # 再將資料讀出來。使用 Write # 還能確保任何地區的資料都被正確讀出。Write
與 Print # 語句不同,當要將資料寫入檔時,Write # 語句會在專案和用來標記字串的引
號之間插入逗號。Write # 語句在將 outputlist 中的最後一個字元寫入檔後會插入一個新
行字元,即回車分行符號,(Chr(13) + Chr(10))。
其他檔函數
LOF(filenumber) 返回一個 Long,表示用 Open 語句打開的檔的大小,該大小以位元組為單
位。
EOF(filenumber) 返回一個 Integer,它包含 Boolean 值 True,表明已經到達為 Random 或
順序 Input 打開的檔的結尾。
Loc(filenumber) 返回一個 Long,在已打開的檔中指定當前讀/寫位置
Seek(filenumber) 返回一個 Long,在 Open 語句打開的檔中指定當前的讀/寫位
7
Visual BASIC 程式設計網路教學

二、Visual BASIC 程式設計網路教學
第一課 VBA 是什麼
1.1 VBA 是什麼
橄欖樹

直到 90 年代早期,使應用程式自動化還是充滿挑戰性的領域.對每個需要自動化的應用程
序,人們不得不學習一種不同的自動化語言.例如:可以用 EXCEL 的巨集語言來使 EXCEL 自動化,使
用 WORD BASIC 使 WORD 自動化,等等.微軟決定讓它開發出來的應用程式共用一種通用的自動化
語言--------Visual Basic For Application(VBA),可以認為 VBA 是非常流行的應用程式開發
語言 VASUAL BASIC 的子集.實際上 VBA 是"寄生于"VB 應用程式的版本.VBA 和 VB 的區別包括如
下幾個方面:
1. VB 是設計用於創建標準的應用程式,而 VBA 是使已有的應用程式(EXCEL 等)自動化
2. VB 具有自己的開發環境,而 VBA 必須寄生于已有的應用程式.
3. 要運行 VB 開發的應用程式,使用者不必安裝 VB,因為 VB 開發出的應用程式是可執行檔
(*.EXE),而 VBA 開發的程式必須依賴于它的"父"應用程式,例如 EXCEL.
儘管存在這些不同,VBA 和 VB 在結構上仍然十分相似.事實上,如果你已經瞭解了 VB,會發現
學習 VBA 非常快.相應的,學完 VBA 會給學習 VB 打下堅實的基礎.而且,當學會在 EXCEL 中用 VBA
創建解決方案後,即已具備在 WORD ACCESS OUTLOOK FOXPRO PROWERPOINT 中用 VBA 創建解決方
案的大部分知識.
* VBA 一個關鍵特徵是你所學的知識在微軟的一些產品中可以相互轉化.
* VBA 可以稱作 EXCEL 的"遙控器".
VBA 究竟是什麼?更確切地講,它是一種自動化語言,它可以使常用的程式自動化,可以創建
自訂的解決方案.
此外,如果你願意,還可以將 EXCEL 用做開發平臺實現應用程式.
1.2 EXCEL 環境中基於應用程式自動化的優點
也許你想知道 VBA 可以幹什麼?使用 VBA 可以實現的功能包括:
1. 使重複的任務自動化.
2. 自訂 EXCEL 工具列,功能表和介面.
3. 簡化範本的使用.
4. 自訂 EXCEL,使其成為開發平臺.
5. 創建報表.
6. 對資料進行複雜的操作和分析.
用 EXCEL 作為開發平臺有如下原因:
1. EXCEL 本身功能強大,包括列印,檔處理,格式化和文本編輯.
2. EXCEL 內置大量函數.
3. EXCEL 介面熟悉.
4. 可連接到多種資料庫.
用其他語言開發應用程式,一半的工作是編寫一些基本功能的模組,包括檔的打開和保存,
列印,複製等.而用 EXCEL 作為開發平臺,則由於 EXCEL 已經具備這些基本功能,你要做的只是使
用它.
1.3 錄製簡單的宏
在介紹學習 VBA 之前,應該花幾分鐘錄製一個宏。
新術語:"宏",指一系列 EXCEL 能夠執行的 VBA 語句。
以下將要錄製的宏非常簡單,只是改變儲存格顏色。請完成如下步驟:
1)打開新工作簿,確認其他工作簿已經關閉。
2)選擇 A1 儲存格。調出"常用"工具列。
3)選擇"工具"-"宏"-"錄製新宏"。
4)輸入"改變顏色"作為巨集名替換預設巨集名,按一下確定,注意,此時狀態列中顯示"錄製",特
別是"停止錄製"工具列也顯示出來。替換預設巨集名主要是便於分別這些宏。
★ 巨集名最多可為 255 個字元,並且必須以字母開始。其中可用的字元包括:字母、數位和
1
Visual BASIC 程式設計網路教學

底線。宏名中不允許出現空格。通常用底線代表空格。
5)選擇"格式"的"儲存格",選擇"圖案"選項中的紅色,按一下"確定"。
6)按一下"停止錄製"工具列按鈕,結束巨集錄製過程。
※ 如果"停止錄製"工具列開始並未出現,請選擇"工具"-"宏"-"停止錄製"。
錄製完一個宏後就可以執行它了。
1.4 執行宏
橄欖樹

當執行一個宏時,EXCEL 按照巨集式執行的情況就像 VBA 代碼在對 EXCEL 進行"遙控"。但
VBA 的"遙控"不僅能使操作變得簡便,還能使你獲得一些使用 EXCEL 標準命令所無法實現的功
能。而且,一旦熟悉了 EXCEL 的"遙控",你都會奇怪自己在沒有這些"遙控"的情況下,到底是
怎麼熬過來的。要執行剛才錄製的巨集,可以按以下步驟進行:
1)選擇任何一個儲存格,比如 A3。
2)選擇"工具"-"巨集"-"巨集",顯示"巨集"對話方塊。
3)選擇"改變顏色",選擇"執行",則 A3 儲存格的顏色變為紅色。試著選擇其它儲存格和
幾個儲存格組成的區域,然後再執行巨集,以便加深印象。
1.5 查看錄製的代碼
到底是什麼在控制 EXCEL 的運行呢?你可能有些疑惑.好,讓我們看看 VBA 的語句吧.
1)選擇"工具"-"巨集"-"巨集",顯示"巨集"對話方塊。
2)按一下列表中的"改變顏色",選擇"編輯"按鈕。
此時,會打開 VBA 的編輯器視窗(VBE)。關於該編輯器,以後再詳細說明,先將注意力集
中到顯示的代碼上。代碼如下:(日期和姓名會有不同)
Sub 改變顏色()
'
' 改變顏色 Macro
' xw 記錄的巨集 2000-6-10
'
'
With Selection.Interior
.ColorIndex = 3
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
End With
End Sub
將來會十分熟悉這種代碼,雖然現在它們看上去像一種奇怪的外語。學習 VBA 或程式設計語言在某
種_______程度上比較像在學習一種外語。
Sub 改變顏色():這是宏的名稱。
中間的以" '"開頭的五行稱為"注釋",它在錄製巨集時自動產生。
以 With 開頭到 End With 結束的結構是 With 結構語句,這段語句是巨集的主要部分。注意單詞
"selection",它代表"突出顯示的區域"(即:選定區域)。With Selection.Interior :它讀
作"選擇區域的的內部".這整段語句設置該區域內部的一些"屬性"。
其中:
.ColorIndex = 3: 將該內部設為紅色。注意:有一小圓點,它的作用在於簡化語句,小圓點代
替出現在 With 後的詞,它是 With 結構的一部分。另外:紅色被數位化為 3.(紅色警戒是否可稱
作:3 號警戒,嗯?)有興趣的話,你將 3 改為其他數字試試看。
.Pattern = xlSolid:設置該區域的內部圖案。由於是錄製巨集,所以,雖然你並未設置這一項,
巨集仍然將其記錄下來(因為在"圖案"選項中有此一項,只是你未曾設置而已)。xlSolid 表示純
色。
.PatternColorIndex = xlAutomatic:表示內部圖案網底顏色為自動配色。
End With:結束 With 語句。
End Sub:整個宏的結束語
2
Visual BASIC 程式設計網路教學

1.6 編輯錄製的代碼
橄欖樹

在上一節,我們錄製了一個宏並查看了代碼,代碼中有兩句實際上並不起作用。哪兩句?
現在,在宏中作一個修改,刪除多餘行,直到和下面代碼相同:
Sub 改變顏色()
'
' 改變顏色 Macro
' xw 記錄的巨集 2000-6-10
'
'
With Selection.Interior
.ColorIndex = 3
End With
End Sub
完成後,在工作表中試驗一下。你會發現結果和修改前的狀況一樣。在 With 語句前加入
一行:
Range("A5").Select
試著運行該宏,則無論開始選擇哪個儲存格,宏運行結果都是使 A5 儲存格變紅.
現在可以看到,編輯錄製的宏同樣非常簡單。需要編輯巨集是因為以下三個方面的原因。一:
在錄製中出錯而不得不修改。二:錄製的巨集中有多餘的語句需要刪除,提高巨集的運行速度。三:
希望增加宏的功能。比如:加入判斷或迴圈等無法錄製的語句。
1.7 錄製巨集的局限性
希望自動化的許多 EXCEL 過程大多都可以用錄製巨集來完成.但是巨集記錄器存在以下局限性.
通過巨集記錄器無法完成的工作有:
1)錄製的宏無判斷或迴圈能力.
2)人機交互能力差,即使用者無法進行輸入,電腦無法給出提示.
3)無法顯示 EXCEL 對話方塊.
4)無法顯示自訂表單.
1.8 小結
本課中,你已經掌握了 VBA 的一些基礎知識,你會錄製巨集、編輯宏而且瞭解了錄製巨集的局限
性.你很努力.並且已經為將來學習 VBA 甚至 VB 等程式設計語言打下了基礎.關鍵是你已經瞭解了一
個謎底,就是說,你瞭解了什麼是程式設計.下面是些小練習,做完後才可以去玩喲.
思考:
1)VBA 只能用於 EXCEL 嗎?
2)VBA 是基於哪種語言?
3)說說 EXCEL 和 VBA 的關係.
4)為什麼要用宏?
第二課處理錄製的宏
2.1 為宏指定快速鍵
你也許希望為經常使用的宏指定快速鍵。快速鍵是指鍵的組合,當其按下時執行一條命令。
例如:CTRL+C
在許多程式中代表"複製"命令。當給宏指定了快速鍵後,就可以用快速鍵來執行宏,而不
必通過"工具"功能表。
注意:當包含巨集的工作簿打開時間,為宏指定快速鍵會覆蓋 EXCEL 默認的快速鍵。例如:
把 CTRL+C 指定給某個宏,那麼 CTRL+C 就不再執行複製命令。用以下方法可以列印出 EXCEL 的
快速鍵清單(用 A4 紙列印共有 24 頁之多):
1)打開 EXCEL 説明檔並選擇"目錄"選項。
2)從"使用快速鍵"資料夾中選擇""快速鍵"標題。
3)右擊該標題,從快顯功能表中選擇"列印"。
4)選擇"列印所選標題和所有子主題",按一下"確定"。
3
Visual BASIC 程式設計網路教學

橄欖樹

可以在創建宏時指定快速鍵,也可以在創建後再指定。要在創建(錄製)宏時指定快速鍵,只
須在錄製巨集時在輸入宏名後,在"快速鍵"文字方塊中輸入相應的鍵。錄製巨集後指定快速鍵也很簡
單,只需選擇"工具""巨集",顯示"巨集"對話方塊,選擇要指定快速鍵的巨集,再按一下"選項"按鈕,通
過"選項"對話方塊進行設置。
2.2 決定宏保存的位置
宏可保存在三種可能的位置:
1)當前工作簿。(只有該工作簿打開時,該宏才可用。)
2)新工作簿。
3)個人宏工作簿。
2.3 個人宏工作簿
個人巨集工作簿,是為宏而設計的一種特殊的具有自動隱藏特性的工作簿。第一次將宏創建
到個人宏工作簿時,會創建名為"PERSONAL.XLS"的新文件。如果該檔存在,則每當 EXCEL 啟
動時會自動將此檔打開並隱藏在活動工作簿後面(在"視窗"功能表中選擇"取消隱藏"後,可以
很方便地發現它的存在。)如果你要讓某個宏在多個工作簿都能使用,那麼就應當創建個人宏工
作簿, 並將宏保存於其中。個人宏工作簿保存在"XLSTART" 資料夾中。具體路
徑為:
C:\WINDOWS\Profiles\Application Data\Microsoft\Excel\XLSTART。可以以單詞"XLSTART"
查詢。
注意:如果存在個人宏工作簿,則每當 EXCEL 啟動時會自動將此檔打開並隱藏。因為它
存放在 XLSTART 資料夾內。
2.3.1 保存巨集到個人巨集工作簿
本練習,將保存一個簡單的巨集到個人巨集工作簿,該巨集為文本加底線並改為斜體,步驟如下:
1)建立一個名為"HOUR2"的工作簿,選擇"工具"-"宏"-"錄製新巨集",顯示"錄製新巨集"對話方塊.
2)輸入"格式化文字"作為宏名.
3)從"保存在"下拉清單中選擇"個人宏工作簿".
4)按一下"確定"按鈕.現在進入錄製模式.
5)按一下"斜體"工具列按鈕.一段時間內,滑鼠出現沙漏,特別是在第一次創建個人宏工作簿
時,因為 EXCEL 在創建該工作簿.
6)按一下"底線"按鈕.
7)停止錄製.
2.3.2 使用並編輯個人巨集工作簿中的巨集
剛才已經保存了一個巨集到個人巨集工作簿,現在可以在任何工作簿中使用該宏.可按如下步驟
操作:
1)關閉所有 EXCEL 工作簿.
2)任意打開一個 EXCEL 檔.(EXCEL 自動將個人宏工作簿同時打開並隱藏.)
3)在 A3 中輸入你的名字.
4)選擇"工具"-"巨集",顯示巨集對話方塊.現在可以在宏清單中看到"格式化文字"這個宏.
5)選擇"格式化文字"宏,並執行.現在 A3 儲存格中,你的名字變為斜體字還帶有底線.選
擇"視窗"-"取消隱藏",可以將 PERSONAL.XLS 顯示出來,其中沒有任何文字,但通過 VBA 編輯器可
以在其中的模組中找到"格式化文字"這個宏.在 VBA 編輯器中可以對該宏進行直接編輯或者刪
除.如果 PERSONAL.XLS 中一個宏都沒有,在啟動 EXCEL 時仍會打開 PERSONAL.XLS,這也許是
EXCEL 存在的一個小毛病.
2.4 將巨集指定給按鈕
即使通過快速鍵可以是宏的執行變快,但是一旦宏的數量多了也難於記憶,而且,如果宏是
由其他人來使用,難道你要他們也記住那麼多的快速鍵嗎?
作為 EXCEL 開發者,一個主要的目標是為自動化提供一個易於操作的介面."按鈕"是最常見
的介面組成元素之一.通過使用"表單"工具列,可以為工作簿中的工作表添加按鈕。在創建完一
個按鈕後,可以為它指定宏,然後你的使用者就可以通過按一下按鈕來執行巨集。在本練習中,將創
建一個按鈕,並為它指定一個巨集,然後用該按鈕來執行巨集。具體步驟如下:
1)打開"HOUR2"工作簿。
2)調出"表單"工具列。
4
Visual BASIC 程式設計網路教學

3)按一下"表單"工具列中的"按鈕"控制項,此時滑鼠變成十字形狀。
橄欖樹

4)在希望放置按鈕的位置按下滑鼠左鍵,拖動滑鼠畫出一個矩形,這個矩形代表了該按鈕
的大小。對大小滿意後放開滑鼠左鍵,這樣一個命令按鈕就添加到了工作表中,同時 EXCEL 自
動顯示"指定巨集"對話方塊。
5)從"指定宏"對話方塊中選擇"格式化文字",按一下"確定"。這樣,就把該巨集指定給命令按鈕。
6)在按鈕的標題"按鈕 1"前按一下滑鼠左鍵,按下 DELETE 直到刪除所有文本,輸入"格式化
"作為標題。
7)按一下按鈕外的任意位置,現在該按鈕的標題由預設的"按鈕 1"變為"格式化"而且被指定
了一個宏。
8)試著在某個儲存格中輸入文本,按一下按鈕運行該巨集。
當滑鼠移動至該按鈕時自動變成手的形狀,如果要改變其大小或標題,只需用按右鍵該
按鈕就可以進行修改和設置。很明顯,你再也不需記住宏的名字或快速鍵了,只需按一下按鈕。
2.5 將巨集指定給圖片或其他物件
要執行宏有多種方法可以選擇,可以將巨集指定給按鈕等控制項,還可以指定給圖片、自訂
工具列、表單甚至可以將巨集指定給某個"事件",比如按一下工作表,按兩下工作表,啟動工作表,
打開工作簿等等,"事件"是一個重要的概念,除此而外"方法""物件"都是將來你會經常接觸到
的。現在它們看來十分抽象,但是將來你會很熟悉這些詞語。指定巨集到圖片十分簡單,只需單
擊某個圖片,按一下快顯功能表中的"指定宏"進行設置即可。
如果不希望在工作表上添加控制項或圖片執行巨集,還有一種方法可以選擇:將宏指定給"工具
欄按鈕",可按如下步驟進行:
1)打開"HOUR2"工作簿,選擇"工具"-"定義",顯示"自訂工具列"對話方塊。
2)從"類別"清單方塊中選擇"宏",從"命令"清單方塊中選擇"自訂按鈕"。
3)將"自訂按鈕"拖動到工具列。
4)按右鍵該按鈕,選擇"指定巨集",顯示"指定巨集"對話方塊。
5)選擇"格式化文字"並確定。
6)按一下"關閉"按鈕,關閉"自訂工具列"對話方塊。
7)試著在某個儲存格中輸入文本,按一下工具列按鈕運行該巨集。
2.6 小結
小結與思考: 宏存放於三個可能的位置。個人宏工作簿存放的位置和特性。執行巨集的方式。
指定巨集是為某個物件的事件指定一個程式,一旦這個物件以該事件啟動,系統將運行指定的程
序。
常用的對象有:workbook,worksheet,range,cells,圖表,圖片,樞紐分析表,控制項,表單,工
具欄.每一個物件都有其可以響應的特殊事件(也有一些通用事件如按一下或按兩下等)。如有興趣,
可以通過 EXCEL 幫助檔查詢這幾個詞條。在 EXCEL 中看到的幾乎都是屬於某個對象,而在 EXCEL
中所做的許多工作,如移動一下滑鼠等等,都可能觸發了一個事件。下一學時我們將共同學習"
控制項"。
第三課學習控制項
3.1 EXCEL 開發過程簡介
需要對以下問題有個大致的概念.
1)誰使用-----這決定了程式的操作難度及介面感觀.
2)資料來源和保存在哪裡-----這決定了程式的結構.
3)如何操作-----這將決定程式的介面和細節.
4)資料處理的結果-----最終決定程式的價值.
3.2 認識不同的控制項
開始時請關閉所有工作簿,打開一個新工作簿並另存為"HOUR3".在工具列上按一下滑鼠右鍵,
從快顯功能表中選擇"表單",顯示"表單"工具列.其中有 16 個控制項,只有 9 個可放到工作表內。
1)標籤:它用於表現靜態文本。
2)群組方塊:它用於將其他控制項進行組合。
3)按鈕:用於執行巨集命令。
5
Visual BASIC 程式設計網路教學

4)核取方塊:它是一個選擇控制項,通過按一下可以選擇和取消選擇,可以多項選擇。
橄欖樹

5)選項按鈕:通常幾個選項按鈕群組合在一起使用,在一組中只能選擇一個選項按鈕。
6)清單方塊:用於顯示多個選項並從中選擇。只能單選。
7)下拉式列示方塊:用於顯示多個選項並從中選擇。可以選擇其中的專案或者輸入一個其它值。
8)捲軸:不是你常見的來給很長的表單添加滾動能力的控制項,而是一種選擇機制。例如
調節過渡色的捲軸控制項。包括水準捲軸和垂直捲動條。
9)微調控件:也是一種數值選擇機制,通過按一下控制項的箭頭來選擇數值。例如改變 Windows
日期或時間就會使用到微調控件。
3.3 向工作表添加控制項
用 EXCEL 設計介面十分簡單,要將控制項添加到工作表上,可以按以下步驟操作:
1)創建新工作簿並另存為"HOUR3",顯示"表單"工具列.
2)選擇"標籤"控制項.
3)將滑鼠定位到 E1,此時滑鼠變成小十字.
4)按下左鍵,拖動大約四個儲存格長度,放開滑鼠左鍵.如果希望控制項大小易於控制,可在創
建該控制項時按下 ALT 拖動.
5)在標籤 1 上按一下右鍵,選擇"編輯文字",現在可以輸入文字.完成後,按一下任何儲存格退出
文字編輯.
6)通過以上步驟可以添加其它控制項到工作表中,不再贅述.
3.4 設置控制項的特性
設置控制項的特性,可以按以下步驟操作:
1)選中先前創建的核取方塊控制項,如果沒有馬上創建一個.
2)右擊該控制項,選擇"控制"選項卡.
3)在"儲存格連結"中輸入 A1 並確定.
4)按一下任意儲存格,退出設置.
5)用滑鼠左鍵按一下核取方塊,A1 出現 TRUE,這意味著該控制項被選中.再次按一下該控制項,A1 出現
FALSE.
6)選擇剛才創建的捲軸控制項.並調出"設置控制項格式"對話方塊.
7)在"儲存格連結"中輸入 A3 並確定.
8)在捲軸外任意儲存格按一下滑鼠左鍵,使捲軸不被選擇.
9)用滑鼠按一下捲軸上的箭頭,則 A1 的數值增加 1,繼續按一下則 A1 的數值繼續增加.
10)保存並關閉該工作簿.
3.5 給控制項命名
當創建一個控制項時 EXCEL 會自動給它指定一個名字,但不便於理解和記憶,為控制項取名的方
法基本和給儲存格或區域取名的方法相同.選中某個控制項,再在位於公式欄上的"名字"編輯方塊輸
入控制項名字.這樣就給控制項更改了名字.
3.6 使用使用者表單
如果希望創建專業級的應用程式,並且方便使用者輸入資料,那麼應該使用使用者表單.用戶窗
體可以作為程式的對話方塊和視窗.向使用者表單添加控制項基本類似于向工作表添加控制項,然而第一
步要創建一個使用者表單.這可以通過 VBA 編輯器實現.具體按以下步驟操作:
1)打開"HOUR3"工作簿,選擇"工具"-"宏"-"VBA 編輯器",打開 VBA 編輯器.
2)在 VBA 編輯器中選擇工具列上的"插入使用者表單"按鈕或者選擇"插入"功能表,從下拉式功能表
中選擇"使用者表單"
現在,VBA 編輯器中出現一個名為"USERFORM1"的表單,"控制項工具箱"同時出現,在其中有許
多已經熟悉的控制項,另外還有一些新的控制項.
這些新的控制項是:
A)切換按鈕:該控制項如果被選中,那麼會保持被按下的狀態.如果再次按一下它就恢復為沒有
按下的狀態.EXCEL 工具列中有幾個這樣的按鈕,例如:"全屏顯示","加粗","底線"以及"表單"
工具列中的"切換網格"等.
B)選項卡條(TabStrip):它是包含多個選項卡的控制項.通常用來對相關的資訊進行組織或分
6
Visual BASIC 程式設計網路教學

橄欖樹

類.例如:你也許希望用選項卡條來顯示各個地區的銷售資訊,可以給每個地區設置一個選項卡.
在預設時,選項卡包含兩頁,分別叫做 TAB1 和 TAB2,可以添加更多的選項卡.
C)多頁:外觀類似選項卡條,是包含一頁或多頁的控制項.選項卡條給人相似的外觀,而多頁控
件的各頁包含各自不同的控制項,有各自不同的佈局.多頁的例子很多,例如:"設置控制項格式"對話
框和"工具"功能表中的"選項"對話方塊.以及"格式"功能表中的"儲存格..."對話方塊.
D) 圖像控制項: 它允許向表單上放置圖片. 圖片格式須
為:*.bmp,*.cur,*.gif,*.ico,*.jpg,*.wmf.
F)RefEdit:這是工具箱中預設情況下的最後一個控制項。它外觀象文字方塊,通過這個控制項可
以將使用者表單折疊起來,以便選擇儲存格區域。還記得在使用 fx"粘貼函數"時的情況嗎?
通過實踐,我們會逐漸掌握每個控制項的特性,這的確需要花時間,但不必死記硬背。
在對使用者表單設計得滿意時,可以對其進行預覽,方法是在 VBA 編輯器中選擇該表單,單
擊"運行"功能表中的三角符號"運行子過程/使用者表單",三角符號在 VBA 工具列上也可能看得到,
旁邊是一個垂直的等於符號,最右邊是個小正方形符號,它們類似於答錄機上的按鈕。運行窗
體的另一個方法是按 F5 鍵。
小結:學習完本學時後,我們具備了用於程式介面設計的基本知識。我們對控制項不在感到
陌生,也明白如何向工作表和表單添加控制項,但控制項的內容很多,需要邊用邊理解.此後,我們將
從介面轉移到學習編寫代碼,並最終將二者融合。讓我們準備好學習程式設計吧!
3.7 疑難排解
問題 1.怎樣決定控制項的位置?如何選擇添加到工作表還是添加到使用者表單?
解答:這完全取決於個人的愛好和應用程式的使用者.如果用戶對 EXCEL 非常熟悉,那麼他們
也許更希望以工作表的方式操作.在這種情況下不妨直接在工作表上創建控制項;如果你的用戶對
EXCEL 不熟悉或者你需要給使用者一個專業的介面感覺,那麼應該使用使用者表單.
問題 2.什麼情況下該用選項卡條而不是多頁控制項?
解答:如果每一頁具有相同佈局,則應選擇選項卡條,否則應該選擇多頁.
本節作業
1.思考:
1)列舉兩種可以讓用戶進行多選一的控制項。
2)如何將控制項與儲存格連結起來。
2.判斷:
1)只有在 VBA 編輯器中才能添加使用者表單。
2)在 VBA 編輯器中看到的表單格線在運行時會自動顯示。
3.填空:( )是顯示靜態文本的控制項。
第四課理解變數和變數的作用
4.1 代碼存在的位置:模組
VBA 代碼必須存放在某個位置,這個地方就是模組。有兩種基本類型的模組:標準模組和
類別模組。模組中的每個過程或者是函數過程,或者是副程式概念.本課的最後部分將討論函數過
程和副程式的區別。
新術語:
模組:它是作為一個單元保存在一起的 VBA 定義和過程的集合。
類別模組:VBA 允許你創建自己的物件,物件的定義包含在類別模組中。
你的大部分工作集中在標準模組中(簡稱為模組)當錄製巨集時如果不存在模組,EXCEL 自
動創建一個。EXCEL 和 VBA 不關心代碼存放在哪一個模組中,只要代碼存在於打開的工作簿中
即可。
4.2 對模組的概覽
過程被定義為 VBA 代碼的一個單元,過程中包括一系列用於執行某個任務或是進行某種計
算的語句。工作簿的每個過程都有唯一的名字加以區分。
有兩種不同的過程:副程式和函數過程。副程式只執行一個或多個操作,而不返回數值。
當錄製完宏查看代碼時,所看到的就是副程式。巨集只能錄製副程式,而不能錄製函數過程。一
個副程式的例子如清單 4-1 所示。
程式清單 4-1 副程式的例子
7
Visual BASIC 程式設計網路教學

Sub cmdSmallFont_Click()
With Selection.Font
.Name="Arial"
.FontStyle="Regular"
.Size=16
End With
End sub
橄欖樹

上面列出的過程實際上是一個事件程序。通過它的名字,就可以知道這是一個事件程序。
這個過程的名字是由一個物件的名字 CmdSmallFont 和一個事件的名字 Click 組成的,兩者之間
用底線分開。如果還不明白,可以告訴你,CmdSmallFont 是一個命令按鈕的名字。也就是說,
當按一下這個命令按鈕時,就會運行這個事件程序。
函數過程通常情況下稱為函數,要返回一個數值。這個數值通常是計算的結果或是測試的
結果,例如 False 或 True.正如前面所說,可以用 VBA 創建自訂函數。實際上可以在工作表
上使用你創建的函數。程式清單 4-2 是一個計算價格的 10%為運費的簡單例子。
程式清單 4-2 簡單的使用者定義函數示例。
Public Function Shipping(Price)
Shipping = Price * 0.1
End Function
請注意,這個函數使用一個參數(Price).副程式和函數都可以使用參數。不論 Price 的值
是多少,它都將決定運費額。Price 可以是數位和儲存格引用。函數返回計算出來的運費,這
個函數可以用在儲存格中。
AB
1 Price 100
2 Shipping =shipping(B1)
4.2.1 創建過程
創建第一個過程需要兩個基本步驟。首先,需要向工作簿中添加一個模組。接著需要向模
塊中添加一個工程。對於創建的每一個應用程式,只需添加一次模組。可以使用多個模組,但
這是不必要的。某些開發者喜歡使用多個模組,以便根據他們的目的或者表單對過程進行組織。
在本練習中,創建的過程只顯示一個訊息方塊。
在本練習中創建的過程只顯示一個訊息方塊。在本練習中使用 Msgbox 是為了提供一個可見的
例子,雖然我們還沒有介紹過 Msgbox 語句,但是在本例中將使用它。要創建該過程,請按如下
步驟進行:
1)打開一個新工作簿。
2)選擇"工具"-"宏"-"Visual Basic 編輯器",打開 VBA 編輯器視窗。
3)在`VBA 編輯器的左面,可以看到"工程資源管理器"視窗。在工程資源管理器視窗的
"Thisworkbook"上按一下滑鼠右鍵,選擇"插入"-"模組",這樣就將一個模組添加到應用程式中了。
(如果你沒有看見"工程資源管理器"視窗,可以按 Ctrl+R)
4)選擇"插入""過程",顯示"添加過程"對話方塊。
5)輸入"第一個工程"作為過程名字。在"類型"群組方塊中,確認選擇了"副程式"。按一下"確
定"按鈕。這樣一個新的過程就添加到模組中了。可以在模組中看到以 Public Sub 第一個過程
()開始,以 End Sub 結束的語句結構。
6)在過程中插入游標,輸入以下語句並回車:
Msgbox "這是我的第一個過程"
在輸入 Msgbox 後,會自動彈出一個訊息方塊告訴你有關這條命令的資訊,稱之為自動清單技術。
輸入完成的過程如下所示:
Public Sub 第一個過程()
Msgbox "這是我的第一個過程"
End Sub
VBA 對副程式和函數有如下的命名規則:
* 名字中可以包含字母數位和底線。
* 名字中不能包含空格句號驚嘆號,也不能包含字元@ & $ #.
* 名字最多可以包含 255 個字元。
8
Visual BASIC 程式設計網路教學

4.2.2 運行宏
橄欖樹

創建這個過程後,可以運行一下。運行一個過程有幾種方法:可以直接使用"運行"功能表,"
運行副程式/使用者表單"工具列按鈕或按下 F5 鍵。要運行一個過程,可以按照如下步驟:
1)按一下"運行副程式/使用者表單"工具列按鈕,過程執行並顯示一個訊息方塊。
2)按一下訊息方塊之中的"確定"按鈕,關閉該訊息方塊。
4.3 保存對模組所做的改變
要保存新過程,需要保存過程所駐留的工作簿.可以用 VBA 編輯器保存工作簿.具體步驟如
下:
1)選擇"文件"-"保存工作簿".因為本工作簿還沒有保存過,所以要給它命名.
2)輸入"HOUR4"作為檔案名並按回車鍵,則工作簿和模組與過程都保存下來了. 第四課理
解變數和變數的作用
Excel Home
4.4 變數
變數是用於臨時保存數值的地方.每次應用程式運行時,變數可能包含不同的數值,而在程
序運行時,變數的數值可以改變.
為了說明為什麼需要變數,可以按照如下步驟創建一個簡單的過程:
1)創建一個名為"你叫什麼名字"的過程.
2)在過程中輸入如下代碼:
Inputbox "輸入你的名字:"
現在不要擔心 inputbox 語句的語法,將在第六學時中瞭解到有關這條命令的更多資訊.
3)按下 F5 鍵運行過程,這時會顯示一個輸入框,要求輸入你的名字.
4)輸入你的名字並按"確定"按鈕,則結束該過程.
你輸入的名字到那裡去了?如何找到使用者在輸入框中輸入的資訊?在這種情況下,需要使用
變數來存儲使用者輸入的結果.
4.4.1 變數的資料類型
使用變數的第一步是瞭解變數的資料類型.變數的資料類型控制變數允許保存何種類型的
數據.表 4-1 列出了 VBA 支持的資料類型,還列出了各種類型的變數所需要的存儲空間和能夠存
儲的數值範圍.
資料類型存儲空間數值範圍
Byte 1 位元組 0 - 255
Booleam 2 位元組 True 或者 False
Integer 2 位元組-32768 - 32767
Long(長整型) 4 位元組-2147483648 - 2147483647
Single 4 位元組負值範圍:-3.402823E38 - -1.401298E-45
正值範圍:1.401298E-45 - 3.402823E38
Double 8 位元組負值範圍:-1.79769313486232E308 -
-494065645841247E-324
正值範圍:4.94065645841247E-324 -
1.79769313486232E308
Currency 8 位元組-922337203685477 - 922337203685477
Decimal 14 位元組不包括小數時:+/-79228162514264337593543950335
包括小數時:+/7.9228162514264337593543950335
Date 8 位元組 1000 年 1 月 1 日- 9999 年 12 月 31 日
Object 4 位元組任何引用物件
String(長字串) 10 位元組+1 位元組/字元 0 - 約 20 億
String(固定長度) 字串的長度 1 - 約 65400
Varient(數位) 16 位元組 Double 範圍內的任何數值
Varient(文本) 22 位元組+1 位元組/字元資料範圍和變長字串相同
表 4-1 VBA 資料類型
作為 ABV 程式師,一個目標是選擇需要存儲空間儘量小的資料類型來保存所需要的資料,
這正是表 4-1 提供各種資料類型存儲空間的原因。例如,要保存諸如班級學生總數這樣的小數
9
Visual BASIC 程式設計網路教學

橄欖樹

字,那麼 Byte 資料類型就足夠了。在這種情況下,使用 Single 資料類型只是對電腦存儲空
間的浪費。
4.4.2 用 Dim 語句創建變數(聲明變數)
現在,你對變數可以使用的資料類型已經比較熟悉了,以下我們將創建變數.創建變數可以
使用 Dim 語句,創建變數通常成為"聲明變數" Dim 語句的基本語法如下:
Dim 變數名 AS 資料類型
這條語法中的變數名代表將要創建的變數名.對變數的命名規則和對過程的命名規則相同.
這條語句中的資料類型部分可以是表 4-1 中的任何一種資料類型.
變數名必須以字母開始,並且只能包含字母數位和特定的特殊字元,不能包含空格句號驚歎
號,也不能包含字元@ & $ #.名字最大長度為 255 個字元
在接下來的練習中將說明如何在 VBA 中使用變數,你將要輸入你的名字,並用一個訊息方塊將
其顯示出來.具體步驟如下:
1)創建一個名為"顯示你的名字"的副程式.
2)輸入以下代碼:
Public Sub 顯示你的名字()
Dim s 名字 As String
s 名字= Inputbox("請輸入你的名字:")
Msgbox "你好"& s 名字
End Sub
3)將滑鼠放到過程中的任何地方,按下 F5 鍵運行過程,會顯示一個輸入框.
4)輸入你自己的名字並按回車鍵,會顯示一個訊息方塊,顯示的文字中包含你自己的名字.
5)按一下"確定"按鈕,返回過程中.
在 Dim 語句中不必提供資料類型.如果沒有資料類型,變數將被定義為 Variant 類型,因為
VBA 中默認的資料類型是 Variant.你知道這一點後,最初的反應也許是覺得應該不用自己決定
資料類型,而將一切拋給 VBA.這種觀念是完全錯誤的.你必須決定選擇使用何種資料類型。因為
Variant 資料類型佔用存儲空間較大(16 或 22 位元組)而且它將影響程式的性能。VBA 必須辨別
Variant 類型的變數中存儲了何種類型的資料。
4.4.3 變數命名的慣例
下表給出了推薦的變數命名慣例
資料類型短首碼長首碼
Array a ary
Boolean f bin
Byte b bit
Currency c cur
Double d dbl
Date/Time dt dtm/dat

Integer i int
Long l lng
Object o obj
Single sng
String s str
Variant v var
表 4-2 變數命名的首碼
4.4.4 使用陣列
如果你使用過其他編程式語言,可能對陣列已經比較熟悉了.陣列是具有相同資料類型並共
同享有一個名字的一組變數的集合.陣列中的元素通過索引數位加以區分,定義陣列的方法如
下:
Dim array_name(n) As type (其中 n 是陣列元素的個數)
例如,如果要創建保存 10 個學生名字的陣列,可以用以下語句:
Dim s 學生名字(9) As Integer
注意,括弧中的數字是 9 而不是 10.這是因為在預設的情況下,第一個索引數位是 0.陣列在
處理相似資訊時非常有用.假設要處理 15 門考試成績,可以創建 15 個獨立的變數,這意味著要使
用 15 個 Dim 語句。也可以創建一個陣列來保存考試成績,具體如下:
10
Visual BASIC 程式設計網路教學

Dim s 考試成績(14) As Integer
橄欖樹

聲明陣列時的另一種方法是不給定大小。可以在程式運行時定義其大小。通過創建動態數
組就可以做到。例如,你的程式要創建一表格,可以提示使用者輸入表格的行和列的數目。聲明
動態陣列的語法如下:
Dim dyn_array() As type
對陣列聲明後可以在程式運行時用:ReDim 語句指定陣列的大小:
ReDim dyn_array()(array_size)
參數 array_size 代表陣列的新大小。如果要保留陣列的數值,請在 ReDim 語句後使用保留
字 Preserve,具體語法如下:
ReDim Preserve dyn_array(array_size)

4.4.5 變數賦值
聲明變數後就可以給變數賦值。請注意下列語句中為陣列變數賦值時索引數位的使用。
程式清單 4-4
Dim i 人數 As Integer
Dim i 考試成績 As Integer
Dim i As Integer
i 人數= inputbox("輸入學生的人數:")
ReDim Preserve i 考試成績(i 數量)
For i = 1 to i 人數
i 考試成績(i) = inputbox("輸入考試成績"& i )
Next
第五課利用 VBA 設置工作表使用權限
Excel Home
一般保護工作表採取的方法是用 EXCEL 功能表中的"保護"命令,有時這尚嫌不足,比如一些機密
檔根本要讓某些使用者無法看到,但又需要他來操作工作簿中的其他表,怎麼辦?
可以打開 VBA 編輯器,打開"工程資源管理器",按兩下該工作表,現在出現的是設置該表的屬性
的編輯視窗,按一下視窗左上的下拉式清單方塊,選擇 worksheet ,這時再從該視窗右上方的清單方塊
中選擇 Active("啟動"),這時自動顯示如下的語句塊:
Private Sub Worksheet_Activate()
End Sub
在其中加入代碼:(假設用"123"作為密碼,Sheet"機密文檔"為限制許可權文
檔,sheet"普通文檔"為工作簿中你認為任何適合的工作表)
If Application.InputBox("請輸入操作許可權密碼:") = 123 Then
Range("A1").Select
Else
Msgbox "密碼錯誤,即將退出!"
Sheets("普通文檔").Select
End if
程式如下:
Private Sub Worksheet_Activate()
If Application.InputBox("請輸入操作許可權密碼:") = 123 Then
Range("A1").Select
Else
MsgBox "密碼錯誤,即將退出!"
Sheets("普通文檔").Select
End If
End Sub
這樣做仍有一個問題,就是越權使用者仍會看到一些檔的片段,即在提示密碼的那段時間。
好,你可以這樣做,用上述方法選擇工作表的 Deactivate 事件,輸入以下代碼:
Sheets("機密文檔").Cells.Font.ColorIndex = 2
這段程式使得此工作表在不被啟動時, 所有文字為白色。然後, 在第一個
程式中的
Range("A1").Select 後插入一行,寫入以下代碼:
11
Visual BASIC 程式設計網路教學

ActiveSheet.Cells.Font.ColorIndex = 56

這段程式,在你輸入正確密碼後,將該表所有文字轉變為深灰色。
完整的程式如下:
Private Sub Worksheet_Activate()
If Application.InputBox("請輸入操作許可權密碼:") = 123 Then
Range("A1").Select
Sheets("機密文檔").Cells.Font.ColorIndex = 56
Else
MsgBox "密碼錯誤,即將退出!"
Sheets("普通文檔").Select
End If
End Sub
第六課提高 Excel 中 VBA 的效率
橄欖樹

由於 Microsoft Office 辦公套件的廣泛應用,以及該軟體版本的不斷提升,功能不斷完善,
在 Office 辦公套件平臺上開發出的的 VBA 應用程式越來越多,而 VBA 是一種巨集語言,在運行速
度上有很大的限制。因此 VBA 程式設計的方法直接關係到 VBA 程式運行的效率,本文列舉了一些提
高 VBA 程式運行效率的方法。
方法 1:儘量使用 VBA 原有的屬性、方法和 Worksheet 函數
由於 Excel 物件多達百多個,物件的屬性、方法、事件多不勝數,對於初學者來說可能對
它們不全部瞭解,這就產生了程式設計者經常編寫與 Excel 物件的屬性、方法相同功能的 VBA 代碼
段,而這些程式碼片段的運行效率顯然與 Excel 物件的屬性、方法完成任務的速度相差甚大。例如
用 Range 的屬性 CurrentRegion 來返回 Range 物件,該物件代表當前區。(當前區指以任意空
白行及空白列的組合為邊界的區域)。同樣功能的 VBA 代碼需數十行。因此程式設計前應盡可能多地
瞭解 Excel 物件的屬性、方法。
充分利用 Worksheet 函數是提高程式運行速度的極度有效的方法。如求平均工資的例子:
For Each c In
Worksheet(1).Range(″A1:A1000″)
TotalValue = TotalValue + c.Value
Next
AverageValue = TotalValue / Worksheet(1).Range(″A1:A1000″).Rows.Count
而下面代碼程式比上面例子快得多:
AverageValue=Application.WorksheetFunction.Average(Worksheets(1).Range( ″
A1:A1000″))
其它函數如 Count,Counta,Countif,Match,Lookup 等等,都能代替相同功能的 VBA 程式代
碼,提高程式的運行速度。
方法 2:儘量減少使用物件引用,尤其在迴圈中
每一個 Excel 物件的屬性、方法的調用都需要通過 OLE 介面的一個或多個調用,這些 OLE
調用都是需要時間的,減少使用物件引用能加快 VBA 代碼的運行。例如
1.使用 With 語句。
Workbooks(1).Sheets(1).Range(″A1:A1000″).Font.Name=″Pay″
Workbooks(1).Sheets(1).Range(″A1:A1000″).Font.FontStyle=″Bold″ ...
則以下語句比上面的快
With Workbooks(1).Sheets(1).Range(″A1:A1000″).Font
.Name = ″Pay″
.FontStyle = ″Bold″
...
End With
2.使用物件變數。
如果你發現一個物件引用被多次使用,則你可以將此物件用 Set 設置為物件變數,以減少
對對象的訪問。如:
12
Visual BASIC 程式設計網路教學

Workbooks(1).Sheets(1).Range(″A1″).Value = 100
Workbooks(1).Sheets(1).Range(″A2″).Value = 200
則以下代碼比上面的要快:
Set MySheet = Workbooks(1).Sheets(1)

MySheet.Range(″A1″).Value = 100
MySheet.Range(″A2″).Value = 200
3.在迴圈中要儘量減少對象的訪問。
For k = 1 To 1000
Sheets(″Sheet1″).Select
Cells(k,1).Value = Cells(1,1).Value

Next k
則以下代碼比上面的要快:
Set TheValue = Cells(1,1).Value
Sheets(″Sheet1″).Select
For k = 1 To 1000
Cells(k,1).Value = TheValue
Next k
方法 3:減少物件的啟動和選擇
橄欖樹

如果你的通過錄製巨集來學習 VBA 的,則你的 VBA 程式裡一定充滿了物件的啟動和選擇,例
如 Workbooks(XXX).Activate、Sheets(XXX).Select、Range(XXX).Select 等,但事實上大多數
情況下這些操作不是必需的。例如
Sheets(″Sheet3″).Select
Range(″A1″).Value = 100
Range(″A2″).Value = 200
可改為:
With Sheets(″Sheet3″)
.Range(″A1″).Value = 100
.Range(″A2″).Value = 200
End With
方法 4:關閉螢幕更新
如果你的 VBA 程式前面三條做得比較差,則關閉螢幕更新是提高 VBA 程式運行速度的最有
效的方法,縮短執行時間 2/3 左右。關閉螢幕更新的方法:
Application.ScreenUpdate = False

請不要忘記 VBA 程式運行結束時再將該值設回來:


Application.ScreenUpdate = True

以上是提高 VBA 運行效率的比較有效的幾種方法。


第七課如何在 Excel 裡使用計時器
用過 Excel 97 裡的增益集"定時保存" 嗎?可惜它的來源程式是加密的,現在就上傳一篇
介紹實現它的文檔。
在 Office 裡有個方法是 application.ontime ,具體函數如下:
expression.OnTime(EarliestTime, Procedure, LatestTime, Schedule)

如果想進一步瞭解,請參閱 Excel 的幫助。


這個函數是用來安排一個過程在將來的特定時間運行,(可為某個日期的指定時間,也可為
指定的時間段之後)。通過這個函數我們就可以在 Excel 裡編寫自己的定時程式了。下麵就舉
兩個例子來說明它。
1.在下午 17:00:00 的時候顯示一個對話方塊。
Sub Run_it()
Application.OnTime TimeValue("17:00:00"), "Show_my_msg"

'設置計時器在 17:00:00 啟動,啟動後運行 Show_my_msg 。


End Sub
13
Visual BASIC 程式設計網路教學

Sub Show_my_msg()
msg = MsgBox("現在是 17:00:00 !", vbInformation, "自訂信息")
End Sub
2.模仿 Excel 97 裡的"自動保存宏",在這裡定時 5 秒出現一次
Sub auto_open()
橄欖樹

MsgBox "歡迎你,在這篇文檔裡,每 5 秒出現一次保存的提示!", vbInformation, "請注意!
"
Call runtimer '打開文檔時自動運行
End Sub
Sub runtimer()
Application.OnTime Now + TimeValue("00:00:05"), "saveit"

' Now + TimeValue("00:15:00") 指定在當前時間過 5 秒鐘開始運行 Saveit 這個過程。


End Sub
Sub SaveIt()
msg = MsgBox("朋友,你已經工作很久了,現在就存檔嗎?" & Chr(13) _
& "選擇是:立刻存檔" & Chr(13) _
& "選擇否:暫不存檔" & Chr(13) _
& "選擇取消:不再出現這個提示", vbYesNoCancel + 64, "休息一會吧!")
'提示用戶保存當前活動文檔。
If msg = vbYes Then ActiveWorkbook.Save Else If msg = vbCancel Then Exit Sub

Call runtimer '如果用戶沒有選擇取消就再次調用 Runtimer


End Sub
以上只是兩個簡單的例子,有興趣的話,可以利用 Application.Ontime 這個函數寫出更多更
有用的定時程式。
14
三、學習微軟 Excel 2002 VBA 程式設計和 XML,ASP 技術
ASP 已經趨於淘汰了,Vbscript 將慢慢退出舞臺了,其實他是 vba 的子集,相比 vba 來講更容易
一些,用不著深入學
摘自 2004-3-8hzg7818
版主的答覆
作者:Julitta Korol 翻譯:Tiger Chen Nov 28’ 2004
本書展示了 Excel 2002 在標準使用者介面之外什麼是可行的。如果你曾經想不使用功能表來打開
一個新的工作表,或者創建一個充分自動化的自訂表單來收集資料和在工作表裡存儲結果,那麼
你必須學習一些程式設計。本書教你如何通過將一些費時的和重複的工作交給 Excel,從而更加成果豐
富。使用 Excel 內置語言,VBA (Visual Basic for Applications),你將給自己或他人帶來非常高
的自動化程度的試算表。通過使用許多內置的程式設計工具,你做得可以比想像中容易得多。你不要
增加額外費用,除非你想熟悉 Excel 背後的秘密。在 Excel 視窗下,同時按下 Alt+F11,你將進入 VB
編輯器介面——Excel 的程式設計介面。既然這個保護得很好的秘密已經公開了,就讓我告訴你更多一
些。除了 VBA 之外,本書還介紹了兩種可以和 Excel 並用的熱門英特網技術。一種是 ASP (Active
Server Pages),另一種是 XML (Extensible Markup Language)。你也可以學習到許多其它的支援
技術。因此,如果你真正想要獲得一些熱門技術,請立即購買本書,並且不要浪費時間,馬上開始
學習。Learn Microsoft Excel 2002 VBA Programming with XML and ASP 帶領你從始至終創建 VBA
過程,VBScripts,ASP 頁面,XML 檔和 XSL 工作表。沿著這條路,有許多詳細的,適用的“如
何做”例子和插圖。本書的方法是“由做而學”。本書的前面幾章介紹了一些基本的 VBA 概念,循序
漸進,複雜的主題在後面的章節。十七章中的每一章是按循序的。此外,本書還由四章附錄,討論
在 Excel 裡針對一些特殊方面的操作和程式設計。本書可以當作是一種在辦公室或家裡學習的課程。許
多課程都有前提條件,本書也不例外。Learn Microsoft Excel 2002 VBA Programming with XML and
ASP 不會向你介紹 Excel 的基本東西,例如菜單和快速鍵。我們假設你已經喜歡使用 Excel,並且有
興趣學習如何與 Excel 在它自己的語言裡交流,學習如何將它與現在的英特網技術結合。
第一章試算表自動化簡介和瞭解巨集命令
http://club.excelhome.net/dispbbs.asp?boardID=2&ID=72173&page=5

你準備好了增進你的微軟 Excel 2002 試算表的智能嗎?通過將日常工作自動化,你可以使


你的試算表更快,更有效。本章帶領你步入使用巨集命令來加速試算表的過程。你將學到宏是什
麼,如何以及什麼時候使用它們,乃至如何編寫和修改宏代碼。開始學習巨集命令很簡單。創建宏並
不需要什麼,只是一些你已經擁有的知識——基本的微軟 Excel 2002 菜單知識和試算表概念。
你準備好開始了嗎?確保你坐在電腦前並且打開了 Excel 2002。
1 瞭解宏
巨集是一些儲存了一系列命令的程式。當你創建一個巨集命令的時候,你只是將一系列的鍵盤輸入結合
成一個簡單的命令,你以後可以“回演”這個命令。因為巨集命令可以減少複雜任務的步驟,使用巨集
命令可以顯著得減少你花在創建,設置格式,修改和列印工作表的時間。你可以通過 Excel 內置的
錄製工具來創建巨集命令,也可以在代碼編輯器裡面直接寫代碼。微軟 Excel 2002 試算表具有強
大的程式設計功能。
技巧 1-1:普通語言
Excel 5 是市場上第一個使用 VBA 的軟體。從那以後,VBA 開拓了在所有微軟辦公應用軟體中的應用。
這意味著你從本書中學習的 VBA 將來同樣可以應用到其它微軟辦公軟體中,例如:Word, PowerPoint,
Outlook or Access
2 巨集命令的普通應用
微軟 Excel 2002 帶來了很多內置,節省時間的特點,這些使你工作得更快更聰明。在你決定用宏
命令來自動化工作表任務前,確保沒有現成的內置工具來做這項任務。然而,當你發現你需要反復
地做一些動作,或者 Excel 沒有提供一個內置工具幫你完成該任務,那麼創建一個巨集命令吧。宏命
令可以使你能夠將工作表的任何部分工作自動化。例如,你可以自動化資料錄入——創建一個巨集命
令在工作表輸入標題或者用新標籤取代列標題。巨集命令可以幫你檢查選中的工作表區域裡的重複
15
值。你可以通過巨集命令快速地將格式應用到多個工作表,並且可以結合不同的格式,例如字體,顏
色,邊框和陰影等。儘管如此,Excel 還擁有非常強大的圖表功能,如果你想要將圖表創建和格式
設置自動化,那麼巨集命令是一個好方法。巨集命令也可以幫助你設置列印範圍,頁邊距,頁眉,頁腳,
以及選擇特殊的列印選項。
3 寫宏之前的計畫
在你創建一個巨集命令之前,花幾分鐘來考慮你究竟想做什麼。因為巨集命令是一大堆鍵盤輸入的集合,
事先計畫你的行動非常重要。最早的計畫巨集命令的方法是手動地將巨集命令需要做的事情做一遍。在
你做鍵盤輸入的同時,在一張紙上記錄下他們實際發生的情況,不要漏掉任何東西。象答錄機一樣,
Excel 可以將你的所有動作錄製下來(譯者:事實上並非如此,有些操作是無法錄製的)。如果在
錄製巨集之前,你沒有很好地計畫,你會錄製很多不必要的步驟,而這些都會影響運行速度。儘管修
改巨集代碼比去除錄製巨集裡面不必要的步驟容易,但是,僅僅錄製必要的步驟會節省你修改代碼的時
間和以後的麻煩。
假設你想看一眼哪些區域是文本,哪些是數位以及哪些是公式,圖 1-1 顯示了使用不同的字體顏色
和樣式來區分這些儲存格裡潛在的內容。
圖 1-1
如何設置如圖 1-1 所示的格式?打開一個含有公式計算的工作表,或者創建一個如圖所示的例子。
如果你用圖示例子,請確保用 SUM 函數計算每月和季度的總結。
在錄製巨集之前,請做如下操作:
1. 選取一個儲存格
2. 選擇“編輯”-“定位”
3. 在“定位”對話方塊中,點擊“特殊”按鈕
4. 在“特殊”對話方塊中,勾選“常數”——勾選“文本”,同時去除“數位”,“邏輯值”和“錯
誤值”的勾選
5. 按“確定”返回工作表。注意,這時含有文本的儲存格已經被選中了。小心,不要改變選中
區域,直到你在下一步做一些必要的格式設置
6. 對選中區域。選擇“格式”-“儲存格”
7. 在單元格格式設置對話方塊,選擇“字體”-設置字體為“粗體”,顏色為“紫色”。然後點擊
“確定”關閉對話方塊。注意,含有文本的儲存格顯示了不同的顏色。
步驟 1 到 7 教你定位到文本儲存格,要定位數位儲存格,請按如下操作:
8. 選取一個儲存格
9. 選擇“編輯”-“定位”
10. 在“定位”對話方塊中,點擊“特殊”按鈕
11. 在“特殊”對話方塊中,勾選“常數”——勾選“數位”,同時去除“文本”,“邏輯值”和“錯
誤值”的勾選
12. 按“確定”返回工作表。注意,這時含有數字的儲存格已經被選中了。小心,不要改變選中
區域,直到你在下一步做一些必要的格式設置
16
13. 對選中區域。選擇“格式”-“儲存格”
14. 在單元格格式設置對話方塊,選擇“字體”-設置字體顏色為“暗藍色”。然後點擊“確定”關
閉對話方塊。注意,含有數位的儲存格顯示了不同的顏色。
步驟 8 到 14 教你定位到數位儲存格,要定位公式儲存格,請按如下操作:
15. 選取一個儲存格
16. 選擇“編輯”-“定位”
17. 在“定位”對話方塊中,點擊“特殊”按鈕
18. 在“特殊”對話方塊中,勾選“公式”
19. 按“確定”返回工作表。注意,這時含有公式計算結果的儲存格已經被選中了。小心,不要
改變選中區域,直到你在下一步做一些必要的格式設置
20. 對選中區域。選擇“格式”-“儲存格”
21. 在單元格格式設置對話方塊,選擇“字體”-設置字體為“粗體”,顏色為“紅色”。然後點擊
“確定”關閉對話方塊。注意,含有公式的儲存格顯示了不同的顏色。
步驟 15 到 21 教你定位到公式儲存格。你可以在工作表中加上圖例,以便容易理解。
22. 選取區域 A1:A3,選擇“插入”-“行”
23. 選擇儲存格 A1
24. 選擇“格式”-“儲存格”,並且在“填充色”頁點擊“紫色”,點擊“確定”返回工作表
25. 選擇 B1,輸入“文本”
26. 選擇儲存格 A2
27. 選擇“格式”-“儲存格”,並且點擊“暗藍色”,“確定”返回
28. 選擇 B2,輸入“數位”
29. 選擇 A3
30. 選擇“格式”-“儲存格”,點擊“紅色”,“確定”返回
31. 選擇 B3,輸入“公式”
32. 選擇 A1
完成步驟 22 到 32 後,A1:A3 儲存格會顯示一個簡單的顏色圖例,如圖 1-1 所示。
正如你所看到的,不管工作表顯示的任務多麼簡單,你卻可能需要很多步驟來達到預期效果。創建
一個可以重複你剛才操作的巨集命令,真的很省時間,特別是當你需要重複這個相同的工作到很多工
作表中去。
4 錄製巨集
既然你已經知道了你需要做哪些操作,是時候打開你的宏錄製器來創建你的宏了。在你依照下麵的
錄製步驟之前,請確保你已經清除了剛才例子裡的格式。按下 Ctrl+A 以選中整個工作表,選擇“編
輯”-“清除”-“格式”選擇 A1:A3 並且選擇“編輯”-“刪除”。在“刪除”對話方塊,選擇“整
行”然後點擊“確定”。
依照以下步驟來創建你的第一個巨集命令:
1. 選擇一個儲存格
錄製巨集之前,你應該想好是否要錄製目前的儲存格的位置。如果你需要巨集總是從一個特定的
位置開始,那麼先打開宏錄製器再選擇你想要宏開始選中的特定位置。如果目前的儲存格的
位置無關緊要,那麼先選擇一個儲存格,然後才打開宏錄製器。
選擇“工具”-“宏”-“錄製新宏”。出現一個錄製巨集對話方塊。
圖 1-2 當你錄製新宏的時候,必須有名字。你也可以給它設置一個快速鍵,儲存地方以及描述。
17
2. 給宏取個名字“WhatsInACell”
技巧 1-2:宏命名
如果你忘記給宏命名,Excel 會給出一個預設的宏名,例如:Macro1,Macro2,等等。宏名可以包
含字母,數位和底線,但是第一個字必須是字母(譯者:中文亦可,建議用英文)。例如: Report1
是有效的宏名,然而 1Report 則是非法的。宏名裡不能含有空格。如果你隔開宏名中的每個詞,可
以使用底線。例如:WhatsInACell,改為 Whats_In_A_Cell。
3. 在宏的存貯位置裡,選擇“當前工作簿”
技巧 1-3:保存宏
Excel 讓你可以將宏保存在三個地方:
個人巨集工作簿——如果你將宏保存在這裡,你每次使用 Excel 的時候都可以使用這個宏。個人宏工
作簿在 XLStart 資料夾中。如果這個工作簿不存在,那麼當你第一次使用這個選項的時候, Excel
會自動生成這個工作簿。
新工作簿——Excel 將宏放在一個新工作簿裡。
當前工作簿——宏將被保存在你正在使用的工作簿裡面。
4. 在描述框裡輸入:顯示儲存格裡潛在的內容:文本,數位,公式
5. 點擊“確定”關閉宏錄製對話方塊,並開始錄製。這時,出現了“停止錄製”工具列。Excel 下
面的狀態列顯示“準備錄製”
圖 1-3 停止錄製工具列提供按鈕來停止錄製,以及讓 Excel 如何在錄製巨集的時候處理儲存格位址
技巧 1-4:儲存格地址:相對或絕對?
絕對——如果在執行巨集命令的過程中,無論哪些儲存格被選中了,你都希望宏在特定的儲存格執行
這些錄製的操作,那麼使用絕對儲存格位址。絕對儲存格引用具有如下形式: $A$1,$C$5 等。Excel
宏錄製器預設使用絕對引用。在錄製前,確保停止錄製工具列上的第二個按鈕沒有被按下去。當鼠
標指向這個按鈕,工具提示“相對參照”。
相對——如果你想要巨集可以用在任何區域,就打開相對參照。相對參照具有如下形式:A1,C5 等。
在錄製前,確保停止錄製工具列上的第二個按鈕已經被按下去了。記住,Excel 將會繼續使用相對
引用,直到退出 Excel 或者再次點擊相對參照按鈕。在錄製巨集的過程中,你可以使用這兩種引用方
法。例如:你可以選擇一個特定儲存格(如$A$4),做一個操作,然後選擇另外一個相對於當前位
置的儲存格(如 C9,他在目前的儲存格$A$4 往下 5 行和向右 2 列的位置)。當你複製儲存格時,相對引
用會自動調整引用,而絕對引用則不會。
6. 從新進行剛才你手動完成的那些操作(參見“寫宏之前的計畫“)
錄製巨集的時候,只有當你按下了回車鍵或者點擊了確定之後,你的操作才會被錄製。如果
你按下了 Esc 鍵或者點擊了取消,宏錄製器不會錄製任何操作。
7. 完成所有操作後,點擊停止錄製工具列上的“停止錄製”按鈕,或者選擇“工具”-“宏”
-“停止錄製”
5 運行宏
你創建了一個巨集命令後,至少要運行一次以確保它運行正確。在本章的後面,你將學到好幾種運行
巨集的方法,不過,現在,使用功能表命令。要看到你的成果,確保清除了例子的格式。按下 Ctrl+A
以選中整個工作表,選擇“編輯”-“清除”-“格式”選擇 A1:A3 並且選擇“編輯”-“刪除”。
在“刪除”對話方塊,選擇“整行”然後點擊“確定”。稍後,你將在另外一個宏裡面錄製清除工作
表格式的步驟。
1. 打開任何包含文本,數位和公式的工作表
2. 選擇“工具”-“巨集”-“運行巨集”來打開巨集對話方塊
3. 點擊你要運行的宏的名稱(參見圖 1-4)
4. 選擇“運行”,執行宏
18
圖 1-4 在宏對話方塊,你可以選擇一個宏,運行,編輯或者刪除它
你也許經常會發現錄製的宏不會按你預期的和你第一次操作那麼運行。也許在錄製巨集的時候,你選
擇了錯誤的字體,或者忘記改變儲存格顏色,或者你臨時發現最好加上一個步驟。不必驚慌。Excel
允許你修改代碼,而不會強迫你重新錄製那些單調的操作。
6 修改宏代碼
你必須知道你的宏代碼放在哪裡,你才能找到並修改它。回想你打開宏錄製器的時候,你選擇了“當
前工作簿”作為存儲位址。最容易找到巨集的方法是打開巨集對話方塊,如圖 1-4 所示。
1. 選擇“工具”-“宏”
2. 選擇宏名(本例中為 WhatsInACell)
3. 點擊“編輯”按鈕
Excel 打開一個專門的視窗,叫做 Visual Basic Editor (VBE)如圖 1-5 所示。利用快速鍵 Alt+F11
可快速地在 Excel 表格介面和代碼視窗切換。選擇 VBE 功能表上的關閉選項可以關閉 VBA 代碼視窗,返
回到試算表介面。
代碼視窗暫時看上去有些令人迷惑,不必擔心。只要你開始錄製巨集,以及嘗試寫一些代碼,你終將
這個螢幕所有的元件。現在,看一下代碼視窗的功能表和工具列。這兩個工具列和 Excel 視窗的功能表
完全不同。代碼視窗的功能表和工具包含一些程式設計和測試代碼所需要的工具。只要你徹底地學習本書
的每一章,你就會成為使用這些工具的專家。
圖 1-5 VBE 視窗是編輯巨集命令和書寫新的 VBA 代碼的地方
VBE 視窗的主要部分是多個視窗的集合介面,這些視窗在你創建和測試 VBA 過程的時候是及其有用
19
的。圖 1-5 顯示了三個集合在一起的視窗:工程視窗,屬性視窗和代碼視窗。工程視窗顯示一個開
啟的模組資料夾,在這裡,模組 1 被選中了。Excel 錄製你在工作表裡的操作叫做模組 1,模組 2,
等等。在本書接下來的章節裡,你將利用模組來編寫你自己的過程代碼。模組類似於 Word 中的一個
空白文檔。儲存每個單獨模組的資料夾稱為“模組”
技巧 1-5:宏還是過程?
巨集是通過內置巨集錄製器錄製的,或者在 VB 編輯器裡手動輸入的一系列指令或函數。從 Excel 5.0 開
始,“宏”經常被“過程”這個更廣的概念所代替。儘管這兩個詞可以交替互換使用,但是,許多
程式設計者更喜歡“過程”。雖然巨集可以讓你模仿鍵盤操作,真正的過程則還可以執行一些不能通過鼠
標,鍵盤或者功能表來做的操作。換句話說,過程是一個更複雜的宏,它結合了傳統程式設計語言的言語
結構。
代碼視窗(參見圖 1-5)顯示了下列由巨集錄製器錄製的代碼:
Sub WhatsInACell()
'
' WhatsInACell Macro
' Macro recorded 5/31/2002 by Julitta Korol

' Indicates the contents of the underlying cells: text, numbers, formulas.

'
Selection.SpecialCells(xlCellTypeConstants, 2).Select

With Selection.Font
.Name = "Arial"
.FontStyle = "Bold"
.Size = 10
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ColorIndex = 13
End With
Range("B6").Select
Selection.SpecialCells(xlCellTypeConstants, 1).Select

With Selection.Font
.Name = "Arial"
.FontStyle = "Regular"
.Size = 10
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ColorIndex = 11
End With
Range("C6").Select
Selection.SpecialCells(xlCellTypeFormulas, 23).Select

With Selection.Font
.Name = "Arial"
.FontStyle = "Bold"
.Size = 10
20
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ColorIndex = 3
End With
Range("A1:A3").Select
Selection.EntireRow.Insert
Range("A1").Select
With Selection.Interior
.ColorIndex = 13
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
End With
Range("B1").Select
ActiveCell.FormulaR1C1 = "Text"

Range("A2").Select
With Selection.Interior
.ColorIndex = 5
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
End With
Range("B2").Select
ActiveCell.FormulaR1C1 = "Numbers"
Range("A3").Select
With Selection.Interior
.ColorIndex = 3
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
End With
Range("B3").Select
ActiveCell.FormulaR1C1 = "Formulas"

Range("B4").Select
End Sub
從現在開始,讓我們注重於尋找下面兩個問題的答案:如何閱讀宏代碼?如何修改宏代碼?
7 添加注釋
看一下錄製的宏代碼,請注意那些開頭帶單引號的行。這些行就是注釋。注釋預設顯示為綠色。執
行宏代碼時,VB 會忽略這些注釋行。注釋經常和宏代碼放在一起,來整理那些意義不甚明顯的語句。
現在,我們來給宏 WhatsInACell 添加注釋。
1. 啟動 VBE 窗口
2. 在 Selection.SpecialCells(xlCellTypeConstants, 2).Select 前麵點擊一下,將游標移至該
語句開頭,回車
3. 將游標往上移一行到空白處,並且添加如下注釋,注意前面有單引號(譯者:英文狀態下的
單引號)
‘ Find and format cells containing text
4. 在 Selection.SpecialCells(xlCellTypeConstants, 1).Select 前麵點擊一下,將游標移至該
21
語句開頭,回車
5. 將游標往上移一行到空白處,並且添加如下注釋,注意前面有單引號(譯者:英文狀態下的
單引號)
‘ Find and format cells containing numbers
6. 在 Selection.SpecialCells(xlCellTypeFormulas, 23).Select 前麵點擊一下,將游標移至該
語句開頭,回車
7. 將游標往上移一行到空白處,並且添加如下注釋,注意前面有單引號(譯者:英文狀態下的
單引號)
‘ Find and format cells containing formulas
8. 在 Range("A1:A3").Select 前麵點擊一下,將游標移至該語句開頭,回車
技巧 1-6:關於注釋
在 VBE 代碼視窗裡,以單引號開頭的都是注釋。注釋的默認顏色是綠色。可以通過“選項”對話方塊
(“工具”-“選項”-“編輯器格式”)更改注釋顏色。注釋也可以寫在代碼的後面。例如,在語
句.ColorIndex = 11 之後添加注釋。點擊該語句句末,按下 Tab 鍵,輸入單引號,然後輸入注釋。
顯示如下:
.ColorIndex = 11 ' Sets the font color to Violet

注釋除了給使用者提供代碼目的資訊之外,沒有任何作用。請別忘記給你的代碼寫上注釋。如果你幾
個月後還要回到你的代碼,那麼注釋將幫你大忙。同樣,注釋可以使別人很快理解你的程式。
9. 將游標往上移一行到空白處,並且添加如下注釋,注意前面有單引號(譯者:英文狀態下的
單引號)
‘Create legend
8 分析宏代碼
所有巨集過程都以關鍵字“Sub”開始,以關鍵字“End Sub”結束。在關鍵字“Sub”之後是宏的真
正的名字,然後緊跟著是一對括弧。在關鍵字 Sub 和 End Sub 之間是那些你每次運行宏代碼時 VB 執
行的語句。VB 從上到下讀取語句,忽略那些句前帶單引號的語句(參見上節關於注釋的內容),讀
到 End Sub 時停止。請注意,錄製的宏代碼裡包含許多停頓(譯者:英文模式下的句號)。每行代碼
中都有停頓,用來連接 VBA 語言中不同的要素。如何閱讀這種語言的用法呢?要從最後一個停頓的
右邊向左讀。看看 WhatsInACell 裡的一些語句:
Range("A1:A3").Select
選擇 A1 到 A3 儲存格
Selection.EntireRow.Insert
往選中的區域中插入行。因為前面你選中的是三個儲存格(譯者:應該說是佔據了三行的儲存格),
VB 將插入三行。
ActiveCell.FormulaR1C1 = "Text"
往目前的儲存格裡輸入“Text”。因為,之前的代碼是 Range("B1").Select,選擇儲存格 B1,B1 是當
前啟動的儲存格,所有 VB 往 B1 儲存格裡面輸入文本。
With Selection.Interior
.ColorIndex = 3
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
End With
這是一段特別的代碼塊,解釋如下:給當前選中的儲存格設置儲存格填充色為紅色( ColorIndex =
3),設置填充模式為實心(xlSolid),並且給目前的儲存格明確為預設的填充模式(xlAutomatic)。
這個代碼塊以關鍵字 With 開始,End With 結束,它將加速巨集代碼的執行。宏代碼知道走捷徑,而不
會每次都重複下面的說明:
Selection.Interior.ColorIndex = 3
Selection.Interior.Pattern = xlSolid

Selection.Interior.PatternColorIndex = xlAutomatic

在關鍵字 With 後面緊跟重複的 Selection.Interior,再以 End With 結尾。


22
9 清除宏代碼
你已經逐行解析了你宏代碼,你會發現 Excel 錄製了許多你並不想要包含進去的資訊。例如,在選
中了文本儲存格後,除了設置字體為粗體和顏色為紫色之外,Excel 還錄製了其它在字體頁的選項
——字體名稱,字體大小,刪除線,上標,下標,陰影和底線。請看下列代碼片斷:
With Selection.Font
.Name = "Arial"
.FontStyle = "Bold"
.Size = 10
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ColorIndex = 13
End With
如果你使用了對話方塊,Excel 總會錄製所有的設定。這些多餘的代碼使得你的宏代碼冗長而難以理
解。因此,你完成錄製巨集後,最好檢查一遍你錄製的代碼並刪除不必要的行。
1. 在下面的代碼中,刪除帶刪除線的行:
With Selection.Font
.Name = "Arial"
.FontStyle = "Bold"
.Size = 10
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ColorIndex = 13
End With
清除後,在關鍵字 With 和 End With 之間只剩下了兩句代碼,這些才是你在錄製巨集的時候真正做
的設置:
With Selection.Font
.FontStyle = "Bold"
.ColorIndex = 13
End With
2. 找到設置數位單元格格式的代碼,依照下面的例子修改代碼:
' Find and format cells containing numbers

With Selection
.SpecialCells(xlCellTypeConstants, 1).Select

.Font.ColorIndex = 11 ' Sets the font color to Violet

End With
Range("C6").Select
3. 找到設置公式單元格格式的代碼,依照下面的例子修改代碼:
' Find and format cells containing formulas

Selection.SpecialCells(xlCellTypeFormulas, 23).Select

With Selection.Font
.FontStyle = "Bold"
.ColorIndex = 3
23
End With
4. 找到下述兩行代碼:
Range("A1:A3").Select
Selection.EntireRow.Insert
5. 用下面的一句代碼取代上面的兩句代碼:
Range("A1:A3").EntireRow.Insert
注意,Excel 使用了 R1C1 形式來設置選中儲存格個公式:
ActiveCell.FormulaR1C1 = "Text"
ActiveCell.FormulaR1C1 = "Numbers"
ActiveCell.FormulaR1C1 = "Formulas"

宏錄製器使用了一次“ActiveCell”和一次“Selection”來選擇目前的儲存格。這兩個詞都稱為屬
性。你將在第二章裡學習屬性。當僅有一個儲存格被選中時,你可以隨意使用“ActiveCell”或者
“Selection”。
10 測試修改好的宏
當你修改宏的時候,很可能會帶入一些錯誤。例如,你可能會刪除一行重要的代碼,或者可能不注
意清除或忽略了一個逗點(停頓)。為了確保你的宏在你修改之後還能正確地工作,你必須運行它。
在 VBE 視窗,將游標放在宏代碼 WhatsInACell 的任意行,選擇“運行”-“運行模組/表單”
如果你在修改代碼的時候,沒有帶入任何問題,那麼宏將順利運行,而不會有任何報錯。你需要切
換到 Excel 介面取看你的巨集運行的結果。你可以點擊工作列,或者按 Alt+F11 回到 Excel 介面。
如果巨集在運行的過程中遇到錯誤,你將會看到一個對話方塊顯示發現的錯誤類型。在你運行巨集命令之
前,你必須確保你的宏可以在當前啟動的工作表裡面運行。例如,你當前電腦上啟動的時一個空的
Excel 工作表,你試圖運行 WhatsInACell,這時你將看到一個錯誤資訊:“執行時間錯誤‘1004’-
找不到儲存格”。點擊“結束”按鈕,在你重新運行宏之前,確保選擇了正確的工作表。
如果選擇的工作表只含有文本,你在運行 WhatsInACell 的時候,VB 試圖選擇含有數字的儲存格時會
遇到同樣的“找不到儲存格”的錯誤。
如果你忽略了 With Selection.Font 中的逗點,VB 會出現“執行時間錯誤‘424’-需要物件”的信
息。點擊資訊框上的“調試”按鈕,你將進到代碼視窗。同時,VB 會進入“中斷”模式,並且將有
問題的行用黃色突出出來。在你更正代碼後,VB 可能會彈出資訊:“這個操作將會重置你的工程,
繼續?”點擊確定。儘管你可以在中斷模式修改代碼,但是,有些修改會終止宏的繼續執行。更正
宏代碼後,重新運行它,也許你會需要解決更多的錯誤,之後才能順利地運行它。你將在第二章和
第十三章裡面學到更多的關於如何處理 VBA 錯誤的方法。
11 兩個層面運行宏的方法
你既可以在 Excel 介面運行巨集, 也可以在 VB 編輯器介面運行它。當你從 VB
編輯器螢幕執行
WhatsInACell 時,VB 在螢幕之後執行這些代碼。你看不到 VB 選擇和設置格式,也看不到 VB 插入三空
行做圖例。為了觀察到 VB 的執行情況,你必須在 Excel 介面,通過選擇“工具”-“宏”,或者將你
Excel 介面和 VB 編輯器介面同時顯示在電腦螢幕上(參見圖 1-6)
24
圖 1-6 如果你從 VB 編輯器運行巨集時,想觀察巨集的運行情況,你必須將 Excel 介面和 VB 編輯器並排地
佈置在一起
按照下列步驟來並排佈置你的 Excel 介面和 VB 編輯器介面:
1. 在工作列上的空白處按一下右鍵。工作列在螢幕的下端,“開始”按鈕的位置。
2. 下列功能表中,選擇“縱向平鋪視窗”
3. 最小化那些不需要的視窗,重複步驟 1
4. 現在,兩個視窗並排顯示了,點擊代碼的任意位置,然後按下“F5”(或者選擇“運行”-“運
行模組/表單”)。坐好,觀察你錄製的宏在運行,不是很激動嗎?稍後,你將學習如何將 VB 慢
慢運行,這樣你就可以一步一步地觀察巨集代碼的運行情況。
12 完善你的宏代碼
錄製巨集後,你可能會發現宏可以進行一些別的操作。如果你已經熟悉了 VB 語言,要往巨集裡面加指令
並不是一件困難的事情。然而,在絕大多數情況下,你可以將這些工作交給 Excel 宏錄製器,從而
更有效地完成這項工作。你也許會說,Excel 錄製了太多的不需要的指令。但是,肯定的是,宏錄
制器不會犯錯,你完全可以依賴於它。
如果你想要通過宏錄製器在你的代碼裡添加指令,那麼你必須錄製一個新巨集,然後複製需要的部分
再粘貼到原來代碼的正確位置。
我們來給 A1:B3 添加粗邊框:
1. 啟動圖 1-6 看到的 Excel 介面
2. 選擇“工具”-“宏”-“錄製新宏”
3. 在宏對話方塊點擊確定,接受預設的宏名並開始錄製
4. 選擇區域 A1:B3
5. 選擇“格式”-“儲存格”,點擊“邊框”頁
6. 在“邊框樣式”部分,點擊“外部”按鈕
7. 在邊框粗細列表,點擊最粗的,再點擊確定關閉對話方塊
25
8. 點擊儲存格 A1。注意,A1:B3 區域有了粗邊框。
9. 點擊“停止錄製”按鈕,或者選擇“工具”-“宏”-“停止錄製”
切換置 VB 編輯器視窗,查看你錄製的宏。給 A1:B3(譯者:原文為 A1:A3)添加粗邊框的代碼如下:
Sub Macro2()
'
' Macro2 Macro
' Macro recorded 5/31/2002 by Julitta Korol

'
'
Range("A1:B3").Select
Selection.Borders(xlDiagonalDown).LineStyle = xlNone

Selection.Borders(xlDiagonalUp).LineStyle = xlNone

With Selection.Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.Weight = xlThick
.ColorIndex = xlAutomatic
End With
With Selection.Borders(xlEdgeTop)
.LineStyle = xlContinuous
.Weight = xlThick
.ColorIndex = xlAutomatic
End With
With Selection.Borders(xlEdgeBottom)

.LineStyle = xlContinuous
.Weight = xlThick
.ColorIndex = xlAutomatic
End With
With Selection.Borders(xlEdgeRight)

.LineStyle = xlContinuous
.Weight = xlThick
.ColorIndex = xlAutomatic
End With
Selection.Borders(xlInsideVertical).LineStyle = xlNone

Selection.Borders(xlInsideHorizontal).LineStyle = xlNone

Range("A1").Select
End Sub
現在,我們來分析一下這些錄製的代碼。你認為你可以去掉其中的一些指令嗎?在你刪除這些不必
要的代碼之前,考慮使用注釋。在你刪除任何代碼之前,請將它們注釋掉,然後運行宏。如果 VB
沒有出現任何錯誤資訊,那麼你就可以安全地刪除這些被注釋了的代碼。如果每次都按照這個指導
思想,你就不會重複錄製相同的操作了。如果這個巨集命令沒有正確地運行,那麼你需要去掉剛才的
注釋,畢竟,這些代碼可能是必須的。關於注釋的詳細資訊,參見第二章。
當你使用巨集錄製器來創建巨集的時候,你可以很快地掌握 Excel 功能表選項和對話方塊設置在 VBA 裡的等同
方法。然後,你可以在線上說明裡面查找這些 VB 指令的意思和用法。很顯然,VB 要執行越多的指令,
巨集運行的速度就越慢。去掉那些無關緊要的命令會加速巨集的運行。然而,為了使你的代碼容易理解,
你需要戴上你的偵探帽子,尋求最佳途徑。例如,看一下你錄製的給選中的儲存格加外框的宏。看
上去,宏錄製器是在分別地給每一條線進行設置。VB 沒有一個簡單的一句命令來給選中的區域加外
邊框,這似乎很難理解。學習任何語言中正確的詞語和表達是很費時的。時間一長,你會發現 VB
實際上有一個另外的方法 BorderAround 讓你在儲存格區域添加邊框和設置顏色,線型和新邊框的粗
細。下面的語句是 VBA 中給選中的儲存格設置週邊粗邊框的最佳方法:
Range("A1:B3").BorderAround Weight:=xlThick

26
上面的指令使用 Range 物件的 BorderAround 方法。它給 A1:B3 區域添加了一個粗線外框。(下一章涵
蓋了 VB 物件,屬性和方法)。
現在我們將上面的指令加到巨集 WhatsInACell 裡面去:
1. 啟動含有巨集 WhatsInACell 的代碼視窗
2. 在 ActiveCell.FormulaR1C1 = "Formulas"之後插入一行
3. 在空白行加入以下指令:
Range("A1:B3").BorderAround Weight:=xlThick 4

4. 游標放在宏代碼的任何位置,按 F5 運行修改好的代碼。
技巧 1-7:附加指令
要在現存的代碼中添加指令的話,通過在需要的位置按回車鍵加入空白行,並且輸入必要的 VB 語句。
如果附加指令是鍵盤操作或功能表命令的話,你可以使用宏錄製器來創建必要的代碼,然後將它們複
制粘貼到原來的宏裡面。
假設你想要 VB 在執行完最後一行代碼時給你提示,這種操作是不可能被錄製下來的,因為 Excel 沒
有相應的功能表選項。但是,你可以手動使用 VB 語言在你的代碼裡面添加指令。
1. 在代碼視窗下,在 End Sub 前回車
2. 游標放在空白行,輸入下列語句:
MsgBox "所有操作都已完成。"(譯者:英文狀態下的引號。)
3. 確保游標在代碼裡,按下 F5
4. VB 執行完最後一個指令後,彈出這個資訊。點擊確定。你現在知道宏已經運行完成。
MsgBox 是用得非常頻繁的 VBA 函數之一,你將在第四章中學習它的使用。
13 重新命名宏
在代碼裡面添加了一些代碼後,為了更好地反映這個巨集的目的,你需要將其改名。過程的名稱應該
越接近它的功能越好。你不需要按任何鍵就可以更改宏名。在代碼視窗,你將關鍵字 Sub 後面老的
宏名清除,並且打入新的名稱即可。
14 運行宏的其它方法
到現在為止,你已經學習了運行宏的方法。你已經知道通過選擇“工具”-“巨集”-“運行巨集”來
運行宏。不幸的是,如果你需要經常運行巨集,這種方法是不方便的。你也可以在 VB 編輯器視窗使用
快速鍵 F5 或者通過選擇“運行”-“運行模組/表單”來運行巨集。此外,你還可以在 VB 編輯器視窗
點擊標準工具列上的按鈕來運行巨集(見圖 1-7)。
圖 1-7 VB 過程可以通過標準工具列來運行
15 使用鍵盤快速鍵運行巨集
流行的方法是通過設置一個快速鍵來運行宏。按 Ctrl+Shift+D 比從巨集對話方塊啟動巨集要容易得多。你
必須給宏設置一個快速鍵,之後才能使用它。
1. 按 Alt+F8 快速打開宏對話方塊
2. 點擊巨集清單裡的 WhatsInACell,然後選擇選項按鈕
3. 彈出巨集選項對話方塊,如圖 1-8。游標定位在快速鍵文字方塊裡
4. 按下 Shift 鍵和鍵盤是的字母 I。Excel 錄製下了快速鍵 Ctrl+Shift+I
5. 點擊確定以關閉巨集選項對話方塊
6. 點擊取消返回工作表。試試用你剛設置的快速鍵來運行宏,確保啟動了 Excel 視窗,然後按下
Ctrl+Shift+I
27
圖 1-8 使用巨集選項對話方塊設置鍵盤快速鍵來運行巨集
技巧 1-8:避免快速鍵衝突
如果你給宏設置的快速鍵和 Excel 內置的快速鍵衝突,而且你打開的又正是含有那個宏的工作表,
那麼按下該快速鍵後 Excel 會運行你自己的宏。
16 通過功能表運行巨集
如果你寧願通過功能表來運行巨集,那麼你可以將你的宏做成一個功能表選項。使用“自訂功能表”對話
框,你可以快速的將你的巨集命令加入到任何 Excel 的內置菜單中。
1. 在 Excel 介面工具列的空白處,按一下右鍵,選擇“自訂功能表”
2. 在自訂功能表對話方塊選擇“命令”頁
3. 在“類別”清單裡選擇“巨集”
圖 1-9 創建自訂菜單(第一步)
4. 將“自訂功能表”拖曳至工具功能表裡去。當工具功能表展開時,你可以將按鈕放在任意地方。
圖 1-10 顯示了自訂功能表在工具功能表的最下面。
28
圖 1-10 創建自訂功能表(第二步),你可以將自訂功能表放在 Excel 功能表裡,也可以放在子功能表裡
5. 在功能表項目上按一下右鍵,並且在快顯功能表“名稱”的文字方塊裡,將其改成你想要的名字(參見
圖 1-11)。例如,將名稱改為“Contents of Ce&lls”。連接子用以表示鍵盤快速鍵。將連接
符放在你想顯示底線的字元之前。這個自訂功能表將會顯示為“Contents of Cells”,注
意,功能表裡面字與字之間可以有空格。
6. 選擇最後一個選項(快顯功能表上)——“指定宏”(參見圖 1-11)。在巨集對話方塊,選擇巨集
“WhatsInACell”,點擊確定,關閉自訂菜單對話方塊。
現在,你的巨集可以通過自訂功能表來運行了。如果你沒有給自訂功能表選項指定宏就關閉了
這個快顯功能表,Excel 在你第一次試圖使用這個自訂功能表選項時會提示你要宏名。
7. 選擇“工具”-“Contents of Cells”,或者按 Alt+T 和 l 來運行宏。如果你在做上述操作時,
清除了內置功能表或功能表選項,可以打開自訂功能表對話方塊,點擊工具頁,然後選擇“重置”
按鈕就可以恢復了。然而,這樣操作後,會恢復 Excel 默認設置,你的自訂功能表選項也不復
存在了。
29
圖 1-11 創建自訂功能表(第三步)你可以使用快顯功能表給功能表選擇重命名,已經設置你自己的宏。
你必須先打開自訂功能表,才能使用該快顯功能表
17 通過工具列按鈕運行巨集
如果你喜歡使用工具列裡的按鈕,你可以輕易地在任何工具列裡添加按鈕,並且指定你自己的宏。
我們來添加 WhatsInACell 到工具列去。
1. 選擇“工具”-“自訂”
2. 在自訂對話方塊,點擊“命令”頁
3. 在類別清單裡選擇巨集
4. 拖曳“自訂按鈕”圖示到工具列的任何地方。在本例中,這個按鈕放在標準工具條中格式
刷的右邊。
5. 修改按鈕的工具提示:在按鈕上按一下右鍵,然後在出現的快顯功能表的名稱選項中,編輯名稱
文本。本例中,將工具提示改為“Contents of Ce&lls”
6. 修改按鈕圖示:在按鈕上按一下右鍵,並且選擇“修改按鈕圖示”,出現 42 個 Excel 預先設計的
圖示供你選擇。本例中,用鉛筆圖示取代了預設的圖示
7. 給按鈕指定巨集:在按鈕上按一下右鍵,並且選擇“指定宏”
8. 選擇“WhatsInACell”點擊確定
9. 點擊關閉,關閉自訂對話方塊
10. 游標指向你剛才創建的自訂按鈕上,按鈕的旁邊顯示工具提示“Contents of Cells”(參
見圖 1-12)。點擊按鈕運行巨集
30
圖 1-12 你可以在任何工具列添加自訂按鈕來運行巨集
18 通過工作表裡面的按鈕運行巨集
在本書後面,你將學習如何在工作表中添加按鈕,説明 Excel 初學者做資料登錄。現在,我們來過
一遍如何將宏 WhatsInACell 指定在一個工作表的按鈕上。
1. 啟動含有資料的工作表
2. 選擇“視圖”-“工具列”,並且選擇“表單”。表單工具列出現了,如圖 1-13
圖 1-13 你可以將宏指定給一個工作表裡的按鈕
3. 在表單工具列上點擊按鈕
4. 在工作表任意地方點擊一下
5. 當出現指定巨集對話方塊時,選擇巨集名(WhatsInACell)然後點擊確定
6. 改變按鈕 1 的名稱:確保選中了按鈕,並且輸入名稱“Contents of Cells”。按鈕被選中後,
它就像圖 1-13 裡顯示的一樣。如果選擇的符號沒有顯示,在按鈕上按一下右鍵,並且在快捷菜
單上選擇“編輯文本”,選擇預設的文字,然後輸入新的名稱
7. 按鈕重命名後,在工作表按鈕之外的任何地方點擊一下退出按鈕編輯狀態
8. 點擊你剛才創建的按鈕,運行巨集
31
圖 1-14 控制項工具箱的預設工具
技巧 1-9 往工作表裡添加控制項
你可以使用表單工具列往工作表裡添加控制項(參見圖 1-13),也可以使用控制項工具箱(參見圖 1-
14)。兩種工具列都可以通過視圖選擇工具列選項來獲得。
表單裡的控制項和 Excel 的早期版本(5.0,7.0 和 97)相容,並且可以用在圖表,老的 XLM 巨集表和所有
你想通過點擊控制項來運行巨集的工作表裡。
控制項工具箱裡的控制項就是人們熟知的 ActiveX 控制項。你可以將 ActiveX 控制項放在工作表或者你用 VB
編輯器創建的表單上。然而,表單工具列上的控制項只對點擊(Click)事件反應,ActiveX 控制項則有
許多行為,或者說事件,發生於你使用它的時候。
當你使用表單控制項時,你給它指定宏。這個巨集時儲存在本工作表,新工作表或者個人宏工作簿的一
個模組裡。當你使用 ActiveX 控制項時,書寫的巨集代碼時儲存在控制項本身的。
19 保存宏
在這章中,你創建的宏 WhatsInACell 位於一個 Excel 工作表中。你需要保存這個開啟了的工作表來
保存這個宏。我建議你將其保存為 Chap01.xls。保存後,關閉它,然後打開一個新工作表。注意,
你工具列上的自訂按鈕還在那兒,正如工具功能表裡的 Contents of Cells 樣還在那兒一。在你使
用這些工具運行宏之前,請在儲存格 A1 裡輸入“Addition”,A2 裡輸入數位 2,A3 裡輸入數位 4,已
經 A4 裡輸入“=SUM(A2:A3)”。當你運行這個宏時,Excel 會打開適當的工作表並且執行這個指定給
自訂工具的過程。
20 列印宏
如果你要將你的宏歸檔起來,或者在你離開電腦的時候研究巨集代碼,你就需要列印巨集。你可以列印
你儲存巨集的整個模組,也可以列印選擇的行。
列印含有巨集的整個模組:
1. 將游標放在模組的任意地方
2. 選擇“檔”-“列印”
3. 在列印-VBA 對話方塊,選擇“當前模組”
4. 點擊確定列印模組
列印選中的文本:
1. 在模組裡,選擇你要列印的文本
2. 選擇“檔”-“列印”
3. 在列印-VBA 對話方塊,選擇“選擇”
4. 點擊確定列印選中的文本
21 保存巨集在個人巨集工作簿
當你錄製巨集時,可以將它保存在個人宏工作簿裡面。當你儲存巨集在個人巨集工作簿裡時,Excel 創建
一個名為“Personal.xls”的檔並且放在“Program Files\Microsoft Office\Office”的子文
件夾——XLStart 資料夾裡。保存在 XLStart 資料夾的檔每次在 Excel 啟動的時候都會自動打開。
32
個人巨集工作簿是一個保存通用宏代碼的方便的地方,就像下面這個宏。現在來錄製一個通用的宏
“FormulasOnOff”。這個巨集的目的是設置是否顯示工作簿的公式。
1. 選擇“工具”-“宏”-“錄製新宏”
2. 在錄製巨集對話方塊,輸入宏名“FormulasOnOff”
3. 在保存巨集的下拉式功能表裡選擇“個人宏工作簿”
4. 點擊快速鍵文字方塊,並且按下“Shift+F”
5. 選擇確定退出錄製巨集對話方塊
6. 按下“Ctrl+~”打開公式的顯示,或者選擇“工具”-“選項”並且點擊“視圖”頁上“窗
口選項”中的“公式”檢驗盒。當你打開公式顯示時,工作簿儲存格裡顯示的是公式,而非
這個公式計算出來的數值。如果你是在一個空白工作表中錄製這個宏的,那麼你將注意到的
唯一變化是工作表的列寬。
7. 點擊“停止錄製”,或者選擇“工具”-“宏”-“停止錄製”
8. 查看代碼:按下 Alt+F11,或者選擇“工具”-“宏”-“VB 編輯器”
這時,VB 編輯器螢幕上的工程視窗裡顯示了一個多出來的 VBA 工程(Personal.xls)。點擊這個過程
名左邊的加號來打開這個工程。這個 VBA 工程包含兩個資料夾:Excel 物件和模組。點擊模組資料夾
的加號來打開它,然後按兩下模組 1。這時代碼視窗顯示了巨集 FormulasOnOff 的內容(參見圖 1-15)。
每個 Excel 工作表只有一個工程。你第一次錄製巨集的時候,Excel 創建一個模組資料夾,並且將你的
代碼儲存在模組 1 裡面。如果你在相同的工作表裡錄製另一個宏,Excel 將其放在前一個錄製的宏的
同一個模組 1 的下麵。相同工作時間錄製的所有的宏都儲存在相同的模組裡面。但是,如果你關閉
Excel,然後再重新這個工作簿,Excel 就會將它儲存在一個新的模組。
圖 1-15 在工程流覽器視窗,你可以選擇你需要的工程
錄製巨集的時候,你打開了公式的顯示。這個巨集的名稱表明可以切換公式顯示的開和關。你必須修改
代碼才能確保它按照這種方式運行。
33
錄製的巨集設置當前視窗顯示公式為真:
ActiveWindow.DisplayFormulas = True
設置為“False”將關閉公式的顯示:
ActiveWindow.DisplayFormulas = False

為了在 VBA 裡設置轉換,你需要按照下面的方法來連接兩語句:


ActiveWindow.DisplayFormulas = Not ActiveWindow.DisplayFormulas

用上面的語句代替你錄製的代碼,並且運行這個宏。無論你運行多少次,這個宏總是知道做什麼。
你可以使用相同的思路來創建代碼以切換格式線或其它 Excel 特點的顯示與否。當你關閉 Excel 時,
它會提示你保存個人宏工作簿的變化,點擊確定以保存變化。當你重啟 Excel,個人宏工作簿會在
後臺自動開啟。
如果你想要在個人宏工作簿裡保存其它的宏,你可以選擇下列方法中的一個:
錄製一個新巨集,並且選擇個人巨集工作簿來儲存
切換到 VB 編輯器,打開你要移動到個人宏工作簿裡去的巨集,剪切這個巨集,並且打開個人巨集工作
簿。將巨集粘貼到已經存在的模組中,或者創建一個新模組再粘貼
選擇“檔”-“導入檔”……從另外一個 VB 工程(*.frm,*.bas,*.cls)導入宏代碼
22 打開含有宏的工作簿
無論何時你打開一個含有宏的工作簿,Excel 顯示一個警告資訊,如圖 1-16。為了避免顯示這個警
告資訊,你可以通過安全對話方塊關閉病毒保護(參見圖 1-17)。
當病毒資訊出現時,你可以選擇:
取消宏——當你打開一個來源不熟悉的含有宏的工作簿,例如網際網路,電子郵件,為了保護你
的電腦不被巨集病毒破壞,你應該選擇“取消宏”。工作簿打開時不會運行它裡面的任何宏。如果
沒有密碼保護的話,你就可以切換到 VB 編輯視窗查看代碼。查看代碼後(譯者:如果代碼安全),
你可以關閉該工作簿,然後重新打開它並且啟用宏。
圖 1-16 如果你打開了病毒保護,當工作簿含有宏時,Excel 會彈出一個警告資訊
啟用宏——你如果指定這個工作簿來自於一個可靠的來源,也含有有用的巨集,點擊啟用巨集按鈕。
更多資訊——在你決定取消或者啟用巨集時,如果你需要瞭解更多的資訊,那麼點擊這個按鈕。
Excel 2002 有一個有用的功能讓你自動取消所有沒有簽名並且來源不明的宏。選擇“工具”-
“宏”-“安全”進入這個功能。
當你創建一個需要給別人使用的巨集時,你可以使用 VB 編輯器工具功能表裡的數位簽章來確認這個宏不
會帶來病毒。宏的數位簽章正如在紙上的簽名。請在 Excel 線上說明裡搜索如何安裝和創建你自己
的數位簽章。輸入“數位簽章”就可以獲得相關主題。
34
圖-17 選擇中間的選項,讓你根據工作簿決定是否取消或者啟用宏
23VB 編輯視窗
現在,你已經知道如何錄製,運行和修改宏了,讓我們花些時間來熟悉 VB 編輯器的一些特點。使用
VB 編輯器上的工具,你能夠:
編寫你自己的宏過程
創建自訂表單
查看和修改物件屬性
測試 VBA 過程和定位錯誤
有兩種方法進入 VB 編輯器:
從 Excel 介面的工具功能表:選擇“工具”-“宏”-“巨集編輯器”
從鍵盤:按下 Alt+F11
(譯者:在工作表標籤上按一下右鍵,然後選擇查看代碼)
24 瞭解工程流覽視窗
工程視窗顯示當前打開的工程和它的組成部分清單。VBA 工程包括下列組成:
工作表
圖表
當前工作簿——工程儲存的工作簿
模組
類別模組——特殊的模組讓你可以創建自己的物件
表單
引用到其它工程
通過工程流覽器,你可以管理你的工程,容易地在當前打開的工程中切換。
你可以通過三種途徑啟動工程流覽器:
通過視圖功能表,選擇工程流覽器
通過鍵盤,按下 Ctrl+R
通過工具列,點擊工程流覽器按鈕(參見圖 1-18)
工程流覽器有三個按鈕。左邊第一個按鈕(查看代碼)顯示當前選中的模組(譯者:或者表單)裡
的代碼視窗。中間那個按鈕(查看物件)顯示 Excel 介面當前工作表,或者表單資料夾裡面的表單。
右邊的按鈕(切換資料夾)隱藏或者顯示工程流覽器裡的資料夾。
35
圖-18 標準工具列上的按鈕提供了快速的方式進入許多 VB 特徵
25 瞭解屬性視窗
屬性視窗讓你查看你工程裡的物件和設置它們的屬性。當前選中的物件的名稱就顯示在屬性視窗的
標題列下麵的對象欄。物件的屬性可以按照字母順序查看,也可以按類別查看(參見圖 1-19)。
圖 1-19 屬性視窗顯示的是當前被選中的物件的屬性設置
字母順序——按字母順序地列出被選擇的物件的所有屬性。通過選擇屬性名,並且輸入或者選
擇新的設置,來更改屬性設置。
類別——按類別列出被選中的物件的所有屬性。你可以將清單折疊起來,查看類別,你也可以
展開類別查看屬性。類別名稱左邊的加號(+)說明這個類別可以展開。減號(-)說明這個
類別已經展開。
有三種方式可以進入屬性視窗:
視圖功能表,選擇屬性視窗
從鍵盤,按下 F4
從工具列,點擊屬性視窗按鈕(參見圖 1-18)
26 瞭解代碼視窗
代碼視窗是用來 VB 程式設計的,也是用來查看,修改錄製的宏代碼和現存的 VBA 工程的。每個模組會以
一個專門的視窗打開。有好幾個方法可以啟動代碼視窗:
從工程流覽器視窗,選擇你要的使用者表單或者模組,然後點擊查看代碼按鈕
從功能表,選擇“視圖”-“代碼”
36
從鍵盤,按下 F7
在代碼視窗的上面,有兩個下拉清單清單(參見圖-20),方面你快速地移動到任意代碼處。在代
碼視窗左上角的物件清單方塊,你可以選擇你想查看代碼的物件。你可以在代碼視窗右上角的清單方塊
裡選擇一個過程或者事件程序查看代碼。當你打開這個清單方塊,這個模組裡的所有過程名按字母順
序排列在那兒。如果你選擇了一個過程,游標就會跳到那個過程的第一行處。
將列分工具條(參見圖 1-20)拖曳下列,你就可以將代碼視窗分為兩半了(參見圖 1-21)。
圖 1-20 代碼視窗有幾個部分,使得定位過程和查看代碼變得很輕鬆
你可以查看不同的代碼部分了。這樣設置代碼視窗,目的是方便在同一個模組內的過程裡複製或剪
切,並且粘貼代碼片斷。只要簡單地將列分工具條拖曳至代碼視窗上面就行。代碼視窗的底部有兩
個圖示。點擊“過程查看”圖示,代碼視窗裡一次只顯示一個過程,可以通過過程/事件清單方塊選
擇另外的過程。點擊“全部模組查看”則可以顯示這個模組裡的所有過程。使用豎向捲軸可以在
代碼中滾動。
頁邊指示工具條是在修改代碼和調試是提供一些幫助指示的。如果你想快速查看關於指示的資訊,
請看第十三章。
37
圖 1-21 你可以將代碼視窗列分為兩個視窗來查看長過程
27 VB 編輯器裡的其它視窗
除了代碼視窗,VB 環境下還有很多其它視窗頻繁地被使用:
表單視窗用來創建自訂對話方塊和使用者表單。
圖 1-22 在選項裡,你可以選擇顯示哪些視窗
你將在第十章裡學習到更多的這類知識。
圖 1-22 顯示了一些可以顯示了 VB 編輯器裡的視窗清單。你將在第二章裡(物件流覽器,立即視窗)
38
和在第三章裡(當地視窗,觀察視窗)學習如何使用這些視窗。
28 接下來……
通過一些試算表自動化的例子,你不但已經如何錄製巨集,而且也已經學習了如何查看,閱讀和修
改 VB 代碼。還有,你也嘗試了用許多方法運行。最後,你還快速過了一遍 VB 編輯器視窗。
在下一章,我們將會給你介紹 VBA 基礎,你將學習許多新術語,更重要的是,你將獲得有用的 VBA
詞彙表,這將幫你將更多的任務交給 Excel 幫你完成。
第二章 VBA 第一步
作者:Julitta Korol 翻譯:Tiger Chen Nov 28’ 2004
語言學習是一個長期的活動,在此過程中,你會經歷不同的階段,由生疏到熟悉。學習 VBA 程式設計也
是一樣的,沒有捷徑。要想精通 VBA,你必須從一個初級階段起步(第二至四章)。只有當你對 VBA
後面的一些基本的東西有了很好的理解之後,才能提高到中級階段(第五至七章)和高級階段(第
八至十七章)。但是,重要的事情先來。在你能夠用 VBA 自由自在地控制 Excel 之前,你需要獲得一
些你的術語和語法。在 VB 裡,如何表達這些?“在工作簿裡添加一個新工作表”,“刪除儲存格 A5
的內容”,“將儲存格 A1 的公式複製到儲存格 B1”?你也許知道這些單個的詞語,然而,你怎麼知道
如何將它們正確地組合在一起,Excel 才能執行這些任務?你將在本章中學習 VBA 的術語和規則。
1 瞭解指令,模組和過程
在第一章,你學習到了 Excel 宏錄製器創建的一系列指令是和你實際進行的操作完全等同的。這些
指令自動地放在工作簿裡一個叫做“模組”的表裡。Excel 將模組儲存在模組資料夾裡,這個檔
夾在當前工作簿,新工作簿或者個人宏工作簿裡面。你必須啟動 VB 編輯器視窗,並且按兩下工程流覽
器裡的模組資料夾才能查看到這些模組。當模組表在代碼視窗裡打開了後,你才能最後分析你的過
程代碼。
所有錄製的指令都包括在“過程”裡面。過程裡面的每一行都是一個“指令”。指令的類型有很多
中,例如,關鍵字,運算子,或者其它過程的調用。“關鍵字”代表 VB 中的一個特殊的意義。在第
一章中,你學習了最常見的 VBA 關鍵字——Sub 和 End Sub,它們表示一個過程的開始和結束。關鍵
詞預設地顯示為藍色。你不要將這些術語做其它的目的,因為關鍵字已經被 VB 保護了。
除了關鍵字,VB 指令裡還可以有運算子。運算子有四種類型:算數運算,字元串連接,邏輯運算和
比較運算。“運算子”允許你將某些值結合起來。例如,減號運算子(/)可以用來計算總數的百分
比。本書中,你有很多機會看到如何在 VBA 過程中使用運算子。
VB 指令的另外一種類型是程序呼叫。程序呼叫讓你快速地跳到其它過程並且執行其它指令。是不是
很難想像?讓我們看一下你在第一章中錄製的宏 WhatsInACell。假設你也需要包含同一模組中巨集
FormulasOnOff 中的一些語句。怎麼做呢?你可以複製需要的代碼行,再粘貼過去。然而,有一種
更簡單快速的方法。你可以調用這個過程,而不需要在兩個過程中複製。例如,你想 VB 在遇到指令
MsgBox "所有操作都已完成"之前執行巨集 FormulasOnOff 裡面的指令,只要添加下面一句代碼就行:
FormulasOnOff
當 VB 到達這一行,它就會立即跳到 FormulasOnOff 過程並且執行它的代碼。之後,它會回到宏
WhatsInACell 去執行剩下的代碼,遇到關鍵字 End Sub 時則停止。
在你嘗試這個例子之前,你必須學會如何給 VBA 過程和模組命名,已經如何調用不同工程裡的過程。
2 VBA 工程命名
工程是一套 Excel 物件,模組,表單和引用。除了 VBAProject 這個位於工程流覽器中工作簿名稱之
前預設名稱,每個工程需要一個獨特的名稱。我們來給 VBAProject (Chap01.xls) 和 VBAProject
(Personal.xls)命名:
1. 啟動 Excel,打開 Chap01.xls,這裡儲存了巨集 WhatsInACell 代碼。你錄製了宏 FormulasOnOff 的
個人宏工作簿會自動開啟
2. 切換到 VB 編輯器視窗
3. 選擇 VBAProject (Chap01.xls)
4. 按兩下屬性視窗裡的名稱屬性,這個操作選中了預設的工程名稱 VBAProject
5. 輸入“FirstSteps”作為該 VBA 工程的名稱,回車。注意,工程流覽器現在顯示的是名稱是
39
FirstSteps (Chap01.xls)
6. 在工程流覽視窗選擇 VBAProject (Personal.xls)
7. 按兩下屬性裡的名稱屬性
8. 輸入“Personal”作為它的名稱,回車
技巧 2-1 避免名稱衝突
為了避免 VBA 工程之間的命名衝突,請給你的工程獨特的名稱。你可以使用下述方法之一來更改工
程名稱:
在工程流覽器視窗,選擇工程名稱,按兩下屬性視窗裡的名稱屬性,再輸入新的名稱
在工程流覽器視窗,在工程名稱上按一下右鍵,並且選擇“工程名稱屬性”。出現如圖 2-1 顯示的
工程屬性對話方塊,在工程名稱文字方塊裡面輸入新的名稱
圖 2-1 工程屬性視窗可以用來更改當前被選中的工程名稱和描述
3 模組重命名
當你錄製巨集或者創建新的過程時,VB 會創建一個模組資料夾來儲存你的 VBA 代碼。第一個資料夾
叫“模組 1”,第二個叫“模組 2”,等等。你打開一個新的工作簿並且創建 VBA 工程時,新 VBA 工程裡
的模組資料夾又會命名為“模組 1”,“模組 2”,等等。模組擁有相同的名稱不但對你,而且對 VB 造
成很大混淆,因為,它要在一個打開許多工程的環境中執行你的巨集或工程。
為了避免模組混淆,給 FirstSteps (Chap01.xls) 工程和 Personal (Personal.xls) 工程裡的“模
塊 1”重新命名:
1. 在工程流覽器視窗,選擇 FirstSteps (Chap01.xls)工程,並且選擇“模組 1”
2. 按兩下屬性視窗裡的名稱屬性,這個動作選中了模組的預設名稱“模組 1”
3. 輸入“WorksheetFormatting”作為模組 1 的名稱,並且回車。注意,工程流覽器視窗現在顯示
的模組名稱是“WorksheetFormatting”
4. 在工程流覽器視窗選擇 Personal (Personal.xls)
5. 按兩下屬性視窗裡的名稱屬性
6. 輸入“Switches”作為模組 1 的名稱,回車
40
圖 2-2 工程流覽器視窗顯示通過屬性視窗給工程和模組設置的獨特名稱
4 從其它工程調用過程
你只要明確過程名稱,就可以調用這個在同一個工程裡任何模組裡的過程。假設過程 FormulasOnOff
和巨集 WhatsInACell 在同一個工程裡的不同模組(或者同一個模組),在 WhatsInACell 巨集裡面調用過
程 FormulasOnOff,你所要做的所有工作只是明確過程名稱,示例如下:
Sub WhatsInACell()
<這裡是你錄製的指令>
FormulasOnOff
End Sub
然而,如果兩個或者兩個以上的模組含有這個相同的過程名稱,你除了要明確過程名稱外,還必須
包括模組名稱。假設工程 FirstSteps (Chap01.xls)有三個模組。模組 FormulaFormatting 包含巨集
WhatsInACell,但是,模組 Switches 和模組 Formulas 都含有一個叫 FormulasOnOff 的宏。如何在
WhatsInACell 調用 FormulasOnOff(模組 Switches 裡面的)?請看下面例子:
Sub WhatsInACell()
<這裡是你錄製的指令>
Switches.FormulasOnOff
End Sub
要調用其它工程裡的過程,你必須建立對該工程的引用。你可以在“引用”對話方塊進行這些操作。
因為 FormulasOnOff 在 Personal (Personal.xls)工程裡,在你能夠從 WhatsInACell 調用它之前,你
需要添加對“Personal”的引用。下面是幾種建立引用的方法:
1. 在工程流覽器視窗,點擊 FirstSteps (Chap01.xls)
2. 選擇“工具”-“引用”
3. 在引用對話方塊,選中“Personal”旁邊的勾選框(參見圖 2-3),然後點擊確定(譯者:在截圖
前,我並沒有保存 Personal,所以在附圖裡沒有 Personal 一行,如果你依照書中一步一步走下
來,應該沒有問題)
41
圖 2-3 引用對話方塊列出了所有這個工程可以引用的工程。如果你想要執行其它工程裡的過程,你
就必須建立對這個工程的引用
既然對“Personal”工程的引用已經建立了,我們就來從 WhatsInACell 裡調用 FormulasOnOff 吧。
1. 在工程流覽器視窗,選擇 FirstSteps (Chap01.xls)並且定位到含有 WhatsInACell 的模組
2. 在 MsgBox "所有操作都已完成"之前增加一空白行,並且輸入代碼:FormulasOnOff
3. 返回到 Excel 介面,確保當前工作表是這個例子資料(參見第一章的圖 1-1)
4. 使用任何你在第一章裡學到的方法來運行宏 WhatsInACell
如果你給兩個不同工程裡的不同過程以相同的名稱,那麼你必須明確工程名稱才能調用它。
我們假設 FirstSteps (Chap01.xls)工程和 Personal (Personal.xls) 工程裡都有叫 FormulasOnOff
的宏,要調用 Personal (Personal.xls)工程裡的 FormulasOnOff(記住,你已經必須先建立對
Personal 的引用),必須包括工程名稱:
Sub WhatsInACell ( )
<這裡是你錄製的指令>
Personal.Switches.FormulasOnOff
End Sub
技巧 2-2 VB 如何定位被調用的過程
當你調用一個過程,VB 先在主調方(WhatsInACell)的同一個模組裡查找。如果沒有找到被調過程
(FormulasOnOff),VB 就會在同一個工程的其它模組裡查找。如果還是找不到,VB 則會檢查對其它
工程的引用。
技巧 2-3 工程名稱不在引用對話方塊
如果你想要調用一個當前關閉的工程裡的過程,當你打開引用對話方塊試圖建立引用時,這個過程名
稱不在清單中。點擊“流覽”,並且打開被調用過程所在的資料夾。添加引用的對話方塊默認地列出
資料庫檔(*.olb, .tlb, .dll)。從檔案類型的下拉清單中選擇 Excel 檔(*.xls, *.xla),選
擇並打開含有你要調用過程的檔。這個工程的名稱將會加在引用對話方塊的最後一行。
5 瞭解物件,屬性和方法
使用 VBA,你可以創建工程控制 Excel 的許多東西,你同樣也可以控制很多其它的應用程式。VB 的偉
大來自於它的控制和管理各種各樣的物件的能力。但是,“物件”是什麼呢?“對象”是你通過 VBA
控制的東西。工作簿,工作表,工作表裡的儲存格區域,圖表或者工具條,這些只是一些用 Excel
時想要控制的東西的舉例。這些東西就是物件。Excel 含有超出一百種你可以通過不同方式操作的
對象。所有的 VB 物件都被分層歸類。一些物件本身又可能含有其它的物件,例如,Excel 時一個應
用物件,這個應用物件包含其它物件,例如工作簿或者命令條。工作簿物件可能包含其它物件,如
42
工作表或者圖表。你將在本章種學習如何控制以下 Excel 物件:區域,視窗,工作表,工作簿和應
用。我將“區域”列在了第一位置,有一個非常重要的原因,如果你不知道如何操作儲存格區域的
話,你基本上不能用試算表來做什麼。
某些物件看上去相似。如果你打開一個新工作簿,檢查它的工作表,你不會發現什麼不同。一組相
似的物件被稱為“集合”。例如,工作表的集合包含所有具體工作簿中的工作表;命令條的集合包
含所有的工具條和功能表。集合同樣是物件。Excel 中使用得最頻繁的集合是表(Sheets)集合,它
代表所有的工作表和圖表,還有工作簿集合,工作表集合以及視窗集合。當你使用集合時,相同的
動作可以在這個集合中所有的物件上執行。
每一種物件都有一些特徵供你描述。在 VB 裡,這些物件的特徵被稱為“屬性”。例如,工作簿物件
有名稱屬性;區域物件有列,字體,公式,名稱,行,樣式和值等屬性。這些物件屬性是可以設置
的。你通過設置物件的屬性控制物件的外觀和位置。物件屬性一次只能設置為一個特定的值。例如,
當前工作簿不可能同時有兩個不同的名稱。VB 中最難理解的部分是有些屬性同時又可以是物件。想
想區域(Range)物件,你可以通過設置字體顏色來改變選定儲存格的外觀。但是,字體(Font)
可以有不同的名稱(Times New Roman, Arial, …),不同的字型大小(10,12,14,…)和不同的樣
式(粗體,斜體,底線,…)。這些是字體的屬性。如果字體有屬性,那麼字體也是物件。
屬性真是了不起,讓你改變物件的外觀,但是,如何控制這些操作呢?你在使 Excel 為你執行任務
之前,你需要知道另外一個術語。物件有方法。每一種你想要物件做的操作都被稱為“方法”。最
重要的 VB 方法是 Add 方法。你可以使用這個方法添加一個新工作簿或者工作表。物件可以使用不同
的方法。例如,區域(Range)物件有專門的方法讓你清除儲存格內容(ClearContents 方法), 清
除格式(ClearFormats 方法)以及同時清除內容和格式(Clear 方法)。還有讓你選擇,複製或移動
物件的方法。方法有可選擇的參數確定方法執行的具體方式。例如,工作簿(Workbook)物件有一
個叫關閉(Close)的方法。你可以使用它關閉任何打開了的工作簿。如果工作簿有改動,Excel
會彈出一個資訊,問你是否要保存變化。你可以使用關閉方法和設定它的保存變化( SaveChanges)
參數為假(False)來關閉這個工作簿並且不管它的任何變化。正如例子:
Workbooks("Chap01.XLS").Close SaveChanges:=False

6 學習物件,屬性和方法
當你學習新的事物時,理論會給你必須的背景,但是,你如何真正知道那是什麼呢?大多數人習慣
形象思維,為了使 Excel 物件易於理解,VB 線上說明提供了一個物件模型,請看接下來的附圖。注
意,Application 物件位於樹型圖的最上端,它實際上代表 Excel 本身。其它物件在較低的層次。
假設你想要控制 Range 物件,在你能夠控制任何 Excel 物件之前,你必須對它創建引用。為了獲得下
圖中的 Range 物件,只要遵照下面幾行代碼。每次看到樹型圖中的線指向不同的層時,你只要巧妙
地將線換成一個逗點運算子(停頓,英文狀態下的句號)。這樣,最終你會以下面的方式到達 Range
對象:
Application.Workbook.Worksheet.Range

你可以使用 Excel 物件樹型圖來尋找到其它物件的路徑,例如視窗(Window),批註(Comment),自


動篩選(AutoFilter)或者繪圖區(ChartArea)。分析物件模型是一個學習 Excel 物件的非常好的
方法。你花在這裡的時間,以後你開始編寫 VBA 過程的時候,會給你加倍的回報。通常,你需要明
確你引用的對象的名稱。
現在,我們來點更具體的。假設你要清除儲存格 A4 裡的內容。手動做這個時,只要選擇儲存格 A4
然後按下鍵盤上的 Delete 鍵就可以了。用 VB 做同樣的操作,你首先需要知道如何使 Excel 選中了正
確的儲存格。儲存格 A4 和其它的工作表儲存格一樣,是 Range 物件。VB 沒有 Delete 方法來清除單元
格內容,取而代之的是 ClearContents 方法,例如:
Range("A4").ClearContents
注意在物件名稱和方法之間的逗點運算子。這個指令去除儲存格 A4 裡的內容。然而,如何使 Excel
清除工作簿 Chap02.xls 第一個工作表裡儲存格 A4 的內容呢?我們仍然假設打開了好幾個工作簿。
43
圖 2-4 Excel 對象樹型圖(第一頁)(譯者:原書圖 2-5(工作簿物件樹型圖)已包含在內,故在
此略過)
如果你不希望最後在錯誤的工作簿或工作表裡刪除了 A4 裡的內容,那麼你必須寫下詳細的指令,這
樣 VB 就知道在哪裡找這個儲存格:
Application.Workbooks("Chap02.xls").Worksheets("Sheet1") .Range("A4").ClearContents

上面的指令應該寫成一行,並且應該從右到左閱讀:清除儲存格 A4 裡的內容,這個儲存格在一個叫
“Sheet1”的工作表裡,而這個工作表又在一個叫“Chap02.xls”的工作簿裡面,工作簿“Chap02.xls”
又應該是 Excel 應用程式的一部分。注意,集合名稱的後面帶有一個字母“s”:Workbooks 和
Worksheets。所有引用的工作簿,工作表或儲存格名稱都必須用引號(譯者:英文狀態的引號)包
括起來。
如何找到 Excel 物件樹型圖?選擇 Excel 介面上的“説明”- “Excel 説明”-“程式設計資訊”-“微
軟 Excel VB 參考”-“Excel 物件模型”。(譯者:2002 版有全域的物件模型,2003 版好像沒有這個。)
除了 Excel 物件,你還可以使用 Office,Forms 和 DAO,ADO 物件模型。屬於這些資料庫(library)
中的物件都可以用在 Excel 中,也可以用在 Office 家族中的其它應用軟體中。在十五章,你可以看
到使用 DAO 和 ADO 物件,從 Excel 進入 Access 資料庫的例子。
44
圖 2-6 Excel 對象(Worksheet)
技巧 2-4 VBA 和 Excel 的早期版本
Excel 線上說明列出了 Excel 物件模型從早期版本以來的變化。許多物件,屬性和方法都已經被更新
的,改進的特色所代替了。為了提供相容性,被取代的物件已經被隱藏起來了(譯者:它們仍然是
可用的)。打開 VB 編輯器視窗上的線上說明,隱藏的物件同樣可以在物件流覽器裡找到。如果你在
物件流覽器視窗上按一下右鍵,你可以選擇顯示隱藏成員的選項。你將在以後的章節中學習如何使用
對象流覽器。
7 句法和文法
既然現在你已經知道了 VBA 的一些基本組成要素(物件,屬性和方法),是時間開始使用它們了。但
是,你怎麼將物件,屬性和方法連接成正確的語言結構呢?每種語言都有語法規則,人們必須遵循
語法以確保他們被理解了。無論你說的是英語,西班牙語,法語還是其它語言,你在讀,寫的時候
都必須遵從一定的規則。在程式設計中,我們使用“句法”(syntax)這個術語來更確切地明確它的語
言規則。你可以在線上說明或者在物件流覽器視窗查找每個物件,屬性或方法的句法。下面列出一
些你必須的 VB 常用規則:
規則 1:引用物件的屬性
如果這個屬性沒有引數,使用下面的句法:
Object.Property
45
物件是一個預留位置,是你放置你想要進入的實際物件名稱的地方。屬性同樣也是一個預留位置,你可
以在這裡放置該物件的特點。例如:指向工作表中儲存格 A4 中輸入的值,見下述指令:
Range("A4").Value
注意物件名稱和屬性之間的句號。當你需要進入一個存在於多個其它物件裡的物件的屬性時,你必
須按順序地寫上所有物件的名稱,並且用句號運算子分開。例如:
ActiveSheet.Shapes(2).Line.Weight
這個例子指向當前工作表裡圖形(Shapes)集合裡的第二個物件裡的直線(Line)物件的粗細
(Weight)屬性。
有些屬性要求一個或多個引數。例如,使用偏移(Offset)屬性,你可以選擇一個和目前的儲存格
相對位置的儲存格。Offset 屬性需要兩個引數,第一個引數為行號(rowOffset)第二個是列
號(columnOffset)。
物件屬性引數
ActiveCell.Offset(3, 2)
在上面的例子中,假設目前的儲存格是 A1,Offset(3, 2)將會指向往下 3 行和往右兩列的儲存格,也
就是儲存格 C4。因為,在括弧內的引數總是很難理解,通常操作是將它們的名稱也列上,見下例:
ActiveCell.Offset(rowOffset:=3, columnOffset:=2)

注意,引數名稱後面總是跟著一個冒號和一個等於號(:=)。如果你使用帶名稱的引數,你可
以任意順序地列出它們,上面的指令也可以寫成這樣:
ActiveCell.Offset(columnOffset:=2, rowOffset:=3)

改後的指令沒有改變意思,你仍然指向儲存格 C4。然而,如果你改變 ActiveCell.Offset(3, 2)中


引數的次序,結果你會指向 D3,而不是 C4。
規則 2:改變物件的屬性
Object.Property = Value
Value 是一個新的你要賦給該物件屬性的值。這個值可以是:
一個數字
Range("A4").Value = 25
上面的指令在當前工作表的儲存格 A4 裡輸入數字 25
在引號裡的文本
ActiveCell.Font.Name = "Times New Roman"

上面的指令將目前的儲存格字體改為 Times New Roman


邏輯值(True 或 False)
ActiveCell.Font.Bold = True
上面的指令設置目前的儲存格的字體為粗體。
規則 3:返回物件屬性的當前值
Variable = Object.Property
Variable(變數)是 VB 將要儲存屬性設置的地方的名稱,你將在第三章裡學習關於變數的知識。
變數物件屬性
CellValue = Range(“A4”).Value
上面的指令將當前 A4 儲存格裡的值保存到變數 CellValue。
規則 4:指向物件的方法
如果該方法沒有引數,那麼句法應該是:
Object.Method
物件是一個預留位置,是你放置你想要進入的實際物件名稱的地方。方法同樣也是一個預留位置,你可
以在這裡放置要對該物件的進行的操作的名稱。例如,可以使用下述指令來清除儲存格 A4 的格式(譯
者:應該是內容):
物件方法
Range("A4").ClearContents
如果該方法可以使用引數來限制,那麼句法為:
Object.Method (argument1, argument2, … argumentN)
例如,使用 GoTo 方法,你可以快速地選擇工作表裡的任何區域。GoTo 方法的句法為:
46
Object.GoTo(Reference, Scroll) ‘對象.GoTo(參照, 視窗滾動)
Reference 引數是目標儲存格或者區域,Scroll 引數可以設定為真(True)讓 Excel 視窗滾動到
該目標位址出現在視窗的左上角;或者設定為假(False),視窗不滾動(譯者:系統預設為 False)。
例如,下面的 VBA 語句選擇工作表 Sheet1 裡的儲存格 P100,並且視窗滾動:
Application.GoTo _
Reference:=Worksheets("Sheet1").Range("P100"), _

Scroll:=True
上面的指令沒有固定在一行,使用了一條特殊的線(底線)將它分為幾段。下面的部分將講述這
個。
8 打斷很長的 VBA 語句
儘管一行 VBA 代碼最多可以包含 1024 個字母,但是,為了使你個過程容易閱讀,最好將長的語句打
斷為兩行甚至多行。VB 使用一個專門的連續線(底線)置於一行代碼的末尾,表明下一行是這行
的連續。例如:
Selection.PasteSpecial _
Paste:=xlValues, _
Operation:=xlMultiply, _
SkipBlanks: =False, _
Transpose:=False
這個連續符是底線,你必須在底線之後帶一個空格。
你可以在下述幾種情況中使用連續符:
運算子之前或者之後。例如:&,+,Like,NOT,AND
逗號之前或者之後
冒號和等號(:=)之前或者之後
等號之前或者之後
你不可以在冒號和等於號之間使用連續符,例如,下面的代碼 VB 是不認的:
Selection.PasteSpecial Paste: _
=xlValues, Operation: _
=xlMultiply, SkipBlanks: _
=False, Transpose: _
=False Selection.PasteSpecial Paste: _

=xlValues, Operation: _
=xlMultiply, SkipBlanks: _
=False, Transpose: _
=False
同樣,在引號之內的文本之間加連續符也是不對的,例如,下面的底線的使用是無效的:
MsgBox "To continue the long instruction, use the _

line continuation character."


上面的指令應該打斷為如下代碼:
MsgBox "To continue the long instruction, use the " & _

"line continuation character."


9 瞭解 VBA 錯誤
在編寫或編輯 VBA 過程之中,無論你多名小心,出錯的可能性還是很打的。例如,你可以錯誤拼寫
一語句,放錯了一個逗號或引號,或者忘記了一個句號或右括弧。這些類型的錯誤稱為句法錯誤。
幸運的是,VB 比較容易幫助你發現這種類型的錯誤。為了讓 VB 在你輸入一行代碼後,自動幫你檢測
語法的正確性,你需要在 VB 視窗的“工具”-“選項”裡,確保勾選了“編輯器”頁上的“自動語
法檢測”。
47
圖 2-7 選項對話方塊的編輯器上的“自動語法檢測”幫你檢查 VBA 過程裡的打字錯誤
當 VB 發現語法錯誤時,彈出一個錯誤資訊框,並且將有誤的代碼行顏色變為紅色(參見圖 2-8)或
者其它在選項對話方塊“編輯器格式”頁設定的顏色。如果錯誤資訊框上的解釋不夠清楚,你總是可
以點擊“説明”按鈕尋求更多的説明。再如果 VB 線上說明無法給你提供正確的方向,那麼返回你的
程式,仔細檢查有誤的那行代碼是否漏掉了字母,引號,句號,冒號,等於號,左括弧和右括弧等。
圖 2-8 這個錯誤由於漏掉了常數 xlCellType 前面的括弧而產生
48
圖 2-9 當 VB 試圖在工作表或儲存格區域裡選擇一個並不存在的儲存格時,就會產生一個執行時間
錯誤
查找語法錯誤可能是煩人的並且費時的事情。有些語法錯誤只有在過程運行的時候才能被發現。在
試圖運行你的過程的時候,VB 可能找到那種因為使用了無效的引數,或者是漏掉了那些需要成對
使用的指令如 If 語句和迴圈結構,而造成的錯誤。
技巧 2-5:程式調試
你很可能不只一次聽過“電腦程式裡充滿了錯誤”。在程式設計裡,錯誤就被稱為“bug”(錯誤,漏
洞),而“調試”(debug)則是給你的程式除錯的過程。調試的第一步就是改正所有的語法錯誤。
VB 提供了無數種工具,你可以使用它們來追蹤和消除錯誤。在本章中,你將知道如何使用 VB 助手幫
助你在編寫程式時出現盡可能少的錯誤;在第十三章中,你將學習如何使用專門的調試工具來捕獲
你 VBA 程式裡的錯誤。
除了語法錯誤外,還有其它兩種錯誤:執行時間和邏輯。執行時間錯誤發生在過程運行的時候。圖
2-9 顯示了一種典型的執行時間錯誤。執行時間錯誤經常發生在那些程式師在編寫代碼的時候沒有
想到的情況。例如,當程式試圖訪問一個使用者電腦上並不存在的驅動器或者檔,或者沒有首先檢
查是否用戶插入軟碟並關閉軟盤機口而試圖複製一個檔到軟碟,這時就會發生執行時間錯誤。
第三種錯誤——邏輯錯誤,通常不會發出明確的錯誤資訊。過程可能沒有語法錯誤,甚至運行無誤,
然而,得到的卻是錯誤的結果。邏輯錯誤通常非常難以查找,並且它藏得很隱秘,間歇發生,你不
能指望花幾個小時,甚至幾天,就能找到錯誤源。
10 查找幫助
當你使用宏錄製器時,你所有的操作都被翻譯成 VBA 指令,並且放置在一個模組裡。你在研究這些
錄製的過程時,不要忘記幫助隨時可用。你會發現有些代碼的意思可能會非常易懂,然而,有效卻
不怎麼明白。這時候,你就需要尋求説明了。當你獨自工作時,只要輕輕一點或者輕輕一按就可以
請教你的 VBA 老師了。使用 VB 線上說明比使用詞典或參考手冊要快捷和容易得多。如果你討厭一頁
一頁地在詞典裡找你需要的術語,你將驚訝於你如何快地從 VB 代碼視窗找到需要的説明頁面。
讓我們來檢查你如何通過內置的 VBA 老師的幫助,將 WhatsInACell 過程裡的第一句變成你自己的 VBA
詞彙:
Selection.SpecialCells(xlCellTypeConstants, 2).Select

上面的指令可以打斷為三部分,哪些部分?Selection 是物件還是屬性?SpecialCells 是什麼?


Select 是什麼?要回答這些問題,請依照下面的操作:
1. 啟動你要分析的過程的代碼視窗
2. 點擊你不懂的詞語
3. 按下 F1
49
圖 2-10 VBA 的物件,屬性和方法在線上說明裡解釋得很詳細
説明就會顯示相應的頁面。如果你的游標放在詞語“Selection”中間,你就會知道 Selection 可以
是一個應用程式的屬性, 也可以是一個視窗物件。如果你的游標在下一個不明
白的術語
(SpecialCells)並且按上面的步驟再做一遍後,你將看到 SpecialCells 説明螢幕(參見圖 2-10)。
注意,每個說明主題包含許多資訊。被查找的指令類型顯示在説明視窗的上面;指令的類型允許詞
語分類。例如,SpecialCells 是方法,可以使用這個方法的物件名稱列在“應用於”下面(譯者:
點擊“應用於”,出現物件名稱清單)。指令名稱下麵的“參閱”和“示例”讓你快速地跳到其它應
用或意義相似的指令,以及查看使用這個指令的例子。指令的意義顯示在“參閱”和“示例”標題
的下麵。接下來則是語法和需要的引數和其它參數。“說明”部分給出一些推薦使用該指令的情
形。
你可以很容易地將示例中的代碼複製到你的過程中去:選中你要複製的代碼行,按下 Ctrl+C,或者
按一下右鍵,選擇快顯功能表上的“複製”,然後切換到 VB 代碼視窗,點擊你需要粘貼代碼的地方,再
按下 Ctrl+V 或者選擇“編輯”-“粘貼”。
11 語法和程式設計快捷助手
VB 編輯器視窗上的“編輯”工具條上有很多按鈕,説明你快速容易地輸入正確格式的 VBA 指令。如
果“編輯”工具條目前在 VB 編輯器視窗上不可連接,那麼你可以選擇“視圖”-“工具條”
圖 2-11 編輯工具條上的按鈕使得編寫程式和設置指令格式變得很容易
50
在 VB 中編寫過程需要你使用成百上千的內置指令和函數。因為大多數人們無法學習所有 VBA 裡可用
指令的語法,IntelliSense 提供了你所需求的語法和程式設計説明。當你在代碼視窗程式設計時,經常會有
專門的視窗彈出來,引導你完成正確的 VBA 代碼。
12 屬性/方法清單
每個物件都會有許多屬性和方法。當你輸入一個物件名稱和一個句號以分開這個物件名稱和它的屬
性或方法時,彈出一個清單功能表。這個功能表列出了在這個句號之前的物件的所有可用的屬性和方法
(參見圖 2-12)。如何打開這個自動工具?選擇“工具”-“選項”,按一下選項對話方塊上的“編輯
器”頁,確保勾選上“自動列出成員”。
圖 2-12 在輸入 VBA 指令時,VB 會建議可以用於該物件的屬性和方法
如何從彈出功能表(圖 2-12)上選擇項目?試圖輸入你需要的屬性或方法名稱的前幾個字母,當 Excel
突出顯示了正確的專案名稱,則回車,插入該專案到你的代碼中去,並且開始新的一行;或者,你
需要在同一行繼續寫代碼,那麼就按 Tab 鍵代替回車。你也可以按兩下該項目來插入到代碼中去。只
要按 Esc 鍵就可以關閉這個彈出功能表,而不插入任何項目。當你按 Esc 鍵取消了彈出菜單後,VB 將不
會對同樣的物件顯示該功能表。你可以通過以下方法來再次顯示屬性/方法彈出功能表:
按 Ctrl+J
使用“backspace”(後退)鍵刪除句號,然後重新輸入句號
在代碼視窗上按一下右鍵(該物件,句號後),並且在快顯功能表上選擇“屬性/方法清單”
選擇“編輯”-“屬性/方法清單”
點擊“編輯器”工具條上的“屬性/方法清單”按鈕
13 常數列表
本章的前面,你學習了給屬性賦值,需要使用下面規則:
Object.Property = Value
如果選項對話方塊(編輯器頁)已經勾選了“自動列出成員”,Excel 就會在等號前的屬性彈出一個菜
單,列出該對屬性有效的常數。常數是表明確切的描述或者結果的值。Excel 和 Office 裡面的其它
應用軟體都有很多預先定義的內置常數。你將在第三章中學習常數,常數類型和使用。
假設你需要你的程式打開 Excel 工作表上的分頁預覽。“編輯”(譯者:視圖)功能表上有兩個選擇:
普通視圖和分頁預覽。普通視圖是絕大多數 Excel 任務的預設視圖模式;分頁預覽則是編輯視圖,
顯示工作表中有內容的區域。這兩種選項都有相應的內置常數來表示。Excel 常數起名總是以“xl”
開頭。你一旦在代碼視窗裡輸入指令:
ActiveWindow.View =
就會彈出一個功能表,列出這個屬性的有效常數名稱。使用在上節中“屬性/方法清單”彈出功能表同
樣的技術,也可以處理“常數清單”彈出功能表。按下 Ctrl+Shift+J 或者點擊“編輯器”工具條上的
“常數清單”按鈕,可以啟動常數清單功能表。
51
圖 2-13 常數清單彈出功能表顯示了對敲入的屬性有效的常數清單
14 參數資訊
如果你有過使用 Excel 函數的經歷,你就會知道許多函數需要一個或者多個引數(或者參數)。如
果 VB 函數要求引數,你可以在輸入左括弧後在游標下面看到一個提示框,顯示必要的或可選的自
變數的名稱(參見圖 2-14)。參數資訊幫你很容易地給 VBA 函數設置參數。另外,它提醒你其它兩
件對函數運行正確至關重要的事情:引數的順序和引數要求的資料類型。你將在下一章裡學習
資料類型。
在代碼視窗裡面輸入下述代碼,看看參數資訊是如何工作的:
ActiveWorkbook.SaveAs(
圖 2-14 A tip window displays a list of arguments utilized by a VBA function or instruction.

你一旦輸入了開始的括弧,游標的下面就會出現一個提示框,當前的引數會顯示為粗體;當你輸
入完第一個引數並且輸入了逗號,VB 會將下一個引數為粗體。可選的引數會用中括弧[ ]括
起來。只要按下 Esc 鍵就可以關閉參數資訊視窗。如何使用鍵盤來打開提示視窗?輸入指令或函數,
緊接著是左括弧,然後按下 Ctrl+Shift+I。你也可以點擊編輯功能表上的參數資訊按鈕或者選擇“編
輯”-“參數資訊”。
15 快速資訊
當你選擇了代碼視窗裡的指令,函數,方法,過程名稱或者常數,然後點擊編輯工具條上的“快速
資訊”按鈕(或者按下 Ctrl+I),VB 將會顯示突出顯示專案的語法和常數的值。快速資訊可以通過
選項對話方塊來打開或者關閉。在編輯器頁,勾選“自動顯示快速資訊”,打開快速資訊功能。
圖 2-15 快速資訊提供函數參數清單,也可以是常數值和 VBA 語句語法
16 自動完成關鍵字
加速在代碼視窗編寫 VBA 程式的另一種方法是使用“自動完成關鍵字”功能。當你輸入一個關鍵字
的前幾個字母,然後按下 Ctrl+空白鍵,或者點擊編輯工具條上的“自動完成關鍵字”按鈕,VB 會
幫你輸入這個關鍵字的剩餘字母,節約你的時間。例如,在代碼視窗裡輸入關鍵字“Application”
的前四個字母,並且按下 Ctrl+空白鍵:
Appl
52
VB 將會完成剩餘的字母,在 Appl 地方,你將看到整個關鍵字 Application。如果有好幾個關鍵字具
有相同的開頭字母,當你按下 Ctrl+空白鍵後,VB 會顯示一個彈出功能表,列出所有關鍵字。測試這
個例子,可以輸入關鍵字 Application 的前三個字母,按工具條上的自動完成關鍵字按鈕,然後在
彈出菜單上選取合適的關鍵字。
17 縮進/凸出
也許你已經看到,在選項對話方塊的編輯器頁上有許多設置你可以打開以使用代碼視窗許多可用的自
動功能。如果勾選了“自動縮進”選項,你就可以自動縮進所選的代碼行,縮進的量為“Tab 寬度”
文字方塊裡的數字。預設的自動縮進量是 4 個字母,你也可以在文字方塊裡輸入一個新的數字來改變 Tab
寬度。你為什麼需要在代碼裡使用縮進?縮進可以使你的代碼更容易閱讀和理解。特別是輸入一些
做決定或重複性工作的代碼行時,更建議使用縮進。你將在第五和第六章中學習如何創建這種類型
的 VB 指令。現在,我們來練習使用縮進和凸出代碼行,用第一章裡的宏 WhatsInACell 作為例子:
1. 在工程流覽器視窗,選擇 FirstSteps(Chap01.xls)工程,並且啟動含有 WhatsInACell 代碼的
WorksheetFormatting 模組
2. 選擇開始為關鍵字 With 和結束為 End With 的一段代碼
3. 點擊編輯工具條上的縮進按鈕,或者按鍵盤上的 Tab 鍵(譯者:按 Tab 鍵需要將游標放在行首,
而非選中指令)
4. 選中的指令會向右移動 4 個字母的位置,如果使用了 Tab 寬度的默認設置。
5. 點擊編輯工具條上的“凸出”按鈕,或者按 Shift+Tab 將選中的指令行返回原來的位置。
縮進和凸出同樣可以在編輯功能表裡找到。
18 設置注釋塊/解除注釋塊
在第一章中,你學習了一行代碼前面加一個引號表示注釋。注釋不但使代碼更容易理解,而且它在
VBA 過程的測試和處理問題中都是很有用的。例如,你執行一個過程時,它可能和期望的運行不一
致,對於那些可能產生問題的代碼行,你現在想略過它們,但是以後可能還需要用到它們,你就可
以在它們前面加一個引號注釋掉它們,而不必刪除它們。對大多數人來說,需要注釋掉一行代碼的
話,只有在它前面敲入一個引號就可以了,但是,如果要注釋掉整塊代碼,使用“編輯”工具條上
的“設置注釋塊”和“解除注釋塊”按鈕則是很方便的。要注釋掉幾行代碼,只要選中這些代碼行
並且點擊“設置注釋塊”按鈕。點擊“解除注釋塊”按鈕,將注釋掉的代碼恢復到代碼裡。
如果你沒有選中文本,就點擊了“設置注釋塊”按鈕,只有在游標所在的代碼行前面加入引號。
19 使用物件流覽器
如果你想要在 VBA 眾多的元件和功能中自由切換,那麼使用物件流覽器。這個專門的內置工具在 VB
編輯器視窗是可用的。使用下面任何一種方法都可以訪問物件流覽器:
53
圖 2-16 物件流覽器讓你在當前 VBA 工程裡可用的所有物件,屬性和方法裡流覽
按 F2
選擇“視圖”-“物件流覽器”
點擊工具條上的“物件流覽器”按鈕
物件流覽器讓你流覽 VBA 過程中可用的物件,也可以查看它們的屬性,方法和事件。在對象流覽器
的説明下,你可以在 VBA 工程的多個過程之間快速移動。
物件流覽器分為三部分(參見圖 2-16)。視窗的上部顯示“工程/庫”下拉清單,這裡列出了所有
庫名稱以及當前 VBA 工程裡可用的所有工程名稱。庫是包含一個應用程式裡物件的資訊的專門檔。
新的庫可以通過“引用”對話方塊(“工具”-“引用”)來添加。<所有庫>列出了你電腦上安裝了的
所有庫中的所有物件。當你選擇一個叫“Excel”的庫時,僅僅能在 Excel 裡執行的物件名稱才能被
看到。和 Excel 庫相反,VBA 庫列出了所有能在 VBA 裡執行的物件名稱。
在“工程/庫”下拉式清單方塊下面,有一個“搜索”文字方塊,讓你可以快速地在某個庫裡搜索你的信
息。這個地方會記住你最近搜索的四個項目。在物件流覽器的任何地方按一下右鍵,在快顯功能表上選
擇“全字匹配”,你就可以只搜索匹配整個字的內容。對象流覽器上的“搜索結果”(參見圖 2-16
和 2-17)顯示符合搜索條件的庫,類和成員。當你輸入搜索文本並且按一下搜索按鈕,VB 展開物件
流覽器對話方塊以顯示搜索結果。你可以點擊“望遠鏡”按鈕右邊的“顯示/隱藏搜索結果”來顯示
或者隱藏搜索結果。
54
圖 2-17 在對象流覽器裡搜索答案
類清單方塊顯示所選中的庫裡面所有可用的物件類,如果你選擇 VBA 工程,清單顯示該工程裡的物件。
在圖 2-16 裡,CommandBarComboBox 對象類被選中了。當你選中一個類,右邊的清單(成員)顯示
該類可用的屬性,方法和事件。圖 2-16 上顯示了類 CommandBarComboBox 的一些成員。成員默認地
按字母順序列出。然而,你可以使用物件流覽器快顯功能表上的“組成員”來組織這些成員清單(屬
性,方法或事件)。如果你選擇“工程/庫”裡面的 VBA 工程,成員清單方塊列出該工程裡的所有過程。
按兩下該過程名稱,就可以進入該過程並檢查其代碼。如果你選擇類“VBA”,你將看到 VB 內置的函數
和常數。如果你對所選的類或成員需要更多的資訊,可以點擊物件流覽器視窗上面的問號按鈕。對
象流覽器下面的視窗顯示所選成員的代碼格式。如果你點擊代碼格式裡綠色的連接部分,你將跳到
物件流覽器視窗的所選成員的類或庫。代碼格式裡的代碼可以被複製到 Windows 剪切板並且粘貼到
代碼視窗裡去。如果物件流覽器和代碼視窗都是可見的,你只要選中代碼格式裡的文本,直接拖曳
到代碼視窗就行了。
通過物件流覽器視窗上的橫豎分割線,你可以輕易地改變各個視窗的大小。
你已經發現了物件流覽器,你也許在想你如何才能讓它幫助你進行 VBA 程式設計。假設你在工作表中央
放置了一個文字方塊,你如何讓 Excel 將這個文字方塊移動到工作表的左上方?
1. 打開一個新工作表
2. 選擇“視圖”-“工具列”,然後點擊“繪圖”
3. 點擊“繪圖”上的文字方塊,在工作表中央畫一個文字方塊,並且隨便輸入什麼文字
4. 選擇文字方塊之外的任意儲存格
5. 按下 Alt+F11 啟動 VB 編輯器視窗,並且選擇工程流覽器視窗的 Personal (Personal.xls)
6. 選擇“插入”-“模組”,增加一個新的模組
7. 在屬性視窗,給該模組重命名:Manipulations
8. 選擇“視圖”-“物件流覽器”,或按 F2
9. 在“工程/庫”下拉式清單方塊裡選擇“Excel”類
10. 在搜索框裡輸入“textbox”並點擊搜索按鈕。確保你沒有在文字間敲入空格。
55
圖 2-18 Excel 在工作表上面的名稱框裡顯示插入的物件名稱
圖 2-19 使用物件流覽器視窗,你可以找到合適的 VBA 指令來編寫你自己的過程
顯示的結果表明物件 Shapes 掌管我們文字方塊操作(參見圖 2-19)。從成員清單清單上看,你可以很
快就知道 AddTextbox 方法就是用來在工作表裡添加文字方塊的方法。代碼格式視窗顯示了使用該方法
的正確語法。如果你選擇 AddTextbox 方法並且按 F1,你將看到關於它的説明視窗,有更詳細的關於
如果使用該方法的資訊(參見圖 2-20)
56
圖 2-20 要獲取物件流覽器找到的任何專案詳細資訊,只要選擇整個專案並且按 F1 就可以了
當你細看 AddTextbox 方法的引數和它們在説明視窗上的解釋時,你就可以很快地知道文字方塊在工
作表中的位置是由 Left 和 Top 屬性來決定的。你需要做的只是返回代碼視窗,編寫指令來移動文本
框到工作表的左上方。
11. 關閉物件流覽器和説明視窗(如果它們還是打開的)
12. 按兩下 Manipulations 模組,輸入過程 MoveTextBox:
Sub MoveTextBox()
1”
With ActiveSheet.Shapes("Text box 1")

.Select
.Left = 0
.Top = 0
End With
‘Text box 1 在中文版本裡為“文字方塊
End Sub
13. 選擇“運行”-“運行宏”來測試這個過程
當你返回到放置該文字方塊的工作表時,該文字方塊已經移動到了工作表的左上方了。注意,
MoveTextBox 程式在 Shapes 集合裡選擇了“Text box 1”。Text box 1 是工作表裡第一個對象的預設
名稱。你每次增加新的物件後,Excel 將給它安排新的好碼(編號)。除了使用物件名稱外,你還可
以引用集合成員的編號。例如,你可以輸入:
With ActiveSheet.Shapes(1)
來代替:
With ActiveSheet.Shapes("Text box 1")

我們來用 VB 操縱另一個物件,你自己試試。在你放置文字方塊的工作表裡再放置一個小圓圈。使用繪
圖工具上的橢圓工具畫這個圓圈。在 Manipulations 模組裡插入一個新的過程,並且編寫代碼來放
置圓圈。記住,Excel 連續地編號。第一個對象的編號為 1,第二個則為 2,等等,不管這個物件的
類型是文字方塊,橢圓或者是矩形,沒有關係。
下面的過程 MoveCircle 演示如何將當前工作表裡的橢圓移動到左上方去:
57
Sub MoveCircle()
With ActiveSheet.Shapes(2)
.Select
.Left = 0
.Top = 0
End With
End Sub
移動橢圓和移動文字方塊或者放在工作表裡的其它物件類似。注意,過程中引用的是物件的編號,而
非它的名稱橢圓 2。當你運行 MoveCircle 時,Excel 移動的是橢圓,而不是文字方塊了。
20 使用 VBA 物件程式庫
在前面的例子裡,你學習使用了 Excel 物件程式庫裡的 Shapes(圖形)集合成員的屬性。Excel 庫包含專
門使用 Excel 的物件,而 VBA 庫則提供對許多內置 VBA 函數的訪問,這些函數按類別分組。這些函數
是通用的,它們使你能夠管理檔,設置日期和時間,與用戶交流,轉換資料類型,處理文本串或
者進行數學計算。在下面的練習中,你將學習如何使用內置的 VBA 函數來創建一個新資料夾,而不
需要離開 Excel 介面:
1. 回到模組 Manipulations,那裡有你的 MoveTextBox 和 MoveCircle 過程
2. 輸入一個新的過程:
Sub NewFolder()
3. 點擊回車鍵,VB 會自動輸入結束關鍵字 End Sub
4. 按下 F2 啟動物件流覽器
5. 在“工程/庫”清單方塊裡選擇 VBA
6. 在搜索文字方塊裡輸入 file 並且回車
7. 滾動成員清單方塊,並且選中 MkDir 方法(參見圖 2-21)
8. 點擊物件流覽器上的“複製”按鈕,將被選擇的方法名稱複製到剪貼板
圖 2-21 編寫過程時,向物件流覽器尋求説明來找內置的 VBA 函數
9. 返回 Manipulations 視窗,並且將複製的指令粘貼到 NewFolder 過程
10. 輸入一個空格,接著是”C:\Study”。確保你在引號裡輸入了整個路徑名。NewFolder 過程為:
Sub NewFolder()
MkDir "C:\Study"
58
End Sub
11. 運行過程 NewFolder
當你運行 NewFolder 過程,VB 在 C 盤上創建了一個新的資料夾。啟動 Windows 流覽器可以查看該新文
件夾。創建一個新的資料夾後,你可能會發現你根本就不需要它,雖然你可以輕易地從 Windows 瀏
覽器裡刪除該資料夾,但是,如何從程式設計上去掉它呢?物件流覽器上列出了許多對資料夾和文件操
作很有幫助的其它方法。RmDir 方法正如 MkDir 方法一樣使用簡單。想要刪除你硬碟上的“Study”
資料夾,只要將 MkDir 方法換成 RmDir 方法然後重新運行 NewFolder 過程就可以了。或者,你也可以
創建一個新的過程 RemoveFolder,如下:
Sub RemoveFolder()
RmDir "C:\Study"
End Sub
RmDir 方法允許你從硬碟上刪除不需要的資料夾。
21 用物件流覽器來定位過程
除了定位物件,屬性和方法外,物件流覽器還是個定位在不同工程裡面的過程非常方便的工具。下
面的例子給你演示如何查看存在“Personal”工作簿裡面的過程:
1. 啟動物件流覽器並且選擇工程/庫下拉清單裡的 Personal。(譯者:因為我沒有 Personal 這個文
件。。。)
圖 2-22 物件流覽器列出所有在某個特定 VBA 工程裡可用的過程
物件流覽器的左邊顯示所選工程裡面物件名稱,而右邊則列出了所有可用的過程。
2. 按兩下 NewFolder 過程名稱,VB 將游標定位到該過程的第一行
3. 關閉對象流覽器
22 使用立即視窗
在你開始創建一個完善的 VBA 過程前(在下一章),先來做一些熱身練習,增加你的 VBA 詞彙。你怎
樣才能學得快而且沒有痛苦?你如何試驗一下一些新學的 VBA 指令?這裡有一些簡短的,互動的語
言練習:輸入一個簡單的 VBA 指令,Excel 會檢查並且將結果顯示在下一行。我們開始來設定你的練
習螢幕:
1. 在 VB 編輯器視窗,選擇“視圖”-“立即視窗”
在決定使用在你自己的 VBA 過程之前,立即視窗可以用來試驗 VB 語言中不同的指令,函數和運算子。
這是一個非常好的調試新語言的工具,你輸入在這個視窗裡面的指令,將會立即顯示結果。
立即視窗可以在 VB 編輯器視窗上任意移動,也可以設置為可連接的,這樣它就會出現在相同的地方。
可以通過選項對話方塊上的“可連接的”頁來打開或關閉可連接設置。在 VB 編輯器視窗上按下 Ctrl+G
就可以快速訪問立即視窗。立即視窗允許你輸入 VBA 語句,並且測試它們的結果,而不需要寫成一
個過程。立即視窗就像一個草稿板,你可以用它測試你的語句。如果該語句輸出了你希望的結果,
你就可以將立即視窗上的語句複製到你的過程中去(或者,你也可以將語句拖曳到代碼視窗,如果
代碼視窗是可見的)
59
2. 將 Excel 和 VB 編輯器視窗並排排列
3. 在立即視窗裡輸入下述指令,並且回車
Worksheets("Sheet2").Activate
當你按下回車鍵,VB 開始工作,如果你上面輸入的語句是正確的話,VBA 啟動當前工作簿裡的第二
個工作表。工作簿底部的 Sheet2 這時應該是突出顯示的。
4. 在立即視窗,輸入其它 VBA 語句並回車
Range("A1:A4").Select
你一旦按下回車,VB 將選中當前工作表的 A1,A2,A3 和 A4
5. 在立即視窗裡輸入下述指令:
[A1:A4].Value = 55
當你按下回車,VB 在 A1:A4 中的每個儲存格裡放置數字 55。上面的語句是引用 Range 物件的一種所寫
方式,完整的語法可讀性更強:
Range("A1:A4").Value = 55
圖 2-23 將 Excel 和 VB 視窗並排排列讓你可以觀察指令的運行
6. 在立即視窗輸入下述指令:
Selection.ClearContents
回車後,VBA 清除所選儲存格區域的內容,區域 A1:A4 現在是空的
7. 在立即視窗輸入下述指令:
ActiveCell.Select
回車後,VB 啟動 A1 儲存格
圖 2-24 在立即視窗裡輸入指令,一旦你按下回車鍵,指令就會被執行
60
圖 2-24 顯示了上面練習中在立即視窗裡輸入的所有指令。你每次按下回車鍵後,Excel 總是執行光
標所在行的語句。如果你想要再次執行同一指令,那麼點擊該指令行的任意位置,回車。為了更多
的練習,重新運行圖 2-24 裡語句,從立即視窗的第二行指令開始,點擊合適的地方並回車,一個
一個地執行這些指令。
23 獲取立即視窗裡的資訊
到目前為止,你已經使用立即視窗執行操作了,這些操作也可以是手動地在工作表的任意區域點擊
滑鼠並且輸入資料。立即視窗也允許你問問題。假設你想要找到下面問題的答案: “現在選中的是
哪些儲存格?”,“目前的儲存格裡的值是多少?”,“當前工作表的名稱是什麼?”,“當前窗口的編號
是多少?”使用立即視窗,你可以輕易地找到這些問題,以及其它問題的答案。在前面的例子裡,
你輸入了好幾個指令,讓我們返回立即視窗去問幾個問題。Excel 甚至在你關閉了立即視窗後還能
記住你在立即視窗裡輸入的指令。當你退出 Excel 時,立即視窗的內容自動會被刪除。
1. 滑鼠點擊立即視窗第二行你輸入 Range("A1:A4").Select 的任意地方
2. 回車,讓 Excel 再次選擇儲存格 A1:A4
3. 在立即視窗新的一行輸入下面的問題:
?Selection.Address
當你按回車,Excel 不會選擇工作表的任何東西,取而代之,立即視窗上會在另外一行顯示該指令
的結果。在該例中,Excel 返回的是當前被選擇的儲存格的絕對位址($A$1:$A$4)。問號(?)告訴
Excel 在立即視窗顯示指令的結果。除了問號,你還可以使用 Print 關鍵字。讓我們使用關鍵字 Print
問工作表名稱
4. 在立即視窗新的一行,輸入下述問題:
Print ActiveWorkbook.Name
回車後,Excel 在立即視窗新的一行輸入了當前工作簿的名稱。
找找應用程式的名稱如何?Chap02.xls 的父物件是誰?
5. 在立即視窗新的一行,輸入下述問題:
?Application.Name
Excel 會顯示它自己的全名:Microsoft Excel
立即視窗也可以用來做一個快速計算
6. 在立即視窗新的一行,輸入下述問題:
?12/3
回車後,Excel 會在下一行顯示該除法運算的結果。但是,萬一你想立即知道 3+2 和 12*8 的結果呢?
你可以將它們輸入在一行,而不必分成兩行,例如:
?3+2:?12*8
注意,冒號將兩個代碼塊分割開來。
當你按下回車鍵,Excel 分別在立即視窗的兩行顯示結果 5,96。
下面是你在立即視窗裡輸入的所有指令,以及 Excel 對你問題的回答:
Worksheets("Sheet2").Activate
Range("A1:A4").Select
[A1:A4].Value = 55
Selection.ClearContents
ActiveCell.Select
?Selection.Address
$A$1:$A$4
Print ActiveWorkbook.Name
Chap02.xls
?Application.Name
Microsoft Excel
?12/3
4
?3+2:?12*8
61
5
96
要清除立即視窗裡的指令,只要選中所有指令並且按下 Delete 鍵
24 學習對象
要在 Excel 裡創建一些自訂應用程式,需要一些常用物件或者物件集合的工作知識,例如 Range,
Workbook (Workbooks),Worksheet (Worksheets),Window (Windows)和 Application。在前面部
分,你開拓了學習 VB 的許多方法。這裡有一個總結關於什麼時候使用什麼工具:
當你在一個現行 VBA 過程,對物件,屬性或方法有疑義時按 F1 打開線上說明
如果你需要快速列出每個可用物件的屬性和方法時,或者查找一個很難找到的過程時,使用對
象流覽器
如果你想要測試 VBA 並且立即查看 VBA 命令的結果時,啟動立即窗口。
本章剩餘的幾頁裡有一些 VBA 語言訓練,可以説明你更好地理解 VBA 語法。如果你花些時間在立即窗
口過一遍這些語法訓練,你將理解絕大部分。
圖 2-25 Excel 物件模型裡的 Range 物件
25 試算表儲存格操作
當你已經準備好編寫你自己的 VBA 過程,將試算表任務自動化的時候,你很可能是從尋求操作電
子表格儲存格的指令開始的。你需要知道如何選擇儲存格,如果在儲存格輸入資料,如何給儲存格
區域命名,如何設置單元格格式,以及如何移動,複製和刪除儲存格。雖然這些任務可以通過滑鼠
或鍵盤輕易執行,掌握 VBA 這些方面的技術需要一些練習。你必須使用 Range 物件來引用單個儲存格,
儲存格區域,行或列。如果你看了 Excel 物件模型,你會注意到 Range 物件是另外一個大物件——
Worksheet 物件——的一部分。有三種屬性讓你訪問 Range 物件:Range 屬性,Cells 屬性和 Offset
屬性。
26 使用 Range 屬性
Range 屬性返回一個儲存格或者儲存格區域。引用必須是 A1 在引號裡的樣式(例如:”A1”)引用
可以包括區域運算子冒號(例如:”A1:B2”)或者聯合運算子逗號(例如:”A”,”B12”)
VBA 操作
選擇單個儲存格(例如 A5)
選擇一個儲存格區域(例如 A6:A10)
立即視窗輸入
Range("A5").Select
Range("A6:A10").Select
選擇一些不相鄰的儲存格(例如 A1, B6, C8) Range("A1, B6, C8").Select
選擇一些不相鄰的儲存格和儲存格區域(例

A11:D11, C12, D3)
27 使用 Cells 屬性
Range("A11:D11, C12, D3").Select
當你要選擇一個確定的儲存格時,Cells 屬性要求兩個引數,第一個是行號,第二個是列號或者
列字母。引數輸入在括弧中。如果忽略引數,Excel 將會選擇當前工作表的所有儲存格。
62
VBA 操作
選擇單個儲存格(例如 A5)
選擇一個儲存格區域(例如 A6:A10)
選擇工作表中所有儲存格
立即視窗輸入
Cells(5, 1).Select 或 Cells(5, A).Select
Range(Cells(6, 1), Cells(10, 1)).Select

Cells.Select
注意,在上面的例子中,你如何結合使用 Range 和 Cells 屬性:
Range(Cells(6, 1), Cells(10, 1)).Select

在上面的例子裡,第一個 Cells 屬性返回儲存格 A6,而第二個返回儲存格 A10。Cells 屬性返回的單


元格之後又當做 Range 物件的參數。結果 Excel 就選擇了上面儲存格為第一個 Cells 屬性返回的結果
和下面為第二個 Cells 屬性返回儲存格的區域了。
工作表是儲存格的集合,你也可以使用只帶一個引數的 Cells 屬性來表示儲存格在工作表所有單
元集合中的位置。Excel 按下列方式給儲存格編號:儲存格 A1 是工作表中的第一個儲存格,B1 是第
二個,C1 是第三個,等等。Cell256 是第一行中的最後一個儲存格。你也許會想起 Excel 只有 256 列。
VBA 操作
選擇儲存格 A1
選擇儲存格 C1
選擇儲存格 IV1
選擇儲存格 A2
立即視窗輸入
Cells(1).Select or Cells.Item(1).Select

Cells(3).Select or Cells.Item(3).Select

Cells(256).Select or Cells.Item(256).Select

Cells(257).Select or Cells.Item(257).Select

注意,Item 是返回一個集合成員的屬性。因為 Item 是一個集合的預設成員,你可以直接引用工作表


儲存格,而不必明確地使用 Item 屬性。
現在你發現了兩種方法選擇儲存格(Range 屬性和 Cells 屬性),你也許很迷惑為什麼要使用更複雜
的 Cells 屬性呢?很明顯 Range 屬性更具有可讀性,畢竟,你遠在決定學習 VBA 之前就在 Excel 公式和
函數裡面使用了 Range 引用。然而,當需要將儲存格當做集合操作的時候,Cells 屬性則使用更方便。
使用這個屬性去訪問儲存格集合中的所有儲存格或者單個儲存格。
28 使用 Offset 屬性
另外一個引用工作表儲存格非常靈活的方法是使用 Offset 屬性。當工作表任務自動化時,你也許不
知道某個儲存格的確切地址。你如何能夠選擇一個你根本不知道位址的儲存格?你可以讓 Excel 基
於當前選擇的儲存格來選擇一個位置。Offset 屬性通過計算從開始選擇的儲存格向下或向上移動的
具體行數,來得到新的區域。同樣也可以從當前選擇的儲存格區域向右或向左移動具體的列數。
Offset 屬性使用兩個引數來獲得新儲存格區域的位址。第一個引數表示行偏移,第二個引數
則表示列偏移。我們來測試一下幾個例子:
VBA 操作立即視窗輸入
選擇儲存格 A1 下面一行和右邊三列的儲存格 Range("A1").Offset(1, 3).Select
選擇儲存格 D15 上面兩行和左邊一列的儲存格 Range("D15").Offset(-2, -1).Select
選擇目前的儲存格上面一行的儲存格(同列) ActiveCell.Offset(-1, 0).Select
上面的第一個例子裡,Excel 選擇的時儲存格 D2。一旦你輸入了第二個例子,Excel 選擇了儲存格 C13。
如果儲存格 A1 和 D15 已經被選中了,你也可以將上面的兩個例子改寫為這樣:
Selection.Offset(1, 3).Select
Selection.Offset(-2, -1).Select
注意,上面第三個例子裡的第二個引數是 0,第一個或第二個引數為 0 時,Offset 屬性相應表示
當前行或當前列。如果當前作用儲存格在第一行,那麼指令 ActiveCell.Offset(-1, 0).Select 會
導致錯誤。
當使用 Offset 屬性時,你可能有時需要改變選擇區域的大小。假設開始選擇的區域是 A5:A10,如何
將選擇區域向下移動兩行,向右移動兩列,然後再改變新選擇區域的大小呢?假設新的選擇區域應
該是 C7:C8。Offest 屬性只能完成前面部分,後面部分要求另外一個屬性來完成。Excel 有個專門的
Resize 屬性,你可以結合 Offset 屬性和 Resize 屬性來回到上面的問題。在你結合這兩個屬性之前,
63
我們先來看看如何獨立地使用它們:
1. 將 Excel 視窗和 VB 視窗並排顯示
2. 啟動立即視窗,並且輸入下述指令:
Range("A5:A10").Select
Selection.Offset(2, 2).Select
Selection.Resize(2, 4).Select
上面的第一條指令選擇區域 A5:A10,當前作用儲存格是 A5。第二條指令將選區偏移到 C7:C12。單元
格 C7 處於作用儲存格 A5 的向下兩行和向右兩列。現在,作用儲存格是 C7。最後一條指令將當前選區
改變大小,儲存格區域 C7:C8 被選中了,而不再是 C7:C12。象 Offset 屬性一樣,Resize 屬性也需要
兩個引數。第一個是你要選取的行數, 第二個則是要選取的具體列數因此,
指令
Selection.Resize(2, 4).Select 將當前選擇區域改為兩行和四列
後面兩行指令可以結合成下面方式:
Selection.Offset(2, 2).Resize(2, 4).Select

上面的例子,先是 Offset 屬性計算得到新區域的起始點(譯者:選區左上角的儲存格),接著是 Resize


屬性決定新選區的大小,然後是 Select 方法選取具體的儲存格區域。
技巧 2-6:錄製儲存格的選擇
巨集錄製器預設地使用 Range 屬性錄製選擇儲存格。如果你打開宏錄製器,並且選擇儲存格 A2,輸入
“text”,再選擇儲存格 A5,你將在 VB 編輯器視窗裡得到下述代碼:
Range("A2").Select
ActiveCell.FormulaR1C1 = "text"
Range("A5").Select
如果你使用相對參照方式,巨集錄製器會使用 Offset 屬性。你可以在錄製前,點擊宏錄製工具條上的
相對參照按鈕。宏錄製器將得到如下代碼:
ActiveCell.Offset(-3, 0).Range("A1").Select

ActiveCell.FormulaR1C1 = "text"
ActiveCell.Offset(3, 0).Range("A1").Select

當你使用相對參照方式錄製巨集時,過程總是會選擇相對於當前作用儲存格的儲存格。注意,上面指
令中的第一和第三行的引用儲存格 A1,即使我們沒有涉及到 A1 的任何東西。你可能記得,在第一章
中,巨集錄製器用它自己的方式將事情搞定。為了將上面的指令變簡單一些,你可以刪除對儲存格 A1
的引用:
ActiveCell.Offset(-3, 0).Select
ActiveCell.FormulaR1C1 = "text"
ActiveCell.Offset(3, 0).Select
使用相對參照來錄製過程後,不要忘記再次點擊這個按鈕,如果下次錄製一個非相對位址的過程。
29 選擇儲存格的其它方法
如果你經常需要訪問你工作表裡某些遙遠的儲存格,你可能已經對下面的鍵盤快速鍵很熟悉:End+
上箭頭, End+下箭頭, End+左箭頭和 End+右箭頭。在 VBA 中,你可以使用 End 屬性快速地移動到遙
遠的儲存格。
VBA 操作
選擇任何行的最後一個儲存格
選擇任何列的最後儲存格
選擇任何行的第一個儲存格
選擇任何列的第一個儲存格
立即視窗輸入
ActiveCell.End(xlright).Select
ActiveCell.End(xldown).Select
ActiveCell.End(xleft).Select
ActiveCell.End(xlup).Select
注意,End 屬性要求一個引數來表示你要移動的方向。使用下列 Excel 內置的常數來跳到具體的方
向:xlright, xlleft, xlup, xldown。
30 選擇行和列
Excel 使用 EntireRow 和 EntireColumn 屬性來選擇整行或整列。
64
VBA 操作
選擇當前作用儲存格所在行的整行
選擇當前作用儲存格所在列的整列
立即視窗輸入
Selection.EntireRow.Select
Selection.EntireColumn.Select
你選擇了一個儲存格區域,你也許想要知道選區包括多少行,多少列。我們來讓 Excel 計算區域
A1:D15 中的行數和列數:
1. 在立即視窗裡輸入下述 VBA 語句
Range("A1:D15").Select
如果 Excel 視窗可見,當你按回車後,VBA 會選中區域 A1:D15
2. 輸入下列語句來得到選區的行數
?Selection.Rows.Count
一旦你回車,VBA 在下一行顯示結果。你的選擇包括 15 行
3. 輸入下列語句來得到選區的列數
?Selection.Columns.Count
現在 VBA 告訴你,選中的區域 A1:D15 佔據了四列的寬度。
4. 將游標放在關鍵字 Rows 或 Columns 中的任意位置,並且按下 F1,獲取這些有用屬性的更多資訊。
31 獲取工作表資訊
Excel 工作表有多大?它有多少儲存格,列和行?即使你忘記了這些細節,使用 Count 屬性。
VBA 操作
計算 Excel 工作表裡總儲存格數
計算 Excel 工作表裡總行數
計算 Excel 工作表裡總列數
立即視窗輸入
?Cells.Count
?Rows.Count
?Columns.Count
Excel 2002 工作表裡有 16,777216 個儲存格,65,536 行和 256 列。
32 往工作表輸入資料
輸入工作表裡的資訊可以是文本,數位或者公式。你可以使用 Range 物件的兩種屬性之一來往單元
格或儲存格區域裡輸入資料:Value 屬性或者 Formula 屬性。
Value 屬性:
ActiveSheet.Range("A1:C4").Value = "=4 * 25"

Formula 屬性:
ActiveSheet.Range("A1:C4").Formula = "=4 * 25"

上面兩種例子,A1 儲存格都顯示 4 乘 25 的結果 100。


VBA 操作
在儲存格 A5 裡輸入文本“Amount Due”
在儲存格 D21 裡輸入數位“123”
在儲存格 B4 裡輸入公式“=D21*3”
33 返回工作表中的資訊
立即視窗輸入
Range("A5").Formula = "Amount Due"
Range("D21").Formula = 123
Range("D21").Value = 123
Range("B4").Formula = "=D21 * 3"
毫無疑問,你在某些 VB 過程中可能需要返回儲存格或者儲存格區域的內容。雖然你既可以使用 Value
屬性也可以使用 Formula 屬性,但是,這次,Range 物件的這兩個屬性是不可互用的。
Value 屬性顯示具體儲存格中公式的結果。例如,如果 A1 中含有公式“=4*25 ”,那麼指
令?Range("A1").Value 將會返回值 100
如果你想要顯示公式,而不是結果,那麼你必須使用 Formula 屬性:?Range("A1").Formula。
Excel 將會顯示公式“=4*25”而不是結果 100
65
34 單元格格式
一個頻繁的任務就是給選中的儲存格或區域設置格式。你的 VBA 過程可能需要查明某個具體儲存格
的格式。我們可以使用 NumberFormat 屬性來找回單元格格式:
?Range("A1").NumberFormat
在立即視窗輸入上面的問題後,Excel 顯示“General”(譯者:中文版本是“常規”,G/通用格式),
它表示所選的儲存格沒有設置任何特殊的格式。要用 VBA 改變單元格格式,輸入下列指令:
Range("A1").NumberFormat = "$#,##0.00"

如果你在儲存格 A1 裡輸入 125,當你使用上面的指令給它設置格式後,儲存格 A1 將顯示“$125.00”。


你可以在 Excel 視窗的“設置單元格格式”對話方塊裡查找必要的格式代碼(“格式”-“儲存格”)。
如果你要的格式沒有列在“設置單元格格式”對話方塊裡,那麼請參考線上說明,查找創建用戶定義
的格式指導。
圖 2-26 你可以使用設置單元格格式對話方塊裡的自訂給選擇的儲存格或儲存格區域設置不同的
格式
35 移動,複製和刪除儲存格
在你做一個新的工作表範本時,你會發現經常要移動,複製和刪除儲存格內容。VB 讓你的工作表編
輯工作自動化變得可能,只要使用一些容易使用的方法就行:Cut,,Copy 和 Clear。
VBA 操作
移動儲存格 A5 的內容到儲存格 A4 裡面
複製儲存格 A3 裡的公式到區域 D5:F5 中
清除儲存格 A4 裡的內容
立即視窗輸入
Range("A5").Cut
Destination:=Range("A4")
Range("A3").Copy
Destination:=Range("D5:F5")
Range("A4").Clear
Range("A4").Cut
注意,使用在 Range 物件上的 Cut 和 Copy 方法都需要一個叫“Destination”的特殊引數。這個自
變數明確你要放置剪切或複製的資料的儲存格或儲存格區域位址。在最後一個例子中,使用了沒有
Destination 引數的 Cut 方法來去除具體儲存格的資料。Clear 方法將刪除具體儲存格或儲存格區
域的所有內容,包括格式和批註。如果你想要明確你要刪除什麼,使用下列方法:
ClearContents-僅清除儲存格或儲存格區域內的資料
66
ClearFormats-僅清除格式
ClearContents-清除區域裡的所有批註??注:ClearComment
36 操作工作簿和工作表
既然你已經涉足操作工作表儲存格和儲存格區域,是時候上一個臺階,學習如何控制單個工作簿,
已經整個工作簿集合了。如果你不知道如何打開一個新工作簿的話,你就不知道準備一個新的電子
表格了;如果你不知道如何關閉工作簿,你就不知道如何將工作簿從螢幕上消除。這些重要的任務
由兩個 VBA 方法處理:Add 和 Close。下面的練習將給你必要的如何操作工作簿和工作表的語言技巧。
VBA 操作
打開一個新工作簿
獲得第一個工作簿的名稱
獲得打開的工作簿數目
啟動第二個打開的工作簿
啟動工作簿 Chap02.xls
當前活動的工作簿存檔為 NewChap.xls
關閉第一個工作簿
關閉當前活動的工作簿,不保存變化
關閉所有打開的工作簿
立即視窗輸入
Workbooks.Add
?Workbooks(1).Name
?Workbooks.Count
Workbooks(2).Activate
Workbooks("Chap02.xls").Activate
ActiveWorkbook.SaveAs Filename:="NewChap.xls"

Workbooks(1).Close
ActiveWorkbook.Close SaveChanges:=False

Workbooks.Close
如果你運行了最後一個例子,那麼現在你所有的工作簿都已經關閉了。在你要在工作表上使用前,
請確保先打開一個新工作簿。當你除了單個工作表時,你必須知道如何在工作簿裡添加新的工作表,
知道如何選擇一個或一組工作表,知道如何命名、複製、移動和刪除工作表。在 VB 裡,每個任務都
需要一個專門的方法或屬性。
VBA 操作
添加一個新工作表
獲得第一個工作表的名稱
選擇名為“Sheet3”的工作表
選擇第一,第三和第四個工作表
啟動名為“Sheet1”的工作表
立即視窗輸入
Worksheets.Add
?Worksheets(1).Name
Worksheets(3).Select
Worksheets(Array(1,3,4)).Select
Worksheets(“Sheet1”).Activate
將工作表“Sheet2”移動到工作表“Sheet1” Worksheets("Sheet2").Move
之前
重命名工作表“Sheet2”為“Expenses”
獲得當前工作簿裡的工作表數目
刪除當前工作簿裡的工作表“Expenses”
注意 Select 方法和 Activate 方法之間的區
別:
Before:=Worksheets("Sheet1")
Worksheets("Sheet2").Name = "Expenses"

?Worksheets.Count
Worksheets("Expenses").Delete
當只要一個工作表被選擇時,Select 和 Activate 方法可以互換使用
如果你要選擇一組工作表,Activate 方法將讓你決定你選中的工作表中哪個要啟動。我們知道,
同時只能有一個工作表被啟動。
技巧 2-7:Sheets(譯者簡稱為“表”)而不是 Worksheets(譯者簡稱為“工作表”)
除了工作表之外,工作簿集合裡還包括圖表。使用 Add 方法在工作簿裡添加一個新圖表:
Charts.Add
統計圖表數目,使用:
?Charts.Count
在 Excel 97 之前的版本中,工作簿集合裡包括兩種額外的表:DialogSheets 和 Modules。Dialogs
已經被更親切的使用者表單(UserForms)所取代了。從 Excel 97 開始,對話方塊和模組都被創建在 VB
編輯器視窗裡面了。
37 操作視窗(Windows)
當在好幾個 Excel 工作簿上工作,並且需要比較或者鞏固資料,或當你想要看同一個工作表裡的不
67
同部分時,你很可能要用到 Excel“視窗”功能表裡的選項:新建視窗和重排視窗。我們來看看如何
通過 VBA 來安排視窗。
VBA 操作
在新視窗裡顯示當前活動工作簿
在螢幕上顯示所有打開了的工作簿
啟動第二個視窗
獲得當前窗口的名稱
將當前窗口的名稱改為“My Window”
立即視窗輸入
ActiveWorkbook.NewWindow
Windows.Arrange
Windows(2).Activate
?ActiveWindow.Caption
ActiveWindow.Caption = "My Window"
當你在螢幕上顯示視窗時,你可以決定如何排列它們。Arrange 方法有許多引數,讓你如何放置
視窗的引數稱為 ArrangeStyle(排列方式)。如果你忽略 ArrangeStyle 引數,Excel 將平鋪所有
窗口。
常數
xlArrangeStyleTiled
xlArrangeStyleCascade
xlArrangeStyleHorizontal
xlArrangeStyleVertical

1
7
2
3
描述
平鋪視窗(預設模式)
層疊窗口
水準並排視窗
垂直並排窗口
除了使用常數名稱外,你也可以使用上面列出的等價值。要將所有視窗層疊起來,寫下面的指令就
可:
Windows.Arrange ArrangeStyle:=xlArrangeStyleCascade

或者更簡單點:
Windows.Arrange ArrangeStyle:=7
38 管理 Excel 應用程式
在本章的開始部分,你學習了物件是組織在一個叫物件模型的專門結構。在應用程式的物件模型的
最上面就是應用程式它本身。通過控制 Application 物件,你可以進行很多操作,例如將螢幕顯示
效果保存為當日最後顯示的效果,或者退出該應用程式。你知道,Excel 允許你使用“檔”功能表
裡的選項“保存工作區”來保存螢幕設定。在 VBA 裡可以很容易地完成保存工作區的工作:
Application.SaveWorkspace "Project"
上面的指令將螢幕設置保存在名叫“Project”的工作區裡。下次你要在相同的檔和窗口排列時,
只要打開“Project”檔,Excel 就會打開正確的檔和恢復你要的螢幕。
VBA 操作
獲取當前應用程式名稱
立即視窗輸入
?Application.Name
將 Excel 應用程式標題改為“My Application” Application.Caption = "My Application"
將 Excel 應用程式標題改回為“ Microsoft Application.Caption = "Microsoft Excel"
Excel”
獲取你正在使用的作業系統
獲取該應用程式註冊的人名或公司名
獲取 Excel.exe 保存的資料夾路徑
退出 Excel
39 接下來……
?Application.OperatingSystem
?Application.OrganizationName
?Application.Path
Application.Quit
在本章,你學習了很多基礎的 VBA 術語和內置工具,可以用來使你的程式編寫和調試更容易。你現
在應該對絕大多數 Excel 物件是如何組織和控制的有了一個比較好的瞭解。我努力使描述儘量少,
並且注重於教你如何使用你的新語言技巧立即控制 Excel 而不必先編寫過程。正因為此,我注重於
立即窗口。VB 過程通常包含多於一行的代碼,事實上,它們可能變得很複雜。在你開始創建完整的
VBA 過程之前,你仍然需要學習一些事情。例如,你如何保存目前 Excel 返回的資訊,你的過程稍後
68
可以使用?在立即視窗裡輸入指令的時候,你學習了如何問 Excel 一些重要的資訊,你得到了類似
“當前活動工作簿裡有多少工作表?”或“儲存格 A4 的內容是什麼?”問題的答案。Excel 不會在
乎你的問題有多麼煩,只要你輸入符合嚴格 VBA 語法的問題,Excel 就會給你答案。當你開始編寫你
自己的過程時,你會需要知道如何保存 Excel 的答案。在下章裡,你將學習如何保存這種以後變數
需要用到的資訊。你也將會展開資料類型和常數主題的學習。
第三章瞭解變數,資料類型和常量
作者:Julitta Korol 翻譯:Tiger Chen Dec 18’ 2004
就象現實生活中一樣,程式設計中也是有些事情必須馬上做,而其它的事情可以稍後進行。當你推遲一
件事情,你可以將它放入你心裡的或紙上的“要做的事情”清單。清單上零零散散的事情,經常按
照它們類型或者重要性來分類。當你將任務交給別人或者最終你要開始做了,你需要將該任務從清
單裡劃掉。本章將演示你的 VBA 過程如何記住一些重要的資訊,後面將用在你的語句或計算裡。你
將學習過程如何保留不斷地往變數輸入“要做的事情”,如何聲明變數,以及它們和資料類型及常
量有何關係。
1 保存 VBA 語句的結果
在第二章,你在立即視窗上輸入一些 VB 指令,並且返回一些資訊。例如,當你輸入?Cells.Count,
你發現工作表裡有 16,777,216 個儲存格。然而,當你在立即視窗之外的地方寫 VB 過程時,你不能使
用問號。當你忽略問號輸入 Cells.Count,VB 不會突然停下來告訴你這個指令的結果。如果你想要
知道某個指令執行後的結果,你就必須告訴 VB 記住它。在程式設計中,VB 指令返回的結果可以賦值給變
量。
2 變數是什麼
變數是一個簡單的用來引用一條資料的名稱。你每次想要記住一個 VBA 指令的結果時,考慮用一個
名稱來代表它。例如,如果你必須用數字 16,777,216 來提醒你工作表中的總儲存格數目,你可以使
用一個名稱,如 AllCells,NumOfCells,TotalCells,等等來代替。變數名稱裡可以包含字母,數
字和一些標點符號,除了下面這些之外, # $ % & @ !
變數的名稱不可以以數位開始,也不可以含有空格。如果你想在變數名稱裡包含多於一個詞語,可
以使用底線。雖然變數名稱最多可以包含 254 個字母,但是,你最好使用短而簡單的變數名稱。
使用短名稱將會節省你的輸入時間,如果你需要在你的 VB 過程裡多次引用該變數的話。VB 不管你在
變數名稱裡使用大寫字母還是小寫字母,然而,大多數程式設計者使用小寫字母,並且當變數名稱包括
一個或多個詞語時,他們使用標題字母,那就是,象下面這樣,他們將每個詞語詞頭大寫:
NumOfCells,First_Name。(譯者:中文也可以做為變數名稱使用,但是,個人不建議使用中文)
技巧 3-1 不能用作變數名稱的詞語
除了這些 VBA 佔用了的詞語之外,你可以使用任何你想用的標籤作為變數名稱。在 VBA 中有特定意義
的 VB 語句以及其它某些詞語不能用作變數名稱。例如,詞語 Name,Len,Empty,Local, Currency
或者 Exit,如果你使用它們作為變數名,將會產生錯誤。
技巧 3-2 富有意義的變數名稱
給變數那種可以説明你記住它們作用的名稱。有些程式師使用首碼來識別變數類型。在你的代碼中,
一個以首碼“str”開頭的變數名稱(例如 strName),很快就可以知道它是傳遞文本字串的變數。
3 資料類型
當你創建 VB 過程時,你腦海裡必然有個目的,你想要處理資料。因為你的過程要處理不同類型的信
息,所以,你應該瞭解 VB 如何儲存資料。“資料類型”這個術語決定了資料如何儲存在電腦的記憶體
裡。例如,資料可以儲存為數位,文本,日期,物件,等等。如果你忘了告訴 VB 你的資料類型,VB
將分配資料類型為“Variant”。“Variant”類型有能力解決資料本身的操作類型並且使用該類型。
表 3-1 裡列出了 VB 資料類型。除了內置的資料類型之外,你還可以定義你自己的資料類型。(你將
在第八章裡看到用戶自訂的資料類型的例子。)因為不同的資料類型佔據電腦記憶體的空間是不一
樣的,一些類型比另外一些更“貴”些,因此,為了保存記憶體並確保你的過程運行更快,你應該選
擇佔用位元組最少的,同時又能夠處理你資料的資料類型。
表 3-1 VBA 資料類型
69
資料類型(名稱) 大小(位元組) 描述
Boolean
Byte
Integer
Long
Single
Double
2
1
2
4
4
8
邏輯值 True 或 False
0 到 255 的整數
–32,768 到 32,767 的整數
–2,147,483,648 到 2,147,483,647 的整數
單精確度浮點數值
負數:–3.402823E38 到–1.401298E–45
正數:1.401298E–45 到 3.402823E38
雙精度浮點數值
負數: –1.79769313486231E308
–4.94065645841247E–324

Currency
Decimal
Date
8
14
8
正數:4.94065645841247E–324 到 1.79769313486231E308
(放大的整數(譯者:整數除以 10000 得到的數值,參見 VBA
説明))使用在定點計算中:
–922,337,203,685,477.5808 到
922,337,203,685,477.5807
+/–79,228,162,514,264,337,593,543,950,335 沒有小數
點;
+/–7.9228162514264337593543950335 小數點後有 28 位數
字;
最小的非 0 數字是
+/–0.0000000000000000000000000001
從 100 年 1 月 1 日到 9999 年 12 月 31 日的日期
String(變長字串) 10 位元組+

符串長度
變長字串最多可包含大約 20 億( 2^31)個字元。
String(定長字串) 字串長度定長字串最多可包含大約 65,400 個字元。
Object
Variant(帶數字)
Variant(帶字母)
用戶定義類型
(使用 Type)
4 如何產生變數
4
16
22 位元組+

符串長度
成員所需

數值
物件變數用來引用 Excel 中的任何物件
最高範圍到 Double 類型的任何數值
和變長字串的範圍一樣
每個成員的範圍和它的資料類型的範圍一致
你可以通過一個專門的命令來聲明變數從而產生一個變數,或者也可以直接在語句裡使用變數(而
不需要聲明)。當你聲明變數時,你實際上讓 VB 知道該變數的名稱和資料類型,這叫做“強制顯式
聲明變數”。
如果你在使用變數前不告訴 VB 關於該變數的任何資訊,你這是在含蓄地告訴 VBA 你想要創建這個變
量。沒有明確聲明的變數會自動地分配為 Variant 資料類型(參見表 3-1)。雖然不聲明變數很方便
(你可以隨意創建變數,並且不用事先知道被賦值的數值的資料類型就可以賦值給該變數),但是,
它會導致很多問題,參見技巧 3-4 中要點。
技巧 3-3 強制顯式聲明變數的好處
強制顯式聲明變數加速過程的執行。因為 VB 知道資料類型,它只會佔用實際儲存資料需要的內

強制顯式聲明變數使你的代碼可讀性和可理解性增加,因為所有的變數都已列在過程的最前面
強制顯式聲明變數説明預防由於變數名稱拼寫錯誤而導致的錯誤。VB 根據變數聲明裡的拼寫自
動更正變數名稱。
技巧 3-4 隱式聲明變數的壞處
如果你錯誤拼寫了一個變數名稱,VB 會顯示執行時間錯誤,或者產生一個新的變數。你保證需
70
要浪費很多時間來做故障排除,然而,如果在過程前聲明了變數,這些很容易避免
因為 VB 不知道你要保存的變數的資料類型,它將分配給它 Variant 資料類型。這導致你的過程
運行要慢一些,因為 VB 每次在處理這個變數時不得不檢查資料類型。因為 Variant 可以儲存任
何一種資料類型,VB 不得不佔用更多的記憶體來儲存你的資料
5 如何聲明變數
你可以使用關鍵字 Dim 來聲明變數,Dim 代表“Dimension”。關鍵字 Dim 後面緊跟變數名稱,再後面
就是資料類型。假設你想讓過程顯示員工的年齡,你計算年齡之前,必須給過程提供員工的生日。
你可以這樣做,聲明一個叫 DateOfBirth 的變數:
Dim DateOfBirth As Date
注意,關鍵字 Dim 之後是變數名稱(DateOfBirth)。如果你不喜歡這個名稱,你可以自由地改為其
它的,只有你想用的名稱不是 VBA 關鍵字之一就行。關鍵字 As 以及後面的表 3-1 其中的一個資料類
型,明確了該變數的資料類型。資料類型 Date 告訴 VB 變數 DateOfBirth 將會儲存日期。
要儲存員工的年齡,按下面方式聲明變數 Age:
Dim Age As Integer
變數 Age 將會儲存今天和該員工生日之間年數的數字。因為年齡顯示為整年,所以變數 Age 就被分配
為 Integer 資料類型。
你可能還想要你的程式追蹤員工的姓名,因此需要聲明另一個變數來保存員工的名和姓:
Dim FullName As String
因為詞語“Name”已經在 VBA 佔用的清單上,在你的 VBA 程式裡使用它的話保證會有錯誤。將變數命
名為 FullName 並且將它聲明為 String 類型(因為員工姓名是文本),來保存員工姓名。
技巧 3-5 隱式聲明變數
沒有用 Dim 語句來明確聲明的變數叫做隱式聲明。這些變數自動會被分配一個資料類型 Variant。它
們可以保存數位,字串和其它資訊類型。你可以通過在你 VBA 程式的任何地方,簡單地賦值給一
個變數名稱來創建一個變數。例如,你可以按下述方式來隱式聲明變數:DaysLeft = 100
聲明變數被認為是程式設計的好習慣,因為它使程式可讀性增強並且説明避免某些類型的錯誤。既然你
已經知道了如何聲明變數,我們就來看一下使用它的一個程式:
Sub AgeCalc( )
‘variable declaration (變數聲明)
Dim FullName As String
Dim DateOfBirth As Date
Dim Age As Integer
'assign values to variables (賦值給變數)
FullName = "John Smith"
DateOfBirth = #01/03/1967#
'calculate age (計算年齡)
Age = Year(Now())-Year(DateOfBirth)

'print results to the Immediate window (在立即視窗裡列印結果)


Debug.Print FullName & " is " & Age & " years old."

End Sub
(譯者:Debug 是非常好的工具,它讓物件在運行時將結果在立即視窗上顯示)
變數在程式的開始部分就被聲明了,從那裡開始,它們就可以使用了。在上面的過程裡,每個變數
聲明在分開的行。如果你想,你也可以同時在一行裡聲明好幾個變數,用逗號分開每個變數,例如:
Dim FullName As String, DateOfBirth As Date, Age As Integer

注意,關鍵字 Dim 只在變數聲明行的開頭出現了一次。


當 VB 執行變數聲明語句時,它產生了有確切名稱的變數,並且佔用記憶體空間來儲存它們的值,然後,
明確的值被賦給這些變數。如何給變數賦值?變數名稱,之後是一個等號,等號的右邊是你希望用
該變數儲存的資料。這裡你輸入的資料必須是該變數聲明的資料類型。文本資料應該使用引號包括
起來,而日期需要用井號#包括起來。VB 使用 DateOfBirth 提供的資料來計算員工的年齡,並且將計
算結果儲存到 Age 這個變數。員工的姓名和年齡通過指令 Debug.Print 列印到立即視窗。當程式運行
71
結束後,你必須打開立即視窗來查看結果。
我們來看看你聲明了錯誤的資料類型,會發生什麼情況。下面的過程是計算一個工作表裡的總單元
格數目,並且將結果使用一個對話方塊顯示給使用者。
Sub HowManyCells( )
Dim NumOfCells As Integer
NumOfCells = Cells.Count
MsgBox "The worksheet has " & NumOfCells & " cells."

End Sub
錯誤的資料類型會導致錯誤。在上面的過程裡,當 VB 嘗試將 Cells.Count 語句的結果賦給變數
NumOfCells 時失敗,Excel 顯示資訊“執行時間錯誤 6——溢出”。這個錯誤的產生原因時變數的無
效資料類型,工作表裡儲存格總數目不在 Integer 資料範圍之內。要更正這個問題,你應該選擇一
個可以包含一個大資料的資料類型。然而,要快速地解決上面程式遇到的問題,你只要刪除變數類
型 As Integer 就行了。當你重新運行這個過程時,VB 將給你的變數分配為 Variant 類型,儘管 Variant
比其它變數類型使用更多的記憶體和降低程式運行速度(因為 VB 不得不做額外的工作區檢查變數類
型),但是,如果這是在一個簡短的程式裡,使用 Variant 的代價也是難以覺察的。
技巧 3-6 變數類型是什麼?
通過下述方法,你可以快速地查明你程式裡使用的變數的類型:在變數名稱上按一下右鍵,並且從快
捷功能表上選擇“快速資訊”。
技巧 3-7 串聯
你可以將兩個或多個字串結合成為一個新的字串。這個操作稱為串聯。你已經在 AgeCalc 和
HowManyCellss 過程裡看到了串聯的例子。串聯用&符號在表示。例如,“His name is ” & FirstName
將會產生下述字串:His name is John,或者 His name is Michael。人名取決於變數 FirstName
的內容。注意,在 is 和結束引號之間有一個空格。字串的串聯也可以使用加號(+)來代表,然
而,許多程式師為了消除混淆,寧願將加號限制於數位的運算。
6 明確變數的資料類型
如果你在 Dim 語句裡沒有明確變數的資料類型,你最終將得到沒有歸類的變數。沒有歸類的包括,
在 VBA 裡,總是當成 Variant 資料類型。高度建議你產生歸類了的變數。當你聲明變數為某種資料類
型,你的 VBA 程式會運行得更快一些,因為 VB 不需要停下來分析 Variant 變數到底是什麼類型。
VB 可以使用很多種數位變數。Integer 變數只能保存從–32,768 到 32,767 之間的所有整數。其它類
型的數位變數有 Long,Single,Double 和 Currency。Long 變數可以保存從–2,147,483,648 到
2,147,483,647 範圍的所有整數。與 Integer 和 Long 相反,Single 和 Double 變數可以保存小數。String
變數用來引用文本。當你聲明了一個 String 資料類型的變數時,你最好告訴 VB 這個字串有多長,
例如:
Dim extension As String * 3
聲明變數 extension 字串的長度為 3 個字元。如果你不給它分配一個明確的長度,這個字串變數
將是動態的。這意味著 VB 將會佔用足夠大的電腦記憶體來處理任意容量的文本。聲明了變數後,你只
能保存聲明語句裡顯示的資訊類型。給數位類型的變數賦文本值,或給文本類型變數賦數位值,都
會導致“類型不匹配”的錯誤資訊,或者導致 VB 修正該值。例如,如果你的變數聲明為保存整數,
而你的資料是小數, 那麼 VB 會忽略小數部分而只用資料的整數部分。試驗一下下面的 MyNumber 過
程,看看 VB 是如何修正資料以適合變數資料類型的:
Sub MyNumber()
Dim myNum As Integer
myNum = 23.11
MsgBox myNum
End Sub
如果你不用 Dim 語句聲明變數,你通過在變數名稱後面加上一個特殊字元同樣可以指明該變數的類
型。例如下面,你可以在變數名稱後面附上美元($)符號,來指明變數 FirstName 為字串類型
(String):
Dim FirstName$
72
上面的聲明和 Dim FirstName As String 是一樣的。其它類型的聲明字元列在表 3-2 裡面。
表 3-2 型別宣告字元
資料類型
Integer
Long
Single
Double
Currency
String
字元
%
&
!
#
@
$
注意,型別宣告字元只能用於六種資料類型。將這些字元附在變數名稱後面就可以使用這些類型聲
明字元了。過程 AgeCalc2 示範表 3-2 中型別宣告字元的使用情況:
Sub AgeCalc2()
'variable declaration (變數聲明)
Dim FullName$
DateOfBirth As Date
Dim Age%
'assign values to variables (給變數賦值)
FullName$ = "John Smith"
DateOfBirth = #01/03/1967#
'calculate age (計算年齡)
Age% = Year(Now())-Year(DateOfBirth)

'print results to the Immediate window (在立即窗口裡輸出結果)


Debug.Print FullName$ & " is " & Age% & " years old."

End Sub
技巧 3-8 聲明變數類型
變數類型可以用 As 後面的關鍵字來標示,也可以用後面附加的類型符號來標示。如果你既不加類型
符號也不使用 As 命令,那麼這個變數將為預設的類型,那就是 VBA 中的 Variant 類型。
7 變數賦值
既然你已經知道如何命名和聲明變數了,是時候開始使用它們了。我們以學習如何創建變數開始。
在 VB 中,你可以在你程式的任何地方創建變數,只有給它賦個值就行。
1. 打開一個新工作簿並且保存為 Chap03.xls
2. 啟動 VB 編輯器視窗
3. 在工程流覽器視窗,選擇這個新的工程並在屬性視窗裡將它的名稱改為 Chap03
4. 選擇“插入”-“模組”在工程 Chap03 裡面添加一個新模組
5. 在屬性視窗將該模組名 Module1 改為 Variables
6. 在代碼視窗,輸入 CalcCost 過程,如下面所示。這個過程基於下述假設來計算購買一個計算器
的價錢:計算器的價格為 35 美元,銷售稅為 8.5%
Sub CalcCost()
slsPrice = 35
slsTax = 0.085
Range("A1").Formula = "The cost of calculator"

Range("A4").Formula = "Price"
Range("B4").Formula = slsPrice
Range("A5").Formula = "Sales Tax"
Range("A6").Formula = "Cost"
Range("B5").Formula = slsPrice * slsTax

Cost = slsPrice + (slsPrice * slsTax)

With Range("B6")
73
.Formula = Cost
.NumberFormat = "0.00"
End With
strMsg = "The calculator total is " & "$" & Cost & "."

Range("A8").Formula = strMsg
End Sub
過程 CalcCost 使用了四個變數:slsPrice,slsTax,Cost 和 strMsg。因為這些變數都沒有顯式聲明,
所以它們的資料類型都是 Variant。變數 slsPrice 和 slsTax 是在過程的開始時通過給它們賦值而產
生的,變數 Cost 分配的值是下面計算的結果:slsPrice + (slsPrice * slsTax)。價格的計算是使
用變數 slsPrice 和 slsTax 提供的值來進行的。變數 strMsg 將資訊合併為一個文本資訊給使用者,然後
這個資訊是在工作表的一個儲存格裡輸入一個完整的句子。
當你給變數賦值時,需要在變數名稱後面輸入一個等號,等號之後是你要輸入的值。它可以是數位,
公式或者帶引號的文本。賦給變數 slsPrice,slsTax 和 Cost 的值比較容易理解,然而保存在變數
strMsg 的值則有些棘手。我來解釋一下變數 strMsg 的內容吧。
strMsg = "The calculator total is " & "$" & Cost & "."

字串“The calculator total is ”被引號包括起來了,注意,後面的引號前有個空格。


字元&讓你將一個字串附加在另一個字串或者變數的內容後面
在引號裡面的美元符合(“$”)用來表明貨幣類型。因為美元符合是字元,它需要用引號來
包括起來
字元&必須用於每次你要在前面的字串後加新資訊的時候
變數 Cost 是一個預留位置,當過程運行時,計算器的實際價格將顯示在這兒
字元&可以連接任何字串
句號用引號包括起來。當你需要在句子後面加句號時,如果它是在一個變數後面時,你必須單
獨再在後面加上它。
現在來運行它,將游標放在過程 CalcCost 的任何地方,並且選擇“運行”-“運行宏”
技巧 3-9 變數初始化
VB 創建變數的時候就將其初始化了。變數假定為它們的預設值,數字型變數設置為 0,布林型變數
初始化為 False,字串變數設置為空字元(””),已經日期型變數則設置為 1899 年 12 月 30 日
注意,你在運行這個過程時,VB 可能會彈出下面的資訊:“編譯錯誤:變數未定義”。如果這個情況
發生了,點擊確定以關閉這個資訊框。VB 將會選中變數 slsPrice 並且加亮過程名稱 Sub CalcCost,
標題列則顯示“Microsoft Visual Basic-Chap03.xls [中斷]”。VB 中斷模式允許你在繼續之前更
正錯誤。在本書的後面,你將學習如何在中斷模式下解決問題。就現在而言,如果你遇到上面提及
的錯誤時,通過選擇“運行”-“重新設置”來退出中斷模式;接下來,在代碼視窗的上面刪除顯
示在第一行的語句 Option Explicit。Option Explicit 語句意味著在本模組裡使用的所有變數都必
須經過正式聲明。你將在下一節裡學習這個語句。刪除 Option Explicit 語句後,重新運行該過程,
運行後,切換到 Excel 介面,過程運行的結果應該和圖 3-1 一致。
圖 3-1 VBA 過程可以在工作表裡輸入資料並計算結果
儲存格 A8 顯示變數 strMsg 的內容。注意,在儲存格 B6 裡面輸入的價格有兩位小數,而 strMsg 的價格
卻顯示三位元小數。要在儲存格 A8 裡顯示帶兩位元小數的計算器價格,你必須給變數 Cost 設置需要的格
74
式,而不是給該儲存格設置格式。VBA 有專門的函數讓你改變資料格式,你將使用 Format 函數來改
變變數 Cost 的格式。該函數的語法是:
Format(expression, format)
Expression(運算式)是你要設置格式的值或者變數;format(格式)則是你要使用的格式類型。
1. 在 CalcCost 過程裡更改變數 Cost 的計算:
Cost = Format(slsPrice + (slsPrice * slsTax), "0.00")

2. 將 With…End With 代碼塊取代為下述指令:


Range("B6").Formula = Cost
3. 將語句 Range("B5").Formula = slsPrice * slsTax 改為下麵指令:
Range("B5").Formula = Format((slsPrice * slsTax), "0.00")

4. 重新運行修改後的程式
試驗過程 CalcCost 之後,你可能會困惑,為什麼我們要為聲明變數煩惱,如果 VB 自己可以處理未聲
明的變數的話?因為過程 CalcCost 是如此之短,因此你不必擔心 VB 每次使用這些 Variant 變數時會
佔用多少記憶體。然而,在短的過程中,記憶體問題不重要,但是當你輸入變數名稱時,你很可能出現
錯誤。當你第二次使用 Cost 變數時,你忽略了“o”而寫成“Cst”,後果會如何呢?
Range("B6").Formula = Cst
如果你使用了 Tax 在下面的公式中,而沒有用 slsTax,結果你將得到什麼呢?
Cost = Format(slsPrice + (slsPrice * Tax), "0.00")

引入上面提及的錯誤後過程 CalcCost 的結果顯示在圖 3-2。


圖 3-2 變數名稱錯誤導致結果錯誤
注意,在圖 3-2 裡,儲存格 B6 沒有數值,因為 VB 沒有找到變數 Cst 的任務語句。再因為 VB 不知道銷
售稅,顯示的計算器價格為總價(而沒有加稅金,見儲存格 A8)。VB 不會猜測,它只是簡單地做你
告訴它的事情。這帶我們到下一個部分,解釋如何避免這類錯誤。在你繼續之前,確保更正變數 Cst
和 Tax 為 Cost 和 slsTax。
8 強制聲明變數
VB 使用 Option Explicit 語句自動提醒你正式地聲明你的變數,這個語句必須放在每個模組的最上
面。如果你試圖運行一個含有未定義的變數的過程時,Option Explicit 語句會讓 VB 產生一個錯誤
信息。
1. 返回代碼視窗你輸入過程 CalcCost 的地方
2. 在模組的最上面輸入 Option Explicit 並回車,Excel 將該語句顯示為藍色
3. 運行過程 CalcCost,VB 顯示錯誤資訊“編譯錯誤:變數未定義”
4. 點擊確定關閉資訊框。
VB 加亮變數名稱 slsPrice。現在你需要正式聲明這個變數。當你聲明了變數 slsPrice 並再次運
行該過程時,VB 一旦遇到另外一個未聲明的變數時,將再次產生同樣的錯誤。
5. 在 CalcCost 過程的開始部分輸入下述聲明:
'declaration of variables (聲明變數)
Dim slsPrice as Currency
Dim slsTax as Single
75
Dim Cost as Currency
Dim strMsg as String
6. 按下 F5 來運行該過程,修改後的程式顯示如下“
Option Explicit
Sub CalcCost()
'declaration of variables
Dim slsPrice As Currency
Dim slsTax As Single
Dim Cost As Currency
Dim strMsg As String
slsPrice = 35
slsTax = 0.085
Range("A1").Formula = "The cost of calculator"

Range("A4").Formula = "Price"
Range("B4").Formula = slsPrice
Range("A5").Formula = "Sales Tax"
Range("A6").Formula = "Cost"
Range("B5").Formula = Format((slsPrice * slsTax), "0.00")

Cost = Format(slsPrice + (slsPrice * slsTax), "0.00")

With Range("B6").Formula = Cost


strMsg = "The calculator total is " & "$" & Cost & "."

Range("A8").Formula = strMsg
End Sub
在模組上面輸入的 Option Explicit 語句強迫你聲明變數,因為如果你需要聲明變數的話,你必須
每次在添加新模組時輸入 Option Explicit 語句,所以你可以讓 VB 幫你輸入它。按照下述步驟來自
動在新模組裡添加 Option Explicit 語句:
1. 選擇“工具”-“選項”
2. 確保選項對話方塊(編輯器頁)上的“要求變數聲明”被勾選上了
3. 點擊確定關閉對話方塊
從現在開始,每個新模組都會在第一行添加 Option Explicit 語句。如果你要在一起創建的模組裡
強制聲明變數的話,你必須手動輸入 Option Explicit。
技巧 3-10 Option Explicit 更多資訊
Option Explicit 強迫正式(顯式)聲明模組裡的所有變數。使用它的一個重大優點是,輸入錯誤
會在編譯時(VB 試圖將原始程式碼翻譯為可執行代碼)被檢測到。Option Explicit 語句必須在模組裡
的任何過程之前出現。
9 瞭解變數範圍
不同的變數在 VBA 過程裡有不同的影響範圍。Scope(範圍)這個術語定義某個特定的變數在同一個
過程,其它過程,或者其它 VBA 工程裡的可用性。變數在 VBA 裡可以是下面三種級別的範圍:
過程級別範圍
模組層級別範圍
工程級別範圍
10 過程級別(當地)變數
從本章起,你已經知道如何通過關鍵字 Dim 來聲明變數,在關鍵字 Dim 在模組中的位置決定了該變數
的範圍。在 VBA 過程中用 Dim 關鍵字聲明的變數擁有過程級別的範圍,過程級別的變數經常被稱為當
地變數。當地變數只能使用在聲明它的過程裡面。未聲明的變數總是過程級別的變數。在它的範圍
內,變數的名稱必須是唯一的,這意味著你不可以在同一個過程裡使用同樣的名稱來聲明兩個變數。
然而,你可以在不同的過程裡面使用同樣的變數名稱。換句話說,過程 CalcCost 裡可以有 slsTax
變數,同一個模組裡的過程 ExpenseRep 裡頁可以有它自己的變數 slsTax,兩個變數相互獨立。
76
11 模組層級別變數
當地變數有助於節省電腦記憶體,一旦該過程結束,該變數便立即消失,並且電腦釋放該變數佔用的
記憶體空間。然而,在程式設計中,你經常需要變數在本過程結束後仍然在其它過程裡可用,這種情形需
要你改變變數的範圍。你可能就需要定義一個模組級別的變數,而不是過程級別的了。要定義模組
級別的變數的話,你必須將關鍵字 Dim 放在模組表裡任何過程的上面(緊接著在關鍵字 Option
Explicit 的下麵)。例如,將 slsTax 設置為任何 Variables 模組裡的過程都可以使用,按照下述方法
聲明 slsTax 變數:
Option Explicit
Dim slsTax As Single
Sub CalcCost( )
<放置過程指令>
End Sub
在上面的例子裡,關鍵字 Dim 在模組的上面,緊挨著 Option Explicit 語句。你需要另外一個過程來
使用變數 slsTax,這樣你才能查看這是如何工作的。
1. 在代碼視窗裡,將變數聲明行 Dim slsTax As Single 從 Variables 模組的 CalcCost 過程裡剪切,
並且粘貼到該模組的上面 Option Explicit 語句的下面
2. 在 CalcCost 過程的同一個模組裡輸入 ExpenseRep 過程代碼:
Sub ExpenseRep()
Dim slsPrice As Currency
Dim Cost As Currency
slsPrice = 55.99
Cost = slsPrice + (slsPrice * slsTax)

MsgBox slsTax
MsgBox Cost
End Sub
ExpenseRep 過程裡聲明了兩個貨幣類型的變數:slsPrice 和 Cost。slsPrice 變數隨後賦值 55.99,
slsPrice 和 CalcCost 過程裡聲明的變數 slsPrice 是獨立工作的。ExpenseRep 過程計算採購的費用,
該費用包括銷售稅,因為銷售稅和 CalcCost 過程裡使用的是一樣的,所以將 slsTax 變數聲明為模組
級別的變數。VB 執行 CalcCost 過程後,變數 slsTax 的內容等於 0.085。如果 slsTax 是當地變數的話,
隨著 CalcCost 過程的終結,變數 slsTax 的內容將被清空。過程 ExpenseRep 結束時顯示兩個資訊框來
輸出變數 slsTax 和 Cost 的值。當你運行 CalcCost 後,VB 清空除了 slsTax 之外的所有變數的內容,因
為 slsTax 以被定義為模組級別的變數。一旦你試圖通過運行 ExpenseRep 來計算價格,VB 就會找到
slsTax 的值,並且將它用到計算中。
技巧 3-11 私有變數
當你在模組層級別聲明變數時,除了關鍵字 Dim 之外,你還可以使用關鍵字 Private。例如,
Private slsTax As Single
私有變數僅僅在聲明該變數的模組裡的過程中可用。私有變數總是在模組的上面 Option Explicit
語句之後聲明。
技巧 3-12 保持工程級別的變數為私有
為了避免工程級別的變數內容被工程之外使用,你可以在 Option Explicit 語句下面,模組的上面
輸入 Option Private Module,例如:
Option Explicit
Option Private Module
Public slsTax As Single
Sub CalcCost( )
<這裡是過程代碼>
End Sub
12 工程級別變數
模組級別的變數用關鍵字 Public(而不是 Dim)聲明時,擁有工程級別範圍。這意味著它們可以在
77
VBA 任何模組裡使用。當你想要在一個打開的 VBA 工程的所有過程裡使用某個變數時,必須用 Public
關鍵字來聲明它,例如:
Option Explicit
Public slsTax As Single
Sub CalcCost( )
<過程代碼>
End Sub
注意,變數 slsTax 在模組上面以 Public 關鍵字聲明的,它將會在該 VBA 工程裡的任何過程裡都可用。
13 變數的存活期
除了範圍之外,變數還有存活期,變數的存活期決定了該變數能保存它的值有多久。一旦該工程打
開,模組層級別和工程級別的變數就會保留它們的值。然而,如果程式的邏輯需要,VB 能夠重新初始
化這些變數。使用 Dim 語句聲明的當地變數當過程結束時就會丟失值,當地變數的存活期是隨著過
程的運行的,並且它們在程式每次運行的時候可以被重新初始化。VB 允許你通過改變聲明方式延長
當地變數的存活期。
14 瞭解和使用靜態變數
用 Static 關鍵字聲明的變數是特殊的當地變數,靜態變數在過程級別聲明。和那些用關鍵字 Dim 聲
明的當地變數相反,靜態變數在程式已經不在它們的過程裡時仍然不會丟失它們的內容。例如,當
一個帶有靜態變數的 VBA 程序呼叫另外一個過程時,在 VB 執行完被調用的過程語句後返回主調過程
時,靜態變數仍然保留它原來的值。過程 CostOfPurchase 示範靜態變數 allPurchase 的使用:
Sub CostOfPurchase()
'declare variables
Static allPurchase
Dim newPurchase As String
Dim purchCost As Single
newPurchase = InputBox("Enter the cost of a purchase:")

purchCost = CSng(newPurchase)
allPurchase = allPurchase + purchCost

'display results
MsgBox "The cost of a new purchase is: " & newPurchase

MsgBox "The running cost is: " & allPurchase

End Sub
上面的過程以一個名為 allPurchase 的靜態變數和兩個當地變數 newPurchase 和 purchCost 的聲明開
始。該過程中使用的 InputBox 函數顯示一個對話方塊並且等著用戶輸入數值,一旦用戶輸入數值並且
點擊確定後,VB 就會將該數值賦給變數 newPurchase。在第四章中有 InputBox 函數的討論。因為
InputBox 函數的結果總是字串,變數 newOurchase 被聲明為字串資料型別了。然而,你不能在
數學計算中使用字串,這就是為什麼需要在下一指令中使用一個類型轉換函數(CSng)來將字元
串值轉換為單精確度浮點類型的數位。函數 CSng 只需要一個引數——你要轉換的數值。函數 CSng
轉換的數位結果保存在變數 purchCost 上。
技巧 3-13 類型轉換函數
在 CSng 上的任意地方按下 F1,可以查看更多關於函數 CSng 的資訊(也可以查看其它類型轉換函數信
息)
下一行指令:allPurchase = allPurchase + purchCost,將 InputBox 函數提供的新數值加和到目
前的採購數值上。當你第一次運行這個過程的時候,變數 allPurchase 和變數 purchCost 的內容是一
樣的;當你第二次運行它的時候,這個靜態變數的值由對話方塊提供的值增加了。你可以隨意多次運
行過程 CostOfPurchase,只要該工程是開著的,變數 allPurchase 就會不斷的變化。依照下述步驟
來試驗該過程:
1. 將游標放在過程 CostOfPurchase 裡的任意地方並且按下 F5
2. 當對話方塊出現時,輸入一個數字,例如,輸入 100 然後回車。VB 顯示資訊““The cost of a new
purchase is: 100.”
78
3. 點擊確定,VB 顯示第二個資訊“The running cost is: 100.”
4. 重新運行該程式,當對話方塊出現時,輸入另外一個數字,例如輸入 50 再回車。VB 顯示資訊“The
cost of a new purchase is 50.”
5. 點擊確定,VB 顯示第二個資訊“The running cost is: 150.”
6. 多次運行該程式,看看 VB 是如何追蹤運行的總量的。
15 聲明和使用物件變數
你已經學習的變數是用於儲存資料的,儲存資料是你使用“普通的”變數的主要原因。除了儲存數
據從普通變數之外,還有引用 VB 物件的特殊變數,這些變數稱為物件變數。你在第二章中已經學習
了多種物件,現在,你開始學習如何用物件變數來代表物件。物件變數不儲存資料,它們告訴資料
在哪兒。例如,你可以用物件變數告訴 VB 資料在當前工作表的儲存格 E10,物件變數使定位資料更
容易。編寫 VB 程式時,你經常需要寫一些很長的指令,例如:
Worksheets("Sheet1").Range(Cells(1,1), Cells(10, 5).Select

你可以聲明一個物件變數來告訴 VB 資料在哪兒,而不必使用很長的指令來指向該物件。物件變數的
聲明和你已經學習的變數聲明類似,唯一的不同是在關鍵字 As 後面,你輸入詞語 Object 作為資料類
型,例如:
Dim myRange As Object
上面的語句聲明了一個叫做 myRange 的物件變數。然而,實際上只聲明物件變數是不夠的,在使用
這個變數於程式裡之前,你還給這個物件變數賦上確定的值。使用關鍵字 Set 來給物件變數賦值,
關鍵字 Set 後面是等號,再後面是該變數指向的值,例如:
Set myRange = Worksheets("Sheet1").Range(Cells(1,1), Cells(10, 5))

上面的語句給物件變數 myRange 賦值,這個值指向工作表 Sheet1 的儲存格區域 A1:E10。如果你忽略


了關鍵字 Set,VB 將會顯示一個錯誤資訊——“執行時間錯誤 91:物件變數或 With 塊變數未設置”。
現在,又是來看看實例的時候了,下面的過程 UseObjVariable 示範一個叫 myRange 的物件變數的使
用:
Sub UseObjVariable()
Dim myRange As Object
Set myRange = Worksheets("Sheet1"). _

Range(Cells(1, 1), Cells(10, 5))


myRange.BorderAround Weight:=xlMedium
With myRange.Interior
.ColorIndex = 6
.Pattern = xlSolid
End With
Set myRange = Worksheets("Sheet1"). _

Range(Cells(12, 5), Cells(12, 10))


myRange.Value = 54
Debug.Print IsObject(myRange)
End Sub
我們來逐行分析一下過程 UseObjVariable 裡的代碼。過程開始的時候是物件變數 myRange 的聲明,
下一行將物件變數設置未 Sheet1 的區域 A1:E10。從現在開始,你每次要引用這個區域時,你不需要
寫下整個位址,而只要使用這個捷徑——該物件變數名稱就可以了。這個過程的目的是在區域
A1:E10 週邊設置邊框,你不必使用下面這樣冗長的指令:
Worksheets("Sheet1").Range(Cells(1, 1), _

Cells(10, 5)).BorderAround Weight:=xlMedium

而可以使用一個捷徑,使用物件變數名稱:
myRange.BorderAround Weight:=xlMedium

下一節語句是將選區 A1:E10 設置底色。同樣,你不需要使用你要操作的物件的冗長位址,你可以使


用簡單的物件變數名稱 myRange。下一句代碼是給物件變數 myRange 分配一個新的引用區域,VB 將忘
記老的引用,你下次使用 myRange 時,它會引用另一個區域 E12:J12。在新區域(E12:J12)輸入了
79
54 後,過程給你顯示如何確定某個變數時物件類型。如果 myRange 是物件變數的話,指令 Debug.Print
IsObject(myRange)將在立即視窗裡面輸入 True。IsObject 是個 VBA 中指明某變數是否是物件變數的
函數。
技巧 3-14 使用物件變數的好處
使用物件變數的好處有:
它們可以代替真實物件使用
它們比真實物件更短更容易記住
當過程運行時,你可以改變它們的意義
16 使用明確的物件變數
物件變數可以引用任意一種物件,因為 VB 有很多種物件,所以,要讓你的程式可讀性更強,運行更
快,最好創建引用到具體物件類型的物件變數。例如,在過程 UseObjVariable 中,你可以將 myRange
物件變數聲明為 Range 物件,而不是通常的物件變數(Object):
Dim myRange As Range
如果你要引用一個具體的工作表,你可以聲明 Worksheet 物件:
Dim mySheet As Worksheet
Set mySheet = Worksheets("Marketing")

當物件變數不再需要時,你可以給它賦值 Nothing,這將釋放記憶體和系統資源:
Set mySheet = Nothing
你將在第九章裡看到更多的使用物件變數的例子。
17 查找變數定義
當你在 VBA 過程裡看到一行給變數賦值的指令時,你可以通過選擇該變數名稱並且按下 Shift+F2,
快速地定位到該變數的定義(聲明)。或者,你也可以選擇“視圖”-“定義”,VB 將跳到變數的聲
明行。要回到剛才的位置,只要按下 Ctrl+Shift+F2 或選擇“視圖”-“最後位置”。我們來試試:
1. 定位到過程 CostOfPurchase 的代碼裡
2. 定位到語句 purchCost = CSng(newPurchase)
3. 在變數名稱上按一下右鍵,並在快顯功能表上選擇“定義”
4. 通過按 Ctrl+Shift+F2 返回剛才位置
5. 試試在其它過程的其它變數上查找定義,每次使用不同的方法跳到聲明位置。
技巧 3-15 這個變數是什麼類型?
你可以使用一個 VB 內置函數來查明變數的類型。參見第四章中使用函數 VarType 的例子。
18 在 VB 過程裡面使用常量
當你的程式運行,變數的內容是可以變化的,如果你想要一次又一次地引用不變的值,那麼你應該
使用常量。常量就像一個指定的變數一樣,總是引用這個相同的值。VB 要求你在使用前要聲明常量。
正如下述例子,使用 Const 語句來聲明常量:
Const dialogName = "Enter Data" As String

Const slsTax = 8.5


Const ColorIdx = 3
常量,象變數一樣擁有範圍。要讓常量僅在一個過程裡可用,將它聲明為過程級別即可,例如:
Sub WedAnniv( )
Const Age As Integer = 25
<place procedure instructions here>

End Sub
如果你想要某個常量在一個模組的所有過程中都可用,則在 Const 語句前加上關鍵字 Private 就可
(譯者:寫在所有過程之上),例如:
Private Const dsk = "B:" As String
私有常量必須在模組的上面,第一個 Sub 語句之上聲明。
如果你要創建一個該工作簿所有模組都可用的常量時,在 Const 語句之前加上 Public 關鍵字就可以
了,例如:
80
Public Const NumOfChar = 255 As Integer

公共常量必須在模組的上面,第一個 Sub 語句之上聲明。


聲明常量的時候,你可以使用下列資料類型之一:Boolean,Byte,Integer,Long,Currency,Single,
Double,Date,String 或者 Variant。
象變數一樣,多個常量也可以在一行裡聲明,例如:
Const Age As Integer = 25, City As String = "Denver", PayCheck As Currency = 350

使用常量可以使你的 VBA 過程可讀性強,容易維護。例如,你在程式裡多次引用某個特定值,就可


以使用常量,而不是這個值本身。這樣,如果以後這個值變了(例如銷售稅率上升了),你只要簡
單地在 Const 語句裡改變這個常量的聲明就可以了,而不必追蹤該值所有發生的地方。
19 內置常量
Excel 和 VBA 都有一長列的預先定義的常量,並且不需要聲明,這些內置常量可以通過物件流覽器查
找,我們已經在第二章裡詳細討論了物件流覽器。我們來打開物件流覽器並查找 Excel 常量清單:
1. 在 VB 編輯器視窗,選擇“視圖”-“物件流覽器”
2. 在“工程/庫”下拉清單裡選擇 Excel
3. 在搜索文字方塊裡輸入“Constants”並回車,VB 顯示搜索結果在“搜索結果”區域
4. 在“類”清單方塊拉下捲軸,選擇“Constants”(參見圖 3-3)。物件流覽器右邊區域顯示所
有 Excel 物件程式庫裡可用的內置常量。注意,所有常量的名稱以首碼“xl”開頭。
圖 3-3 使用物件流覽器查找內置常量
5. 要查找 VBA 常量,在工程/庫文字方塊裡輸入 VBA。注意,所有 VBA 的內置常量以首碼“vb”開頭.
學習內置常量的最好方法是使用宏錄製器,我們來花上幾分鐘來錄製最小化當前視窗的過程:
1. 在 Excel 視窗,選擇“工具”-“宏”—“錄製新宏”
2. 輸入 MiniWindow 作為宏名,選擇“當前工作簿”作為保存宏的地方,然後確定
3. 點擊最小化按鈕,確保你最小化了本檔的視窗,而不是 Excel 應用程式視窗
4. 點擊“停止錄製”按鈕
5. 最大化該被最小化了的視窗
6. 切換到 VB 編輯器視窗,並且按兩下模組資料夾。代碼顯示如下:
Sub MiniWindow( )
ActiveWindow.WindowState = xlMinimized

End Sub
你有時可能會看到 VBA 過程使用數值,而不是內置常量名稱,例如,常量 xlMaximized 的實際數值是
-4137,常量 xlMinimized 的值是-4140,以及常量 xlNormal 的值為-4143(參見圖 3-4)
81
圖 3-4 你可以在物件流覽器裡面選擇常量名稱然後在下面的視窗裡看到它的實際值
20 接下來……
本章介紹了幾個 VBA 概念,包括資料類型,變數和常量。你學習了如何聲明各種變數,也看到變數
和常量之間的區別。既然你知道什麼是變數,也知道如何使用它們,你就可以創建使用比前兩章更
有意義的方法運算元據的過程了。在下一章中,你將通過使用帶有引數和函數的過程來擴展你的
VBA 知識,另外,你還將學習函數,讓你的 VBA 過程與用戶進行交流。
第四章 VBA 過程:副程式和函數
作者:Julitta Korol 翻譯:Tiger Chen Jan 16’ 2005
在第二章中,你知道了過程是一組指令,它讓你在程式運行的時候完成一些具體的任務。VBA 有以
下三種過程:
副程式過程(副程式)執行一些有用的任務但是不返回任何值。它們以關鍵字 Sub 開頭和關鍵
字 End Sub 結束。副程式可以用巨集錄製器(第一章)錄製或者在 VB 編輯器視窗裡直接編寫(見
第二章和第三章)。在你第一章裡已經學習了多種運行這種過程的方法。
函數過程(函數)執行具體任務並返回值。它們以關鍵字 Function 開頭和關鍵字 End Function
結束。在本章中,你將創建你的第一個函數過程。函數過程可以從副程式裡執行,也可以從工
作表裡訪問,就像 Excel 的內置函數一樣。
屬性過程用於自訂物件。使用屬性過程你可以設置和獲取物件屬性的值,或者設置對另外一
個對象的引用。你將在第十一章中學習如何創建自訂物件和使用屬性過程。
在本章中,你將學習如何創建和執行自訂函數,另外,你將發現變數(見第三章)如何用於傳遞
資料給副程式和函數。在本章後面,你將對 VBA 中兩種最有用的函數:MsgBox 和 InputBox 進行比較
徹底的瞭解。
1.關於函數過程
使用 Excel 幾百種內置函數,你可以進行非常寬廣的自動計算,然而,你總有要做個自訂計算的
時候。使用 VBA 程式設計,你可以通過創建函數過程快速的完成這個特殊需求,你可以創建任何 Excel
沒有提供的函數。
2.創建函數過程
象 Excel 函數一樣,函數過程進行計算並返回數值。學習函數的最好方法就是自己創建一個。因此,
我們開始吧。設置完一個新的 VBA 工程後,你將創建一個函數過程來加和兩個數值。
1. 打開一個新 Excel 工作簿,並保存為 Chap04.xls
2. 切換到 VB 編輯器視窗並且選擇 VBAProject(Chap04.xls)
3. 在屬性視窗,將 VBAProject 改為 MyFunctions
4. 在工程流覽器視窗選擇 MyFunctions(Chap04.xls),然後選擇“插入”-“模組”
5. 在屬性視窗將“模組 1”改為 Sample1
82
6. 在工程流覽器視窗,點擊 Sample1 並選擇“插入”-“過程”(譯者:需要啟動右邊的代碼視窗)。
添加過程對話方塊如圖 4-1 所示
圖 4-1 你使用添加過程對話方塊時,VB 自動創建你選擇的過程類型
7. 在對話方塊裡輸入下列設置:
名稱:SumItUp
類型:函數
範圍:公共的
8. 點擊確定退出添加過程對話方塊。VB 輸入了一個空函數過程如下:
Public Function SumItUp()
End Function
第一句聲明函數過程名稱,關鍵字 Public 表面這個函數可以在所有模組的所有過程裡訪問。關鍵字
Public 是可選的。注意,關鍵字 Function 後面是函數名稱(SumItUp)和一對空括弧。在括弧裡你
可以列上計算中需要的資料項目目。每個函數過程都以 End Function 語句結束。
技巧 4-1 關於函數名稱
函數名稱應該點明該函數的作用,並且必須和變數的命名規則一致。
技巧 4-2 設置 VBA 過程範圍
在前幾章你學習了變數的範圍決定它可以在哪些模組和過程裡使用,和變數一樣,VBA 過程也有範
圍。過程的範圍決定其它模組裡的過程是否可以調用該過程。所以的 VBA 過程默認為公共的,這意
味著它可以被任何模組裡的其它程序呼叫。因為過程默認為公共的,所以如果你願意你可以忽略關
鍵字 Public。但是,如果你將 Public 關鍵字換成關鍵字 Private,那麼你的過程只能被同一模組裡
的其它程序呼叫,而不能被其它模組裡的程序呼叫。
9. 將函式宣告修改為這樣:
Public Function SumItUp(m,n)
End Function
這個函數的目的是加和兩個數值。不要將實際值輸給函數。給該函數提供兩個引數以確保該函數
具有靈活性。這樣,你的自訂函數就能夠將你提供的任何兩個數值加和起來了。每個變數代表一
個數值,你在運行該函數時要給每個變數提供數值。
技巧 4-3 使用函數的理由
自訂 VBA 函數可以用於:
分析資料和進行計算
修正資料和彙報資訊
基於提供的或計算的資料採取具體行動
10. 在 Public Function 和 End Function 之間輸入下述語句:
SumItUp = m + n
這條語句意思是將儲存於變數 n 上的資料加在儲存於變數 m 的數值上,並且將結果返回給函數
83
SumItUp。在等號後面輸入該函數名稱,再就是括弧和需要加和的數值。在上面的語句中,設置函
數名稱等於 m + n 的和。完成的自訂函數過程如下:
Public Function SumItUp(m,n)
SumItUp = m + n
End Function
祝賀,你已經創建了你的第一個函數!然而,函數過程並沒有什麼用,除非你知道如何執行它。下
一個段落將給你示範如何使你的函數工作。
3.執行函數過程
在第一章裡,你學習了很多中方法來運行副程式。和副程式不同,函數過程只能使用兩種方法來執
行。你可以用於工作表的公式裡面,或者從另外一個過程裡調用它。你在 VBA 裡創建的函數過程,
不能通過在 Excel 視窗選擇“工具”-“巨集”-“運行巨集”訪問;它們也不能在代碼裡面通過按 F5
鍵來運行。接下來的部分裡,你將學習到執行函數的專門技術。
4.從工作表裡運行函數過程
自訂函數過程和內置函數一樣,如果你不知道確切的函數名稱或者它的引數,那麼你可以使用
“插入函數”對話方塊來説明你在工作表裡輸入需要的函數。
1. 切換到 Excel 視窗,並選擇任何一個儲存格
2. 點擊函數工具列上的“插入函數(fx)”按鈕(譯者:或者選擇“插入”-“函數”),Excel
彈出插入函數對話方塊,上面顯示了所選類別裡所以函數,按字母順序排列
3. 在類別下拉清單裡選擇“全部”或者“使用者定義”,然後滾動函數名稱框,找到並選擇本章中創
建的函數 SumItUp。當你選中這個函數名稱時,在插入函數對話方塊的下部顯示了該函數的語法:
SumItUp(m,n)(譯者:同時可以看到“沒有説明資訊”。請參閱創建自訂函數的相關教材,
如何給自訂函數提供説明資訊)
技巧 4-4 私有函數使用者是看不到的
使用關鍵字 Private 聲明的函數不會出現在“插入函數”對話方塊上,私有函數不能用於公式裡,它
們只能從另一個 VBA 過程裡調用。
技巧 4-5 快速訪問自訂函數
一旦你創建了一個公共的 VBA 函數,Excel 就會將它加入到“插入函數”對話方塊的“使用者定義”的類
別裡。通過選擇這個類別,你可以快速地訪問該自訂函數。
圖 4-2 VBA 自訂函數出現在 Excel 內置函數同一清單上
4. 點擊確定,開始寫公式。“函數參數”對話方塊出現,如下圖所示。對話方塊顯示了函數名稱和每
個參數:m 和 n
84
圖 4-3 公式的調色板功能有助於輸入任何工作表函數,不論是內置或 VBA 程式設計的自訂函數
5. 如圖 4-3 所示輸入引數數值,或者你任意輸入數值。當你輸入數值在參數文字方塊時,Excel
顯示你輸入的數和當前的函數結果。因為兩個參數(m 和 n)都是必須的,所以,如果你忽略了
其中的某個時,函數會返回錯誤。
6. 點擊確定退出函數參數對話方塊,Excel 輸入函數 SumItUp 在所選的儲存格裡,並且顯示它的結果。
要編輯這個公式的話,選擇該顯示公式結果的儲存格,並且點擊“插入函數”按鈕,選擇函數
並點擊確定以訪問函數參數對話方塊。在函數參數 m 和 n 上輸入不同的數值,並點擊確定。也可以
直接在該儲存格上按兩下,修改函數參數值。
技巧 4-6 確保你的自訂函數可用
只有在你儲存該自訂函數的工作簿開啟的時候,你的自訂 VBA 函數才可用,如果你關閉該工作
簿,該函數便不再可用。要確保你的自訂函數每次在你使用 Excel 時都能用到,你可以做下述事
情之一:
保存你的函數在個人巨集工作簿
將含有你的自訂函數的工作簿保存在 XLStart 資料夾裡
創建引用到含有該自訂函數的工作簿(請參見第二章,如何創建對另一個工程的引用)
5.從另外一個 VBA 過程裡運行函數過程
正如前面提到的,你不能在 VB 視窗將游標放在代碼裡並且按 F5 來運行函數,也不能通過選擇“運行”
-“運行巨集”來運行函數過程。要運行函數,你必須從另外一個過程裡調用該函數。要執行自訂
函數,編寫一個 VBA 副程式並且在需要的時候調用該函數。下面的程序呼叫函數 SumItUp 並且將計算
結果輸出在立即窗口:
Sub RunSumItUp()
Dim m As Single, n As Single
m = 370000
n = 3459.77
Debug.Print SumItUp(m,n)
MsgBox "Open the Immediate window to see the result."

End Sub
上面的副程式使用 Dim 語句聲明變數 m 和 n,它們用來給函數提供資料
接下來兩行語句給變數賦值
再下來,VB 調用函數 SumItUp 並且將儲存於變數 m 和 n 的數值傳遞給它。當函數過程裡 SumItUp =
m + n 語句執行完後,VB 返回到子過程 RunSumItUp 裡面,並且使用 Debug.Print 語句將函數的結
果輸出在立即窗口
MsgBox 函數通知使用者在哪裡查看結果
依照下述步驟來試驗上面的例子:
1. 在輸入函數 SumItUp 的同一個模組裡面輸入過程 RunSumItUp
2. 將游標放在該過程的任意地方,按下 F5
85
技巧 4-7 函數的快速測試
你編寫自訂函數後,你可以在立即視窗快速的測試它。打開立即視窗,輸入一個問號(?)在函
數名稱前,可以顯示該函數的計算結果。記住,要在括弧裡輸入函數的參數值。例如,輸入:
? SumItUp(54, 367.24)
然後回車。你的函數使用參數 m 和 n 傳遞的數值進行計算,函數的結果顯示在下一行:
421.24
6.傳遞參數
到目前為止,你已經創建了簡單的可以執行具體任務的 VBA 過程,這些過程在它們運行前沒有要求
你提供額外的資料。然而,在現實生活中,過程(副程式和函數)經常需要參數。參數(引數)
是過程工作時需要的一個或多個數值。參數通常輸入在括弧之間,多個參數用逗號分割開來。使用
Excel 有一陣了,你已經知道 Excel 內置函數根據你提供的資料可能產生不同的結果。例如,如果單
元格 A4 和 A5 分別含有數字 5 和 10,加和公式=SUM(A4:A5)將會返回 15,除非你更改儲存格裡面的數值。
正如你可以傳遞數值給 Excel 內置函數,你也可以傳遞數值給自訂 VBA 過程。現在,我們來看看如
何從副程式傳遞數值給函數 SumItUp。這個自訂函數目的是計算一個人的姓和名的字母數目。
1. 在放置函數 SumItUp 的模組裡輸入下列副程式 NumOfCharacters:
Sub NumOfCharacters()
Dim f As Integer
Dim l As Integer
f = Len(InputBox("Enter first name:"))

l = Len(InputBox("Enter last name:"))

MsgBox SumItUp(f,l)
End Sub
2. 將游標放在過程 NumOfCharacters 的任意地方並按下 F5。VB 將顯示資訊輸入框問你名字,這個
資訊框由下面的函數產生:
InputBox("Enter first name:")
3. 輸入任何名字,回車,VB 接受你輸入的文本並且將它作為一個參數傳遞給函數 Len。函數 Len
計算提供的文本的字母數目。VB 將函數 Len 的結果儲存於變數 f 以供以後使用。這之後,VB 顯示
下一個資訊框,這次是問你的姓。
4. 輸入任何姓,回車。VB 將輸入的姓傳遞給函數 Len 來獲得姓的文本長度,然後數值儲存於變數 l。
接下來發生什麼呢?VB 遇到了函數 MsgBox,這個函數告訴 VB 顯示函數 SumItUp 的結果。然而,
因為這個結果還沒有準備好,VB 很快就跳到函數 SumItUp 裡,使用變數 f 和 l 儲存的數值來做計
算。在函數內部,VB 用變數 f 的值取代參數 m 和變數 l 值取代參數 n。一旦取代工作完成,VB 將兩
個數值加和起來,並且將結果返回給函數 SumItUp。函數 SumItUp 內部沒有其它的任務了,因此,
VB 又馬上返回副程式並且將函數 SumItUp 的結果作為一個參數提供給函數 MsgBox。現在這個信
息出現在螢幕上,顯示了字母總數目。
5. 點擊確定退出資訊框,你可以多次運行過程 NumOfCharacters,每次輸入不同的姓名。
我們來看看另外一個使用變數傳遞參數的例子:
1. 在工程 MyFunctions (Chap04.xls)裡添加一個新模組,並重命名為 Sample2
2. 啟動模組 Sample2 並且輸入副程式 EnterText:
Sub EnterText()
Dim m As String, n As String, r As String

m = InputBox ("Enter your first name:")

n = InputBox("Enter your last name:")

r = JoinText(m, n)
MsgBox r
End Sub
3. 輸入下述函數過程:
Function JoinText(k,o)
JoinText = k + " " + o
86
End Function
4. 運行過程 EnterText
VB 執行語句時,它收集使用者輸入的資料並且將這些資料儲存在變數 m 和 n 上,然後傳遞這些資料到函
數 JoinText。VB 將這些變數的值取代函數 JoinText 的參數,並且將結果賦到函數名稱(JoinText)
上。當 VB 返回過程 EnterText 時,函數的值儲存於變數 r。然後,函數 MsgBox 在資訊框裡顯示變數 r
的內容。結果就是用戶的完整姓名(姓和名用空格分開)。要從函數傳遞數值給副程式,需要將該
值賦到函數名稱,例如,下面的函數 NumOfDays 將值 7 傳遞到副程式 DaysInAWeek:
Function NumOfDays()
NumOfDays = 7
End Function
Sub DaysInAWeek()
MsgBox "There are " & NumOfDays & " days in a week."

End Sub
技巧 4-8 函數過程不能做什麼
函數過程不能進行任何動作,例如,它們不能在工作表裡做插入,刪除或設置資料格式操作,不能
打開檔,或改變螢幕顯示樣式
7.明確參數類型
在前面的部分,你學習了函數根據它們引數傳遞的值進行計算,當你聲明函數時,你將參數名稱
列在一對括弧裡面。參數名稱就像變數一樣,每個參數名稱在函式呼叫時,引用你提供的數值。當
副程式調用函數過程時,它以變數形式傳遞參數。一旦函數做點什麼,它就將結果賦給函數名稱。
注意,函數過程的名稱當作變數來使用。
象變數一樣,函數也有類型,函數的結果可以是字串型,整型,長整型,等。要明確你函數的結
果類型,只有在函式宣告行後添加關鍵字 As 和你想要的類型即可。例如:
Function MultiplyIt(num1, num2) As Integer

如果你不明確資料類型,VB 將把你的函數結果設置為預設類型(Variant 資料類型)。當你明確你的


函數結果的資料類型時,就象你明確變數的資料類型那樣有一些好處,你的程式將更有效地使用內
存,因此它運行得也更快些。
我們來看看這個例子,儘管其參數在主調過程聲明的是單精確度浮點型,但是函數仍然返回整型資料:
1. 在工程 MyFunctions (Chap04.xls)裡添加一個新模組,重命名為 Sample3
2. 啟動模組 Sample3 並且輸入副程式 HowMuch,如下所示:
Sub HowMuch()
Dim num1 As Single
Dim num2 As Single
Dim result As Single
num1 = 45.33
num2 = 19.24
result = MultiplyIt(num1, num2)
MsgBox result
End Sub
3. 在副程式 HowMuch 下面輸入下列函數過程:
Function MultiplyIt(num1,num2) As Integer

MultiplyIt = num1 * num2


End Function
因為儲存於變數 num1 和 num2 的數值不是整數,要確保它們相乘後的結果為整數,你就得將函數結果
設置為整型。如果你不給函數 MultiplyIt 的結果設置資料類型,過程 HowMuch 將會將結果按變數
result 聲明的資料類型顯示,相乘的結果將是 872.1492,而不是 872。
你可以在每次運行該過程時,提供它們不同的數值,來使得該函數更有用,你可以使用 InputBox
函數來輸入資料,而不拘泥於程式碼給定的數值。你自己可以依照前面章節中的副程式
EnterText,花幾分鐘來修改過程 HowMuch。
87
8.按地址和按值傳遞參數
在一些過程中,當你將參數作為變數傳遞時,VB 可能突然改變該變數的數值。要確保該被調函數不
改變傳遞的參數值,你應該在函式宣告行在參數名稱之前加上關鍵字 ByVal。我們來看看這個例子:
1. 在工程 MyFunctions (Chap04.xls)裡添加一新模組,命名為 Sample4
2. 啟動模組 Sample4 並輸入下列代碼:
Sub ThreeNumbers()
Dim num1 As Integer, num2 As Integer, num3 As Integer

num1 = 10
num2 = 20
num3 = 30
MsgBox MyAverage(num1,num2,num3)
MsgBox num1
MsgBox num2
MsgBox num3
End Sub
Function MyAverage(ByVal num1, ByVal num2, ByVal num3)

num1 = num1 + 1
MyAverage = (num1 + num2 + num3) / 3

End Function
使用關鍵字 ByVal 在參數名稱前,可以防止函數改變參數值。副程式 ThreeNumbers 給三個變數賦值,
再調用函數 MyAverage 來計算,最後計算該三個變數的平均值。函數的參數就是變數 num1,num2 和
num3。注意,所有變數的前面都有關鍵字 ByVal。同時,注意,在計算均值之前,函數 MyAverage
改變了變數 num1 的值,在函數內部,變數 num1 等於 11(10+1),因此,當函數將計算的均值傳遞給
過程 ThreeNumbers 時,函數 MsgBox 顯示的結果是 20.3333333333333 而不是期望的 20,接下來的三個
函數顯示每個變數的內容,變數儲存的內容和它們開始被賦的值一致——10,20,30。
如果你在函式宣告行忽略了參數 num1 前面的關鍵字 ByVal,結果會怎樣呢?函數的結果仍然相同,
但是函數 MsgBox 顯示的變數 num1 的內容現在是 11 了。函數 MyAverage 不但返回了出乎意料的結果
(20.3333333333333 而非 20),而且改變了儲存在變數 num1 裡的原始數值。要避免 VB 永久地改變提
供給函數的數值,就得使用關鍵字 ByVal。
技巧 4-9 瞭解你的關鍵字:ByRef 和 ByVal
因為每個要傳遞給函數過程(或副程式)的變數,都可能在接收時改變數值,所以知道如何來保護
變數的原始數值是非常重要的。VB 有兩個關鍵字,提供或者否認改變變數內容的允許——ByRef 和
ByVal。VB 預設地按位址(關鍵字 ByRef)給函數過程(或副程式)傳遞資訊,引用函數被調用時,
函數參數明確的資料。因此,如果函數改變了參數值,原始的數值就被改變了。如果你在函數
MyAverage 聲明參數 num1 的前面忽略了關鍵字 ByVal 時,你就會得到這種結果。如果你想要函數過程
改變原始數值,你不必專門在參數前加關鍵字 ByRef,因為,變數數值的傳遞預設就是 ByRef。當你
在參數名稱前使用關鍵字 ByVal 時,VB 按值傳遞參數,這意味著 VB 複製一份原始資料,然後將複製
值傳遞給函數,如果函數改變了參數的數值的話,原始資料依然不會變——只有複製值變化。這就
是為什麼函數 MyAverage 改變了變數 num1 的數值,而它的原始值還保持不變。
9.使用可選的參數
有時候,你也許要給函數提供額外的參數,例如,你有一個計算每個人膳食的函數。然而,有時你
不希望函數進行相同的計算。在參數名稱前面加上關鍵字 Optional 可以指明該參數不是必須的。可
選參數在必須的參數之後,列在參數清單的最後;可選參數總是 Variant 資料類型,這意味著你不
能使用關鍵字 As 來明確可選參數的類型。在前面部分,你創建了一個計算三個數值的平均值的函數,
假設,你有時只想要計算兩個數的均值,你就可以將第三個參數設置為可選的。為了不破壞原來的
函數 MyAverage,我們來創建一個新的函數 Avg,來計算兩個或三個數字的均值:
1. 在工程 MyFunctions (Chap04.xls)裡添加一新模組,命名為 Sample5
2. 啟動模組 Sample5 並且輸入下列函數:
Function Avg(num1, num2, Optional num3)

88
Dim totalNums As Integer
totalNums = 3
If IsMissing(num3) Then
num3 = 0
totalNums = totalNums –1
End If
Avg = (num1+num2+num3)/totalNums
End Function
3. 現在來從立即視窗調用該函數:
?Avg(2,3)
一旦你按下回車,VB 就顯示結果:2.5
?Avg(2,3,5)
這次結果為:3.3333333333333
你已經看到,函數 Avg 允許你計算兩個或三個數字的平均值,你可以決定計算幾個值的平均值。當
你在立即視窗開始輸入函數的參數時,VB 將可選的參數顯示在方括號裡(譯者:方括號裡的參數表
示是可選的)
我們花幾分鐘來分析一下函數 Avg。該函數最多可以用三個參數;參數 num1 和 num2 是必須的,而第
三個參數 num3 是可選的。注意,可選參數以關鍵字 Optional 開頭,可選參數列在參數清單的後面。
因為參數 num1,num2 和 num3 的類型都沒有聲明,VB 對待它們為 Variant。
函數內部,變數 totalNums 聲明為整型,並且初始賦值為 3。因為該函數必須能夠處理兩個或三個數
字的平均值計算,有關很方便的內置函數 IsMissing 在檢查參數的個數。如果第三個參數沒有提供,
函數 IsMissing 的值為 0,同時變數 totalNums 的值被減去 1,因此如果沒有可選參數,totalNums 為 2。
接下來的代碼根據提供的資料計算平均值,並且將結果傳遞到函數名稱。
函數 IsMissing 用來檢測可選參數是否提供,如果第三個參數沒有提供,那麼該函數返回邏輯值
True,如果提供了第三個參數時,則返回 False。在這裡,函數 IsMissing 是和一個做判斷的語句
If…Then 一起使用的(參見第五章有關該語句和 VBA 中其它判斷語句資訊)。如果沒有參數 num3
(IsMissing),那麼(Then)VB 將 num3 設置為 0,並且將變數 totalNums 減去 1(totalNums = totalNums
– 1)。你還能使用別的方法來運行函數 Avg 嗎?使用你自己的方法在工作表裡運行該函數,確保你
使用兩個和三個參數來運行該函數。
技巧 4-10 測試函數過程
可以編寫一個子程式調用自訂函數,並且顯示它的結果,看它是否能得到設計結果。除此之外,
該子過程還應該能顯示參數的原始資料,這樣,你才能很快知道參數數值是否改變了。如果該函數
使用了可選參數,你也需要檢查不使用可選參數時候的情況。
10.定位內置函數
VBA 自帶了很多內置函數,可以在線上說明裡很容易地找到這些函數。在 VB 編輯器視窗選擇“説明”
-“Microsoft Visual Basic 説明”可以訪問所有 VBA 函數按字母順序排序的清單,然後點擊“函
數”,
例如,以 MsgBox 或 InputBox 函數為例。一個程式好的功能之一就是要與用戶互動,當你使用 Excel
時,你通過多種對話方塊與該應用程式交流,當你犯錯的時候,對話方塊會彈出來,並且告訴你有錯誤。
當你編寫你自己的程式時,你也可以將出乎意料的錯誤或某個計算的結果通知用戶,你可以使用函
數 MsgBox 幫助你做這些。到目前為止,你只是看到了這個函數的一些簡單應用,在下面的部分,你
將瞭解如何控制你資訊的外觀;你也將學習如何使用函數 InputBox 從使用者獲得資訊。在我們詳細討
論這些函數之前,我們來看一個 VBA 函數,在你已經很熟悉變數和它們的類型的時候,它對你特別
有用處。
VB 有個 VarType 函數,它返回一個值變數類型的整數。圖 4-4 例顯示了函數 VarType 的語法和它返回
的值。
89
圖 4-4 使用內置函數 VarType,你可以判別變數的類型(譯者:本截圖與 2002 版本有區別)
現在,看看如何在立即視窗裡使用這個函數:
1. 打開立即視窗
2. 輸入下列給變數賦值的語句
age = 18
birthdate = #1/1/1981#
firstName = "John"
3. 現在詢問 VB 每個變數的資料類型是什麼:
?varType(age)
你按下回車時,VB 返回 2,如圖 4-4 所示,數位 2 代表整數類型。
?varType(birthdate)
VB 返回 7 代表日期。如果你在變數名稱上犯了個小錯誤(比如說,你輸入了 birthday 而不是
birthdate),VB 將返回 0。
?varType(firstName)
VB 告訴你變數 firstName 的資料是字串(8)。
11.使用 MsgBox 函數
你目前使用的 MsgBox 函數局限於給使用者用一個簡單的,一個按鈕的對話方塊顯示資訊。你點擊確定按
鈕或者回車來關閉該資訊框。要創建一個簡單的資訊框,只要在 MsgBox 函數名稱後面帶上一個用引
號包括起來的文本就可以了。換句話說,要顯示資訊“過程已完成”,你應該準備下列語句:
MsgBox "過程已完成" ‘(注意,英文狀態的引號)
你可以將它輸入立即視窗,快速地測試上面的指令,當你輸入完這條指令並且回車後, VB 就顯示如
90
圖 4-5 的信息框。
圖 4-5 將文本作為 MsgBox 函數的參數,來給使用者顯示資訊
MsgBox 函數允許你使用其它參數,使你可能決定可用的按鈕數目,或者將預設的資訊框的標題
(Microsoft Excel)改為你自己的標題。也可以設置你自己的說明主題。MsgBox 的語法如下:
MsgBox (prompt [, buttons] [, title], [, helpfile, context])

注意,MsgBox 函數有五個參數,只有第一個,Prompt(提示),是必須的;這些列在方括號裡面的
參數都是可選的。當你在提示參數輸入一個非常長的文本時,VB 決定如何斷句,使文本適合資訊框
大小。我們在立即視窗裡來做些練習,看不同的文本格式技巧:
1. 在立即視窗輸入以下指令,確保在一行裡輸入整個文本,回車
MsgBox "All done. Now open ""Chap04.xls"" and place an empty disk in the diskette drive.

The following procedure will copy this file to the disk."

一旦回車,VB 顯示資訊框,如圖 4-6


圖 4-6 如果你設置一下文本格式,長資訊看上去將會更吸引人
如果你遇到編譯錯誤,可以點擊確定,然後確定檔案名用雙引號括起來——““Chap04.xls””。
當你的資訊文本特別長時,你可以使用 VBA 函數 Chr 將它分割為好幾行。Chr 函數需要你跟參數,這
個參數是 0 到 255 之間的數位,它返回這個數位代表的字元。例如 Chr(13)返回的是回車(這和按下
回車鍵相同),以及 Chr(10)返回換行字元(這在文本行之間添加空行很有用)。
2. 將上面的指令修改為下述方式:
MsgBox "All done." & Chr(13) & "Now open ""Chap04.xls"" and place" & Chr(13) & "an empty
disk in the diskette drive." & Chr(13) & "The following procedure will copy this file to

the disk."
圖 4-7 通過使用 Chr(13)可以將長文本分割成幾行
你必須將每段文本片斷用引號括起來,內嵌在括弧裡面的文本(顯示狀態)需要再用一對括弧來括
起來,例如““Chap04.xls””。Chr(13)函數指明你希望開始新的一行的地方。字串的連接字
符(&)用來返回連接字串的字元。
在一行輸入及其長的文本的時候,很容易失誤。回想一下,VB 有一個專門的線連續字元(底線_)
幫你將長 VBA 語句分割為幾行,不幸的是,這個線連續符不能在立即視窗使用。
3. 在工程 MyFunctions (Chap04.xls)裡添加一個新模組並命名為 Sample6
4. 啟動模組 Sample6 並且輸入如下所示的副程式 MyMessage,確保在每個線連續符前面加個空格:
Sub MyMessage()
MsgBox "All done." & Chr(13) _
91
& "Now open ""Chap04.xls"" and place" & Chr(13) _

& "an empty disk in the diskette drive." & Chr(13) _

& "The following procedure will copy this file to the disk."

End Sub
你運行過程 MyMessage 時,VB 顯示如圖 4-7 一樣的資訊。正如你看到的,在幾行輸入的文本更具可
讀性,而且代碼更容易維護。你可以在文本行之間添加一下空白行,來增加資訊的可讀性。使用
Chr(13)或 two Chr(10)函數就可以做到,如下列步驟所述。
5. 輸入下面的 MyMessage2 過程:
Sub MyMessage2()
MsgBox "All done." & Chr(10) & Chr(10) _

& "Now open ""Chap04.xls"" and place" & Chr(13) _

& "an empty disk in the diskette drive." & Chr(13)& Chr(13) _

& "The following procedure will copy this file to the disk."

End Sub
圖 4-8 顯示了 MyMessage2 過程產生的資訊框。
圖 4-8 你可以通過在文本行之間添加空行增加資訊的可讀性
既然你已經掌握了文本的格式技術,那麼我們就來仔細地看看 MsgBox 函數的下一個參數吧。儘管按
鈕參數時可選的,但是它的使用還是很頻繁的。這個按鈕參數明確多少個按鈕,並且是什麼樣的按
鈕你想要出現了資訊框上,這個參數可以是個常量(參見表 4-1),也可以是個數位。如果你忽略
這個參數,結果辛苦只會有一個“確定”按鈕,正如你在前面的例子裡看到的那樣。
表 4-1 MsgBox 按鈕參數的設置
常量
按鈕設置
vbOKOnly
vbOKCancel
vbAbortRetryIgnore
vbYesNoCancel
vbYesNo
vbRetryCancel
圖示設置
vbCritical
vbQuestion
vbExclamation
vbInformation
預設按鈕設置
vbDefaultButton1
vbDefaultButton2
vbDefaultButton3
vbDefaultButton4
資訊框形式
vbApplicationModal

0
1
2
3
4
5
16
32
48
64
0
256
512
768
0
描述
僅顯示確定按鈕,這是預設值
顯示確定和取消按鈕
顯示終止,重試和忽略按鈕
顯示是,否和取消按鈕
顯示是和否按鈕
顯示重試和取消按鈕
顯示重要資訊圖示
顯示問號圖示
顯示警告資訊圖示
顯示資訊圖示
第一個按鈕是缺省值
第二個按鈕是缺省值
第三個按鈕是缺省值
第四個按鈕是缺省值
應用程式強制返回;應用程式一直被掛起,直到
92
vbSystemModal
MsgBox 顯示的其它設置
vbMsgBoxHelpButton
vbMsgBoxSetForeground
vbMsgBoxRight
vbMsgBoxRtlReading
4096
16384
65536
524288
1048576
用戶對訊息方塊作出回應才繼續工作。
系統強制返回;全部應用程式都被掛起,直到用
戶對訊息方塊作出回應才繼續工作。
將 Help 按鈕添加到訊息方塊
指定訊息方塊視窗作為前景視窗
文本為右對齊
指定文本應為在希伯來和阿拉伯語系統中的從右
到左顯示
你什麼時候應該使用按鈕參數呢?假設你要用戶對一個問題回到“是”或“否”,你的信息框就需
要兩個按鈕,當資訊框有一個以上的按鈕時,就需要將其中一個設置為缺省值,當用戶回車的時候,
這個預設的按鈕就會自動地被選上。
因為,你可以顯示各種各樣的資訊(重要,警告,資訊),所以,你需要通過按鈕參數設置圖形代
表(圖示)來指明資訊的重要性。
除了資訊類型之外,按鈕參數還可以設置是否使用者必須先關閉該資訊框才能切換到另外的應用程
序。很多情況下,使用者需要在對資訊框的問題做出反應之前,切換到另外的程式或者進行另外的操
作。如果這個資訊框是應用程式模式(vbApplication Modal)的話,使用者必須先關閉該資訊框後
才能繼續使用你的應用程式。另一方面,如果你想要在使用者對資訊框回應之前,將所有應用程式掛
起,那麼你必須在按鈕參數里加上系統強制返回設置(vbSystemModal)。按鈕參數的設置分為五組:
按鈕設置,圖示設置,預設按鈕設置,資訊框形式和其它的 MsgBox 顯示設定(參見圖 4-1)。每組
設置裡面只能選一個加入按鈕參數裡面。你可以將每種需要的設置加和起來,來設置按鈕參數,例
如,要顯示一個帶兩個按鈕(“是”和“否”),問號圖示以及將“否”按鈕設置為缺省值的資訊框,
你可以在表 4-1 裡查找相應的值並且加和起來,你應該得到 292(4+32+256)。你可以在立即視窗裡
面輸入下列代碼,快速查看使用該計算的按鈕參數的資訊框:
MsgBox "Do you want to proceed?", 292

下面顯示的就是資訊框結果。當你直接使用加和起來的值作為參數時,你的程式可讀性就不高了,
因為沒有參考索引表格供你檢查 292 背後的意思。要改善你資訊框函數的可讀性,最好使用常量,
而不要使用它們的值,例如,在立即視窗輸入下列修改後的語句:
MsgBox "Do you want to proceed?", vbYesNo + vbQuestion + vbDefaultButton2

上面的語句得到如圖 4-9 所示的相同結果。


圖 4-9 你可以使用可選的按鈕參數來確定資訊框上的按鈕個數
下面的例子示範如何在 VB 過程裡使用按鈕參數:
1. 在工程 MyFunctions (Chap04.xls)裡添加一新模組,並命名為 Sample7
2. 啟動 Sample7 模組,並且輸入如下子程式 MsgYesNo:
Sub MsgYesNo()
Dim question As String
Dim myButtons As Integer
question = "是否要打開一個新工作簿?"
myButtons = vbYesNo + vbQuestion + vbDefaultButton2

MsgBox question, myButtons


End Sub
在上面的副程式裡, 變數 question 儲存了你的資訊文本, 而按鈕參數的設
置則儲存於變數
myButtons。除了使用常量名稱之外,你還可以使用它們的值,例如下面的:
93
myButtons = 4 + 32 + 256
但是,明確了按鈕常數的常量名稱的話,你可以使你的程式對你自己以及將來可能要使用該程式的
人來說更容易理解。變數 question 和 myButtons 用作 MsgBox 函數的參數。運行該程式後,你將看到
如圖 4-9 所示的結果。注意,現在按鈕“否”是被選中的,它是該對話方塊的預設按鈕,如果你按下
回車,Excel 將該資訊框從螢幕上移除,因為 MsgBox 函數後面沒有任何指令,所以,不會發生其它
操作。將預設按鈕換成 vbDefaultButton1 設置,可以更改預設按鈕。
MsgBox 函數的第三個參數是標題,雖然這也是個可選參數,但是它很方便,因為當你忽略它們(默
認為 Microsoft Excel)時,就不能給程式提供可視提示。你可以使用這個參數,將標題列設置為
你想要的任何文字。假設你要過程 MsgYesNo 顯示標題為“新工作簿”,下面的過程 MsgYesNo2 示範如
何使用標題參數:
Sub MsgYesNo2()
Dim question As String
Dim myButtons As Integer
Dim myTitle As String
question = "Do you want to open a new workbook?"

myButtons = vbYesNo + vbQuestion + vbDefaultButton2

myTitle = "New workbook"


MsgBox question, myButtons, myTitle

End Sub
標題參數文本儲存於變數 myTitle。如果你沒有明確標題參數的內容,VB 將預設顯示為“Microsoft
Excel”。注意,參數是基於 MsgBox 函數決定的順序列出的,如果你要按你自己的順序列出這些參數
的話,你就必須將參數名稱一起寫出,例如:
MsgBox title:=myTitle, prompt:=question, buttons:=myButtons

後面兩個參數——幫助檔(helpfile)和上下文(context)——經常為那些對 Windows 環境下的


幫助檔很熟悉的程式師使用。參數 helpfile 指明某個包含你要顯示給使用者的附加資訊的具體説明
檔的名稱,當你明確了這個參數後,Help 按鈕就會在資訊框上顯示出來。當你使用 helpfile 參數
時,你同時也使用 context 參數。這個參數表明在説明檔裡你要顯示的那個說明主題。假設
Help.hlp 是你創建的幫助檔,55 是你要使用的說明主題,你可以按照如下指令來顯示這些資訊於
信息框上:
MsgBox title:=mytitle, _
prompt:=question _
buttons:=mybuttons _
helpFile:= "HelpX.hlp", _
context:=55
上面只是一條 VBA 語句,使用連接子打斷為好幾行。
12.MsgBox 函數的運行值
當你顯示只有一個按鈕的資訊框時,可以點擊確定按鈕或者回車鍵將資訊框從螢幕上移除,然而,
當資訊框有兩個或以上的按鈕時,你的程式需要知道按的是哪個按鈕。你可以將資訊框結果儲存在
一個變數上來實現。表 4-2 顯示了 MsgBox 函數返回值。
表 4-2 MsgBox 函數返回值
選擇的按鈕
OK(確定)
Cancel(取消)
Abort(終止)
Retry(重試)
Ignore(忽略)
Yes(是)
No(否)
常數
VbOK
vbCancel
vbAbort
vbRetry
vbIgnore
vbYes
vbNo

1
2
3
4
5
6
7
94
MsgYesNo3 過程是 MsgYesNo2 過程修改後的版本,示範如何確定用戶按下的是哪個按鈕:
Sub MsgYesNo3()
Dim question As String
Dim myButtons As Integer
Dim myTitle As String
Dim myChoice As Integer
question = "Do you want to open a new workbook?"

myButtons = vbYesNo + vbQuestion + vbDefaultButton2

myTitle = "New workbook"


myChoice = MsgBox(question, myButtons, myTitle)

MsgBox myChoice
End Sub
在上面的過程裡,你將 MsgBox 函數的結果賦給變數 myChoice。注意,現在,MsgBox 函數的參數列在
括弧裡面:
myChoice = MsgBox(question, myButtons, myTitle)

當你運行 MsgYesNo3 時,出現帶有兩個按鈕的資訊框,當你點擊“是”時,MsgBox myChoice 將顯示


數字 6;當點擊“否”則得到數字 7。你將在第五章裡面學習如果讓程式根據按鈕的選擇進行不同的
任務。
技巧 4-11 MsgBox 函數——使用還是不使用括弧?
當你需要使用 MsgBox 函數返回的結果時,需要使用括弧將該函數的參數包括起來。不使用括弧,意
味著你告訴 VB 你將忽略該函數的結果。當 MsgBox 函數包含兩個或以上的按鈕時,你很可能想要使用
該函數的結果。
13.使用 InputBox 函數
InputBox 函數顯示一個資訊提示使用者輸入資料,這個對話方塊有兩個按鈕——“確定”和“取消”,
當你點擊確定時,InputBox 函數返回使用者輸入在資訊框裡的資訊;當你點擊取消時,函數則返回空
字串(” ”)。InputBox 函數的語法顯示如下:
InputBox(prompt [, title] [, default] [, xpos] [, ypos] _ [, helpfile, context])

第一個參數,prompt,是你想要顯示在對話方塊上的資訊,你可以使用函數 Chr(13)或 Chr(10)將長文


本打斷為幾行(參見本章中使用 MsgBox 函數的例子)。剩下所有的參數都是可選的。
第二個參數,title,讓你改變對話方塊的默認標題,默認的標題是 Mictosoft Excel。
InputBox 函數的第三個參數,default,讓你在文字方塊裡顯示一個預設值,如果你忽略這個參數的
話,顯示的將是空白編輯方塊。
接下來的兩個參數,xpos 和 ypos,允許你設置該對話方塊在螢幕上出現的位置,如果你忽略這兩個參
數,對話方塊就會出現了當前視窗的中央,xpos 參數決定對話方塊在螢幕上從左起的水準位置,忽略它
時,對話方塊顯示在水準中央,而 ypos 參數決定對話方塊在螢幕從上而下的豎直位置,忽略它,對話方塊
就在豎直大約三分之一的位置。xpos 和 ypos 都使用一個叫 twips 的專門單位衡量,1twip 大約等於
0.0007 英寸。
最後兩個參數,helpfile 和 context,和在本章前期討論的 MsgBox 函數相應的參數使用方法一樣。
現在你知道了 InputBox 參數的意義了,我們來看看這個函數的使用示例:
1. 在 MyFunctions (Chap04.xls)工程裡添加一個新模組,重命名為 Sample8
2. 啟動 Sample8 模組,並且輸入下列副程式:
Sub Informant()
InputBox prompt:="Enter your place of birth:" & Chr(13) _

& " (e.g., Boston, Great Falls, etc.) "

End Sub
上面的過程顯示一個帶兩個按鈕的對話方塊,輸入提示顯示在兩行裡。象 MsgBox 函數一樣,如果你想
要使用使用者輸入的資料,那麼你應該使用一個變數來儲存該對話方塊結果。下面顯示的副程式
Informant2 將 InputBox 函數的結果賦值給變數 town:
95
圖 4-10 Informant 副程式產生的對話方塊
Sub Informant2()
Dim myPrompt As String
Dim town As String
Const myTitle = "Enter data"
myPrompt = "Enter your place of birth:" & Chr(13) _

& "(e.g., Boston, Great Falls, etc.)"

town = InputBox(myPrompt, myTitle)


MsgBox "You were born in " & town & ".", , "Your response"

End Sub
注意,這次,InputBox 函數的參數列在了括弧中間。如果你需要在稍後的程式中需要使用 InputBox
函數的結果,那麼括弧是必須的。Informant2 副程式使用常數來確定顯示在對話方塊標題上的文本。
因為,這個值在過程執行過程中從始至終都是保持不變的,所以,可以將對話方塊的標題聲明為一個
常量,然而,如果你願意,你也可以使用一變數。
當你運行使用了 InputBox 的過程時,使用該函數的對話方塊總是出現在螢幕的同一位置,你可以按前
面解釋的那樣,提供 xpos 和 ypos 參數來改變對話方塊的位置。
3. 修改過程 Informant2 中的 InputBox 函數,讓對話方塊顯示在螢幕的左上角,例如:
town = InputBox(myPrompt, myTitle, , 1, 200)

注意,參數 myTitle 後面緊跟兩個逗號,第二個逗號是忽略掉的預設值參數。下面兩個參數決定對


話框的水準和豎直位置。如果你忽略了參數 myTitle 後面的第二個逗號,VB 將會使用數位 1 作為預設
值。如果你使用了參數名稱的話,( 例如, prompt:=myPrompt, title:=myTitle,
xpos:=1,
ypos:=200),你就不必記住在每個忽略了參數的地方加逗號了。
如果你輸入了一個數字,而不是一個城鎮的名稱,後果會怎樣?因為,用戶經常會在對話方塊裡輸入
錯誤的資料,所以,你的程式必須驗證使用者輸入的資料是否可以在將來的資料操作裡使用。InputBox
函數本身並沒有提供驗證資料的工具,要驗證用戶的輸入,你必須使用其它的 VBA 指令,這將在下
章裡講述。
14.資料類型轉變
InputBox 函數的結果總是字串,如果使用者輸入的是個數位,使用者輸入的字串值必須轉換成為數
字值之後,才能用於你程式裡的數學計算。VB 轉換資料類型輕而易舉,不過,在早期的 Excel 版本
裡,這是不可能的。
1. 啟動模組 Sample8,並輸入以下過程 AddTwoNums:
Sub AddTwoNums()
Dim myPrompt As String
Dim value1 As String
Const myTitle = "Enter data"
Dim mySum As Single
myPrompt = "Enter a number:"
value1 = InputBox(myPrompt, myTitle, 0)
mySum = value1 + 2
MsgBox mySum & " (" & value1 & " + 2)"

End Sub
96
圖 4-11 要給使用者提示資料的確切類型,你可以在編輯方塊裡提供一個預設值
上面的程式顯示如圖 4-11 所示的對話方塊。注意,這個對話方塊使用了兩個專門的功能,InputBox 函
數的可選參數標題和預設值。該對話方塊顯示了有常量 myTitle 確定的文本字串作為標題,而不是
默認的“Microsoft Excel”。在編輯方塊裡面的 0 提示使用者輸入數位,而不能輸入文本。一旦用戶輸
入了資料並點擊確定時,用戶的輸入就被賦值給了變數 value1
value1 = InputBox(myPrompt, myTitle, 0)

變數 value1 的資料類型是字串,你可以在上面指令的下面加上如下語句,快速地查看它的資料類
型:
MsgBox varType(value1)
當 VB 運行上面的代碼時,將顯示資訊-數位 8,你可以在本章的圖 4-4 裡查看該數位代表字串類
型。
技巧 4-12 定義常量
你可以將標題文本賦值到一個常量上,以確保某個 VBA 程式裡所有的標題列都顯示相同的文本。依
照這個技巧,你可以在多次輸入某標題文本時節省時間。
技巧 4-13 避免類型不匹配錯誤
如果你試圖在 Excel 的早期版本裡(2000 以前版本)運行 AddTwoNums 過程,當 VB 試圖執行下列代碼
行時,你將得到類型不匹配的錯誤:
mysum = value1 + 2
使用內置函數 CSng 將儲存於 value1 的字串轉換為一個單精確度浮點類型的數位,可以避免類型不匹
配錯誤,代碼寫成:
mysum = CSng(value1) + 2
下一行,mySum = value1 + 2,在使用者輸入的資料上加上 2,並且將計算結果賦值給變數 mySum。因
為變數 value1 的資料類型時字串,在使用它計算之前,VB 在後臺進行資料類型的轉換,VB 知道轉
換的需要。沒有它,兩種不相容的資料類型(文本和數位)將會產生類型不匹配錯誤。程式最後是
一個 MsgBox 函數,顯示計算的結果已經給使用者顯示總數是如何組成的。
15.使用 InputBox 方法
除了 InputBox 函數之外,還有 InputBox 方法,如果你啟動物件流覽器,然後搜索“inputbox”,VB
將顯示兩個 InputBox——一種為 Excel 庫,另一種為 VBA 庫(見圖 4-12)。InputBox 方法在 Excel 庫
裡面可用,它的語法和本章前面講的 InputBox 函數的語法有輕微差別,它的語法為:
expression.InputBox(Prompt, [Title], [Default], [Left], [Top], [HelpFile],
[HelpContextID], [Type] )
所有方括弧裡面的參數都是可選的。Prompt(提示)參數是顯示於對話方塊上的資訊;Title 是對話
框的標題;而 Default 是對話方塊上文字方塊裡的初始值。Left 和 Top 參數是明確對話方塊在螢幕上的位置。
這些參數的輸入值的單位是 Point ( 1/72 英寸)。當使用者點擊説明按鈕時
, 參數 HelpFile 和
HelpContextID 明確幫助檔案名稱以及某個明確的說明主題。InputBox 方法的最後一個參數——
Type(類型)明確返回的資料類型。如果你忽略這個參數,InputBox 方法將會返回文本格式。類型
參數的值列在表 4-3 裡。
97
圖 4-12 別忘記使用物件流覽器來搜索 VB 函數和方法
表 4-3 InputBox 方法返回的資料類型

0
1
2
4
8
16
64
返回的資料類型
公式
數字
字串(文本)
邏輯值(True 或 False)
儲存格引用,作為一個 Range 物件
錯誤值,例如#N/A
陣列
如果你使用 3 作為 Type 參數的話,用戶將既可以輸入一個數位也可以輸入一個文本。這個值是將 1
(數位)和 2(字串)加和而得到的。InputBox 方法很適合那些需要使用者選擇工作表儲存格範圍
的 VBA 程式。
1. 關閉物件流覽器,如果你已經打開了的話
2. 在模組 Sample8 裡面,輸入下列過程 WhatRange:
Sub WhatRange()
Dim newRange As Range
Dim tellMe As String
tellMe = "Use the mouse to select a range:"

Set newRange = Application.InputBox(prompt:=tellMe, _

Title:="Range to format", _
Type:=8)
newRange.NumberFormat = "0.00"
newRange.Select
End Sub
過程 WhatRange 開始於一物件變數的聲明——newRange。試回想一下第三章,物件變數指向資料的
地址。使用者選擇的儲存格被賦值給物件變數 newRange。注意變數名稱前面的關鍵字 Set:
Set newRange = Application.InputBox(prompt:=tellMe, _

Title:="Range to format", _
Type:=8)
類型參數(Type:=8)使使用者能夠選擇任何儲存格區域。當使用者選中儲存格區域時,下句指令:
98
newRange.NumberFormat = "0.00"
改變所選儲存格的格式。最後一句選擇使用者加亮的區域。
3. 運行過程 WhatRange。VB 顯示一個對話方塊,提示使用者在工作表裡選擇一個儲存格區域。
4. 使用滑鼠選擇你要的儲存格,當滑鼠在儲存格上拖動時,VB 就會將選擇的區域引用到對話方塊的
編輯方塊裡面。
圖 4-13 使用 Excel 的 InputBox 方法,你可以從使用者處獲得區域位址
5. 你選擇了儲存格後,點擊對話方塊上的確定按鈕,被選擇的區域就已經設置好格式了。要檢查是
否按你的意思設置了,你可以在該區域的任意儲存格裡輸入一個整數,這個數位應該顯示為兩
位小數。
6. 重新運行該過程,並且當出現對話方塊時,點擊取消按鈕。如果你在選擇了一個儲存格或者一個
區域後點擊確定按鈕,過程 WhatRange 將工作正常。不幸地是,當你點擊取消按鈕或 Esc 按鈕,
VB 將顯示一錯誤資訊——“要求物件”。當你點擊錯誤對話方塊上的調試按鈕,VB 就會加亮導致
錯誤的代碼行。因為你不希望在取消對話方塊時選擇任何儲存格,所以你必須想個法子忽略 VB
顯示的這個錯誤。使用一個專門的語句,On Error GoTo 標誌,你就可以繞過錯誤的發生。該
指令的語法如下:
On Error GoTo 標誌
這個指令應該放在變數聲明行的下面。標誌可以是除了 VB 關鍵字之外的任何你想要的詞語。如
果錯誤發生時,VB 就會直接跳到該特別的標誌,如下面步驟 8 所示。
7. 選擇“運行”-“重新設置”以取消正在運行的程式。
8. 將過程 WhatRange 修改為如下所示 WhatRange2:
Sub WhatRange2()
Dim newRange As Range
Dim tellMe As String
On Error GoTo VeryEnd
tellMe = "Use the mouse to select a range:"

99
Set newRange = Application.InputBox(prompt:=tellMe, _

Title:="Range to format", _
Type:=8)
newRange.NumberFormat = "0.00"
newRange.Select
VeryEnd:
End Sub
9. 運行程式 WhatRange2,一旦出現對話方塊時就點擊取消按鈕。注意,這次程式沒有產生錯誤。當
VB 遭遇錯誤時,就會跳到位於程式結尾處的標誌 VeryEnd。位於錯誤和標誌 VeryEnd 之間的語句
被忽略了(原文不當:The statements placed between On Error Goto VeryEnd and the VeryEnd
label are ignored)。你將在第十三章裡面找到更多的誘捕 VBA 程式裡錯誤的例子。
技巧 4-14 副程式和函數:你應該使用哪個?
創建副程式的時候:
需要執行一些動作
需要獲取使用者資訊
需要在螢幕上顯示資訊
創建函數的時候:
需要不只一次的做一些簡單的計算
需要做複雜的計算
需要不只一次地調用相同的指令塊
需要檢查某些表達正確與否
16.使用主過程和子過程
當你大 VBA 程式得越來越大,要很好地維護這麼多的代碼行是很困難的。要讓你的程式容易編寫、
理解和改變,你就應該使用井井有條的結構化程式。如何創建結構化程式?你只要簡單地將大問題
分成一些可以同時執行的小問題就行。在 VBA 中,你可以通過創建一個主過程和一個或多個子過程
來實現它。因為主過程和子過程都是副程式,所以你都可以用關鍵字 Sub 將它們聲明。主過程可以
調用所需的子過程,並且將參數傳遞給它們。它也可以調用函數。下面的例子顯示過程 AboutUser。
該過程要求用戶姓和名,並且將姓和名從全名中分離出來。最後的語句顯示使用者的姓,隨後是逗號
和名。你再讀下去,該過程將被分割成幾個任務,以示範使用主過程,子過程和函數的概念。
Sub AboutUser()
Dim fullName As String
Dim firstName As String
Dim lastName As String
Dim space As Integer
'get input from user 從使用者獲取資訊
fullName = InputBox("Enter first and last name:")

'get first and last name strings 獲得姓和名字串


space = InStr(fullName, " ")
firstName = Left(fullName, space – 1)
lastName = Right(fullName, Len(fullName) – space)
'display last name, first name 顯示姓和名
MsgBox lastName & ", " & firstName
End Sub
過程 AboutUser 可以分割為一些細小的任務。第一個任務便是獲取用戶的全名;下一個任務則需要
你將使用者提供的資料分割為兩個字串:姓和名,這些任務可以交給不同的函數(例如:GetLast
和 GetFirst);最後的任務是顯示重新排列的姓名字串資訊。既然你已經知道了你應該注重於哪
些任務,我們現在就來看看如何完成每個任務。
1. 在你當前的 VBA 工程裡面添加一個模組,並重命名為 Sample9
2. 在 Sample9 模組視窗裡面輸入下列過程 AboutUserMaster:
100
Sub AboutUserMaster()
Dim first As String, last As String, full As String

Call GetUserName(full)
first = GetFirst(full)
last = GetLast(full)
Call DisplayLastFirst(first, last)
End Sub
上面顯示的主過程通過調用適當的副程式和函數來控制程式的主流程。該主過程以變數生命開始,
第一條語句 Call GetUserName (full)調用子過程 GetUserName(見第三步)並且傳遞給一參數——
變數 full 的內容。
因為變數在執行調用語句之前沒有賦與任何值,所以它的值是一個空字串(“ ”)。注意,子過程
的名稱在 Call 之後。儘管你在調用過程時並沒有要求使用關鍵字 Call,但是,你在調用一個需要參
數的過程時就必須使用它。參數列表必須包括在括弧裡面。
3. 輸入下面的 GetUserName 副程式:
Sub GetUserName(fullName As String)

fullName = InputBox("Enter first and last name:")

End Sub
過程 GetUserName 示範了兩個非常重要的 VB 程式設計概念:如何傳遞參數給一副程式以及如何將值從子
程式傳遞回給主調過程。
在主過程(見第二步)中,你調用了過程 GetUserName,並且將其作為一參數傳遞:變數 full。該
變數被參數 fullName 接收,該參數子過程 GetUserName 的 Sub 語句裡聲明了。因為在 VB 調用子過程
GetUserName 的時候,變數 full 包含一空字串,參數 fullName 同樣也接收了這個空字串。當 VB
顯示對話方塊並且獲得使用者的姓名時,這個姓名將賦給參數 fullName。賦給參數的值被傳遞回給子過
程執行後的匹配參數。因此,當 VB 返回主過程時,變數 full 就回包含使用者的姓名。
傳遞給子過程的引數將被其參數接收。注意,參數名稱(fullName)後面緊跟著資料類型的聲明
(As String)。雖然,參數的資料類型必須和相匹配的引數的資料類型一致,但是,不同的名稱
還是可以使用給一個引數和它相應的參數。
技巧 4-15 引數(Arguments)和參數(Parameters)
引數是傳遞給一個子過程的變數,常量或運算式
參數則只是接收值並傳遞給子過程的變數
4. 輸入下述函數 GetFirst:
Function GetFirst(fullName As String)

Dim space As Integer


space = InStr(fullName, " ")
GetFirst = Left(fullName, space - 1)

End Function
主過程中的第二條語句(見第二步)first = GetFirst(full),將變數 full 的值傳遞給函數 GetFirst。
函數的參數 fullName 接收到該值。要從使用者提供的資料裡分出姓和名,你就必須找到姓和名中間的
空格。因此,該函數的開頭是當地變數 space 的聲明,下條語句則使用 VBA 內置函數 InStr 返回字元
串 fullName 裡空格(“ ”)的位置。然後將獲得的數位賦值給變數 space。最後,Left 函數用來提取
字串 fullName 從左到某特定個數(space -1)的字元。名的長度比儲存在變數 space 的值少一個
字元。函數的結果(使用者的名)賦值給函數名。當 VB 返回主過程時,它就將結果放置於變數 first。
5. 輸入下列函數 GetLast:
Function GetLast(fullName As String)

Dim space As Integer


space = InStr(fullName, " ")
GetLast = Right(fullName, Len(fullName) - space)

End Function
主過程裡面的第三條語句(見第二步)last = GetLast(full),將變數 full 的值傳遞給函數 GetLast。
該函數的目的是提取使用者提供的資料中的使用者的姓。函數 GetLast 使用內置函數 Len 來計算字串
101
fullName 的總字元數。函數 Right 提取字串 fullName 從右邊某個特定字元開始(Len(fullName) –
space)的字元。然後,獲得的字串賦值給函數名稱,一旦返回主過程,它就儲存於變數 last。
6. 輸入下述子過程 DisplayLastFirst:
Sub DisplayLastFirst(firstName As String, lastName As String)

MsgBox lastName & ", " & firstName


End Sub
主過程裡面的第四條語句( 見第二步) Call DisplayLastFirst(first, last) ,
調用子過程
DisplayLastFirst 並且將兩個引數: first 和 last 。為了接收這些引數,
子過程
DisplayLastFirst 和兩個相匹配的參數(firstName 和 lastName)一起被聲明了。回想我們前面說
過,不同的名稱可以用在引數和相應的參數上。然後子過程 DisplayLastFirst 顯示使用者的姓,逗
號,和名。
技巧 4-16 使用子過程的好處
維護多個子過程要比維護一個大過程要容易得多
由一個子過程執行的任務可以給好幾個過程使用
每個子過程再放入主過程裡之前就被單獨測試
多個程式師可以負責各自的子過程,這些子過程再構建一個大的程式
17.接下來……
在本章裡,你學習了子過程和函數之間的區別:子過程執行操作;函數返回數值。而且你可以通過
錄製或者輸入來創建子過程,函數則不可以錄製,因為它們可能需要參數,你必須手動輸入。你看
到了從工作表或者其他 VB 程序呼叫的函數實例。
你學習了如何給函數傳遞引數,決定函數結果的資料類型。你在你的 VBA 關鍵字的系統裡增加了
ByVal,ByRef 和 Optional 等關鍵字。你也看到如何將問題分割為更小更簡單的任務,以使你的程式
更容易理解。最後,你學習了子過程如何在參數的幫助下,將數值傳遞回到主調過程。
過了本章後,你應該能夠創建適合你特定需要的你自己的自訂函數了。你應該可以通過使用
MsgBox 和 InputBox 函數輕鬆地和使用者互動。第五張將介紹程式抉擇,你將學習如何基於你提供的情
形結果改變你的 VBA 過程的方向。
第五章基於 VBA 做決定
作者:Julitta Korol 翻譯:Tiger Chen Jan 16’ 2005
我們每天要作成千上萬個決定,有些決定是自發的,我們自動地作出了這些決定,而不需要停下來
去想。其它的決定則需要我們事先兩個或者兩個以上的選擇甚至計畫好幾個任務。VBA 和其它的編
程語言一樣,提供了專門的語句允許你在自己的程式中包含抉擇點。但是,什麼是做決定呢?舉例
說某人問你這個問題:“你喜歡紅色嗎?”想過這個問題之後,你將回答“是”或者“不”。如果你
不確定或者根本就不關心這個問題,你也許就回答“也許”或“可能”。在程式設計中,你必須決斷,
只有“是”或“否”的答案是允許的。在程式設計中,所有的決定都是基於提供的答案作出的。如果答
案是肯定的,程式就會執行某段特定的指令;如果答案是否定的,程式則將執行另外一段指令或者
乾脆就不做任何操作。在本章,你將學習如何使用 VBA 條件陳述式來改變你的程式流向。條件陳述式通
常稱為“控制結構”,因為它們使你能夠控制你的 VBA 過程的走向,跳過某些語句以及“分叉”到程
序的另外一部份去了。
1.關係和邏輯運算子
你在你的 VBA 過程裡面通過使用專門的控制結構裡的條件運算式來做決定。條件運算式是使用關係
運算子(見表 5-1),邏輯運算子(見表 5-2)或者兩者結合的運算式。當 VB 在你程式裡遇到條件表
達式時,它將評估該運算式是對還是錯。
表 5-1 VBA 中的關係運算子
運算子
=
<>
>
描述
等於
不等於
大於
102
<
>=
<=
表 5-2 VBA 中的邏輯運算子
運算子
AND
OR
NOT
2.If…Then 語句
小於
大於等於
小於等於
描述
在採取某行動前,所有的條件都必須為真(TRUE)
在採取某行動前,至少有一個條件為真(TRUE)
用來否定條件。如果該條件為真,NOT 使它為假;如果條件為假,
NOT 使它變真
在 VBA 過程裡面作決定的最簡單的方法就是使用 If…Then 語句。假使你想要基於某個條件選擇一個
行動,那麼你可以使用下述結構:
If 條件 Then 語句
例如,要刪除工作表裡面的空行,首先得確認目前的儲存格為空白的,如果測試的結果為真則繼續刪
除包含本儲存格在內的整行:
If ActiveCell = "" Then Selection.EntireRow.Delete

如果目前的儲存格不是空白的,VB 將忽略關鍵字 Then 後面的語句。


如果條件為真,你有時候可能想要執行好幾個操作,雖然你可以在同一行加上其它的語句,通過冒
號分隔它們,但是如果你使用多行 If…Then 語句,你的代碼將更清晰,如下:
If 條件 Then
語句 1
語句 2
語句 N
End If
例如,當目前的儲存格的數值大於 50 時執行一些操作,你可以編寫如下指令:
If ActiveCell.Value >50 Then
MsgBox "The exact value is " & ActiveCell.Value

Debug.Print ActiveCell.Adress & ": " & ActiveCell.Value

End If
在上面的例子中,如果目前的儲存格數值小於等於 50 的話,那麼在關鍵字 Then 和 End If 之間的語句就
不會執行。注意,If…Then 語句必須以關鍵字 End If 結束。VB 如何作決定呢?它評估在關鍵字 If
和 Then 中間找到的條件。我們來評估一下下面的條件:ActiveCell.Value >50
1. 在一個空白工作表上選擇任意一個儲存格並輸入 50
2. 切換到 VB 編輯器視窗
3. 啟動立即窗口
4. 輸入下述語句,並且按下回車鍵
? ActiveCell.Value >50
回車後,VB 寫下測試結果——false。當測試結果為假時,VB 將不會讀代碼中關鍵字 Then 之後的語
句,它將直接跳過去讀下行代碼,但是,如果沒有其它的代碼行時,程式就將結束。
5. 現在,將運算子改為小於或等於符號,並且讓 VB 評估下述條件:
? ActiveCell.Value <= 50
這次測試返回真(true),並且 VB 跳到關鍵字 Then 後面的語句上。
6. 關閉立即窗口。
既然你已經知道了 VB 如何評估條件的,我們就來在 VBA 過程裡試試 If…Then 語句吧。
1. 打開一個新工作簿並保存為 Chap05.xls
2. 切換到 VB 編輯器視窗,並且將 VBA 工程重命名為 Decisions
3. 插入一新模組並重命名為 IfThen
103
4. 在 IfThen 模組裡,輸入下述過程:
Sub SimpleIfThen()
Dim weeks As String
weeks = InputBox("How many weeks are in a year:", "Quiz")

If weeks<>52 Then MsgBox "Try Again"

End Sub
過程 SimpleIfThen 將使用者的答案儲存於一個名為 weeks 的變數上,然後,該變數的值將會和數值 52
進行比較。如果比較的結果為真(也就是說,變數 weeks 的值不等於 52),VB 就會顯示資訊“Try Again”
5. 運行過程 SimpleIfThen 並且輸入一個除 52 之外的數字
6. 重新運行過程 SimpleIfThen 並輸入數字 52。當你輸入正確的周數,VB 將不會做任何事情,程式
就直接結束了。當使用者猜對時,最好也顯示一資訊。
7. 在關鍵字 End Sub 前的另外一行輸入下述指令:
If weeks = 52 Then MsgBox "Congratulations!"

8. 再次運行過程 SimpleIfThen 並輸入 52。當你輸入了正確的答案,VB 不會執行語句 MsgBox “Try


Again.”。如果提供的條件測試結果為假時,過程的執行結果就是忽略關鍵字 Then 右邊的語句。
回想一下,VBA 過程可以調用另外一個過程,我們來看看它是否可以調用本身
技巧 5-1 If…Then 語句的兩種格式
If…Then 語句有兩種格式——單行和多行。這種短格式適合於可以寫在一行裡的語句,例如:
If secretCode <> 01W01 Then MsgBox “Access denied”
或者:
If secretCode = 01W01 Then alpha=True : beta = False

這裡的 secreCode,alpha 和 beta 是變數名稱。在第一個例子裡,如果變數 secretCode 的值不等於


01W01,那麼 VB 顯示資訊“Access denied”。在第二個例子裡,當變數 secretCode 值等於 01W01 時,
VB 就將變數 alpha 設置為真,變數 beta 為假。注意,執行的第二條語句用冒號與第一條語句分隔開
來。
如果當條件為真需要執行很多語句或將要執行的語句及其長時,多行的 If…Then 語句會更清楚,如
下例所示:
If ActiveSheet.Name = "Sheet1" Then

ActiveSheet.Move after:=Sheets _
(Worksheets.Count)
End If
在這個例子中,VB 將會檢查當前工作表的名稱。如果它是“Sheet1”,條件 ActiveSheet .Name =
“Sheet1”將為真,並且 VB 將繼續執行關鍵字 Then 後面的代碼行。結果,當前工作表將會被移動到
工作簿的最後。
9. 按下面方法修改過程 SimpleIfThen 裡的第一個 If 語句:
If weeks <> 52 Then MsgBox "Try Again" : SimpleIfThen

我們在原來的過程 SimpleIfThen 後面加上一個冒號和 SimpleIfThen 過程名稱。如果用戶輸入了不正


確的答案,他將看到一資訊,並且他一旦點擊確定按鈕後,他將獲得另外一次機會來提供正確的答
案——輸入框將再次出現。用戶將能夠不斷地猜測答案,事實上,他不能適當地推出該過程,直到
他提供了正確的答案。如果他點擊了取消按鈕,他將不得不去處理不友好的錯誤資訊“類型不匹配”。
在上章裡,你看到了如何使用 On Error GoTo 標誌語句來繞過錯誤,至少你在第十三章裡學習更多
的關於錯誤處理的知識之前,你可以那麼做。現在,你可以這樣來修改你的過程 SimpleIfThen:
Sub SimpleIfThen()
Dim weeks As String
On Error GoTo VeryEnd
weeks = InputBox("How many weeks are in a year:", "Quiz")

If weeks<>52 Then MsgBox "Try Again": SimpleIfThen

If weeks=52 Then MsgBox "Congratulations!"

VeryEnd:
End Sub
104
10. 運行幾遍過程 SimpleIfThen,提供一些不正確的答案。你在程式裡面加的錯誤捕捉指令使得用
戶可以推出猜測,而不必面對這可惡的錯誤資訊。
3.基於多於一個條件的決定
在上面段落裡的過程 SimpleIfThen 裡,If…Then 語句僅評估一個條件,然而,這個語句可以使用一
個以上的條件。你必須使用邏輯運算子 AND 和 OR(參見本章前面的表 5-2)來明確 If…Then 語句裡的
多個條件。這裡是使用 AND 運算子的語法:
If 條件 1 AND 條件 2 Then 語句
在上面的語法例,條件 1 和條件 2 都必須為真,VB 才會執行關鍵字 Then 右邊的語句。例如:
If sales = 10000 AND salary <45000 Then SlsCom = Sales * 0.07

在這個例子中,條件 1 sales=10000,條件 2 salary<45000。當 AND 使用在該條件運算式中時,兩個


條件都必須為真,VB 才會計算銷售傭金(SlsCom)。如果兩個條件中只要有一個為假,或者都為假,
VB 將會忽略 Then 後面的語句。如果符合其中一個條件就足夠好時,你就應該使用運算子 OR。這裡是
語法:
If 條件 1 OR 條件 2 Then 語句
運算子 OR 更靈活一些,只要任何一個條件為真時,VB 就會執行關鍵字 Then 後面的語句。我們來看看
這個例子:
If dept = "S" OR dept = "M" Then bonus = 500
在上面的例子裡,如果有個條件為真,VB 就將給變數 bonus 賦值 500。如果兩個條件都為假,那麼 VB
就會忽略該行剩餘的代碼。
現在我們來看個完整的例子。假設如果你採購 50 套產品的話,就可以獲得 10%的折扣,單價為$7.00。
過程 IfThenAnd 示範運算子 AND 的使用。
1. Decisions (Chap05.xls)工程的 IfThen 模組裡輸入下述過程:
Sub IfThenAnd()
Dim price As Single
Dim units As Integer
Dim rebate As Single
Const strmsg1 = "To get a rebate you must buy an additional "

Const strmsg2 = "Price must equal $7.00"

units = Range("B1").Value
price = Range("B2").Value
If price = 7 AND units >= 50 Then
rebate = (price * units) * 0.1
Range("A4").Value = "The rebate is: $" & rebate

End If
If price = 7 AND units < 50 Then
Range("A4").Value = strmsg1 & 50 - units & " unit(s)."

End If
If price <> 7 AND units >= 50 Then
Range("A4").Value = strmsg2
End If
If price <> 7 AND units < 50 Then
Range("A4").Value = "You didn't meet the criteria."

End If
End Sub
上面的過程 IfThenAnd 使用了四個 If…Then 語句來評估兩個變數 price 和 units 的內容。在 If…Then
關鍵字之間的運算子 AND 使得測試多於一個的條件成為可能。使用了 AND 運算子時,所有條件都必須
為真,VB 才會去執行關鍵字 Then 和 End 之間的語句。因為過程的運行依賴於工作表儲存格裡輸入的
資料,所以從 Excel 視窗來運行它比較方便。
2. 切換到 Excel 應用視窗,並且選擇“工具”-“巨集”-“運行巨集”
105
3. 在宏對話方塊裡,選擇 IfThenAnd 並點擊“選項”按鈕
4. 給你的宏設置快速鍵:Ctrl+Shift+I,並且退出宏對話方塊
5. 在工作表裡輸入以下資料:
6. 按下 Ctrl+Shift+I 運行過程 IfThenAnd
7. 改變儲存格 B1 和 B2 的值,以便你每次運行該過程時,不同的 If…Then 語句為真
技巧 5-2 If 指令塊和縮進
要使 If 區塊更容易閱讀和理解,可以使用縮進。對比下面的代碼書寫:
If condition Then
action1
End If
If condition Then
action
End If
看看下面的代碼,你可以輕易知道該區塊開始在哪裡,結尾又在哪裡。
4.The If…Then…Else 語句
現在,你知道當一個或多個條件為真或為假時如何顯示資訊或採取行動。然而,如果你的程式需要
在條件為真時採取某個行動,而條件為假時採取另外一個行動,應該怎麼辦呢?你可以通過添加一
個 Else 子句就可以根據測試的結果將你的過程引導到一個合適的語句。If…Then…Else 語句有兩種
格式——單行和多行。單行的格式為:
If 條件 Then 語句 1 Else 語句 2
當條件為真時,執行關鍵字 Then 後面的語句,當條件為假時,則執行 Else 後面的語句。例如:
If Sales>5000 Then Bonus = Sales * 0.05 Else MsgBox “No Bonus”
如果儲存在變數 Sales 的值大於 5000 的話,那麼 VB 將使用下述公式:Sales * 0.05 來計算股紅
(bonus)。然而,如果變數 Sales 不大於 5000 的話,VB 就會顯示資訊“No Bonus”。If…Then…Else
語句應該用於決定執行兩個操作中的哪一個。當你要執行多個語句時,你最好使用多行格式的
If…Then…Else 語句:
If 條件 Then
如果條件為真時要執行的語句
Else
如果條件為假時要執行的語句
End If
注意,多行的 If…Then…Else 語句以關鍵字 End If 結束。使用上面顯示的縮進使得程式結構易於閱
讀。在下面的例子中,如果條件 ActiveSheet.Name = “Sheet1”為真,VB 就執行 Then 和 Else 之間
的語句,並且忽略 Else 和 End If 之間的語句。當條件為假時,VB 就忽略 Then 和 Else 之間的語句,並
且執行 Else 和 End If 之間的代碼。
If ActiveSheet.Name = "Sheet1" Then
ActiveSheet.Name = "My Sheet" MsgBox "This sheet has been renamed."

Else
MsgBox "This sheet name is not default."

End If
讓我們來看看程式示例:
1. 在工程 Decisions(Chap05.xls)裡插入一個新模組
2. 重命名該模組為 IfThenElse
3. 輸入下列過程 WhatTypeOfDay:
Sub WhatTypeOfDay()
Dim response As String
Dim question As String
106
Dim strmsg1 As String, strmsg2 As String

Dim myDate As Date


question = "Enter any date in the format mm/dd/yyyy:" _

& Chr(13)& " (e.g., 11/22/1999)"


strmsg1 = "weekday"
strmsg2 = "weekend"
response = InputBox(question)
myDate = Weekday(CDate(response))
If myDate >= 2 AND myDate <= 6 Then

MsgBox strmsg1
Else
MsgBox strmsg2
End If
End Sub
上面的過程要求用戶輸入任意一個日期。使用者提供的字元串通過一個內置函數 CDate 轉變為 Date 數
據類型,最好,函數 Weekday 將日期轉變為一個指明該日期在一周裡的天數(參見表 5-3)。該整數
儲存於變數 myDate 裡。條件測試用以檢查變數 myDate 是否大於等於 2 以及小於等於 6。如果測試結果
為真,那麼用戶就被告知,該提供的資料是個工作日;否則,程式宣佈這是個週末。
表 5-3 內置函數 Weekday 返回的值
常數
vbSunday
vbMonday
vbTuesday
vbWednesday
vbThursday
vbFriday
vbSaturday

1
2
3
4
5
6
7
4. 從 VB 視窗運行該程式。運行幾次,每次提供不同的日期。對照你的桌面或日曆檢查 VB 的答案是
否正確。
技巧 5-3 什麼是結構化程式?
結構化程式設計要求所有的程式具有模組化的設計,並使用三種邏輯結構:順序,決定和迴圈。順序結
構為一條接一條地執行語句;決定結構則是讓你基於一些條件的測試來執行一些特定的語句;而只
要某特定的條件為真,迴圈結構就重複地執行某條或某些語句。迴圈是下章的主題。在結構化程式設計
裡,其它一些邏輯語句,例如 GoTo,是不允許的。結構化程式的代碼容易跟蹤——它從上到下平穩
地走下來,沒有任何跳躍到特定標誌去的語句。下面就是一個結構化程式和非結構化程式的例子:
非結構化程式:
Sub GoToDemo()
Dim num, mystr
num = 1
If num = 1 Then
GoTo line1
Else
GoTo Line2
Line1:
mystr = “Number equals 1”
GoTo LastLine
Line2:
mystr = “Number equals 2”
LastLine:
107
Debug.Print mystr
End sub
結構化程式:
Sub Structure()
Dim num, mystr
num = 1
If num = 1 Then
mystr = “Number equals 1”
Debug.Print mystr
Else
mystr = “Number equals 2”
End if
End Sub
當你在起草你的 VBA 程式,並且需要從一個程式的一行跳到另一行時,你可能會被使用 GoTo 語句所
誘惑,不要跳躍。依賴於使用 GoTo 語句來你程式的路徑將導致令人迷惑的代碼,被稱為義大利式面
條代碼。使用結構化程式設計,你可以輕易地到達你程式裡的目的地。
這裡是另外一個示範 If…Then…Else 語句的過程:
Sub EnterData()
Dim cell As Object
Dim strmsg As String
On Error GoTo VeryEnd
strmsg = "Select any cell:"
Set cell = Application.InputBox(prompt:=strmsg, Type:=8) cell.Select

If IsEmpty(ActiveCell) Then
ActiveCell.Formula = InputBox("Enter text or number:")

Else
ActiveCell.Offset(1, 0).Select
End If
VeryEnd:
End Sub
上面的字程式 EnterData 提示使用者選擇任意儲存格,然後儲存格位址賦值於物件變數 cell 。
If…Then…Else 結構檢查被選擇的儲存格是否為空。IsEmpty 是個內置函數,用來決定某個變數是
否已經被初始化了。如果該變數沒有被初始化,那麼 IsEmpty 函數返回 true(真)。回想我們說過,
當變數被賦予第一個值時,它就被初始化了。在本過程中,如果目前的儲存格為空時,VB 將它當作一
個零長度的字串(””)。除了:
If IsEmpty(ActiveCell) Then
你還可以使用下述指令:
If ActiveCell.Value = "" Then
如果目前的儲存格為空,Then 後面的語句就會被執行。該語句提示使用者輸入一個文本或數位,並且一
旦資料被提供,該資料就會輸入給目前的儲存格。如果目前的儲存格不為空,VB 將跳到子句 Else 後面的
指令。該指令將讓 VB 選擇同列裡的下一個儲存格。當你運行該過程,資訊框提示你選擇一個儲存格,
在工作表上,點擊任何儲存格。被選擇的儲存格位址就會出現在資訊框的編輯方塊裡面。點擊確定退
出信息框。VB 檢查被選擇儲存格的內容並且跳到你過程裡的 true 或 false 指令塊(true 指令塊在 Then
後面,而 false 指令塊在 Else 後面)。
5.If…Then…ElseIf 語句
很多時候,你需要檢查很多種情況,你可以使用子句 ElseIf 來將一些 If 條件結合在一起。使用
If…Then…ElseIf 語句,你可以比用前面章節中的 If…Then…Else 語句評估更多的條件。這裡是
If…Then…ElseIf 語句的語法:
If 條件 1 Then
108
條件 1 為真時要執行的語句
ElseIf 條件 2 Then
條件 2 為真時要執行的語句
ElseIf 條件 3 Then
條件 3 為真時要執行的語句
ElseIf 條件 N Then
條件 N 為真時要執行的語句
Else
所有條件都為假時要執行的語句
End If
Else 子句是可選的;如果當所有條件為假時,沒有要執行的操作,那麼你就可以忽略它。
技巧 5-4 ElseIf 子句
你的程式裡可以包括任何多個 ElseIf 子句和條件。ElseIf 子句總是出現在 Else 子句之前的,只有當
ElseIf 子句的條件為真時,它的語句才會被執行。
我們來看看下述例子:
If ActiveCell.Value = 0 Then
ActiveCell.Offset(0, 1).Value = "zero"

ElseIf ActiveCell.Value >0 Then


ActiveCell.Offset(0, 1).Value = "positive"

ElseIf ActiveCell.Value <0 Then


ActiveCell.Offset(0, 1).Value = "negative"

End If
該例檢查目前的儲存格的值,並且在相鄰的列貼上適當的“標籤”(零,正和負)。注意,此時沒有使
用 Else 子句。如果第一種情況(ActiveCell.Value = 0)為假,VB 將跳到下一個 ElseIf 語句,並且
評估該條件( ActiveCell.Value>0 ), 如果該值不大於 0 , VB 將跳到下個 ElseIf 並檢查
條件
ActiveCell.Value<0。
我們來看看 If…Then…Else 語句在一個完整的過程中如何工作:
1. 在當前工程裡插入一新模組
2. 重命名模組為 IfThenElseIf
3. 輸入下列過程 WhatValue:
Sub WhatValue()
Range("A1").Select
If ActiveCell.Value = 0 Then
ActiveCell.Offset(0, 1).Value = "zero"

ElseIf ActiveCell.Value > 0 Then


ActiveCell.Offset(0, 1).Value = "positive"

ElseIf ActiveCell.Value < 0 Then


ActiveCell.Offset(0, 1).Value = "negative"

‘End If (原文錯誤,多一個 End If)


End If
End Sub
因為你需要運行過程 WhatValue 好幾次來測試各種條件,所以,我們給它設置個臨時的快速鍵。
4. 打開立即視窗,並且輸入下列語句:
Application.OnKey "^+y", "WhatValue"

一旦按下回車鍵,VB 就會運行 OnKey 方法將過程 WhatValue 賦予複合鍵 Ctrl+Shift+Y。這個鍵盤快捷


鍵只是臨時的——當你重新啟動 Excel 後它就不起作用了。你同樣也可以用 Excel 介面-工具功能表-
巨集對話方塊裡的選項來設置快速鍵。
5. 切換到 Excel 介面,並啟動 Sheet1
6. 在儲存格 A1 裡輸入 0,並且按下 Ctrl+Shift+Y。VB 將調用過程 WhatValue 並在儲存格 B1 釐米輸入
“zero”
109
7. 在儲存格 A1 裡輸入任意大於 0 的數位,並按下 Ctrl+Shift+Y,VB 將再次調用 WhatValue。VB 評估
第一種條件,因為該測試的結果為假,所以它跳到 ElseIf 語句。第二個條件為真,因此 VB 執行
Then 後面的語句,並且跳過下一條語句,直接到 End If。因為 End If 後面並沒有其它的語句了,
該過程便結束了,儲存格 B1 現在顯示“positive”。
8. 在儲存格 A1 裡輸入任意小於 0 的數位,並按下 Ctrl+Shift+Y。這次,前面兩個條件都返回假,
因此 VB 繼續檢查第三個條件。因為這次的測試為真,VB 就在儲存格 B1 裡貼上標籤“negative”
9. 在儲存格 A1 裡輸入任何文本,並按下 Ctrl+Shift+Y,VB 的反應是“positive”,然而,這不是
個滿意的答案。你也許希望 VB 通過顯示“text”來區分開正數和文本。要使你的過程 WhatValue
更“聰明”些,你就需要學習如何通過使用嵌套的 If…Then 語句來作一些更複雜的決定。
6.嵌套的 If…Then 語句
將一個 If…Then 語句或 If…Then…Else 語句放在另外一個 If…Then 語句或 If…Then…Else 語句裡
面,你可以在你的 VBA 過程裡作出更複雜的決定。這種一個 If 語句裡包含另一個 If 指令塊的結構稱
為嵌套的 If 語句。
接下來的過程 TestConditions 世上節裡的過程 WhatValue 的修正版,演示嵌套的 If…Then 語句是如
何工作的:
Sub TestConditions()
Range("A1").Select
If IsEmpty(ActiveCell) Then
MsgBox "The cell is empty."
Else
If IsNumeric(ActiveCell.Value) Then

If ActiveCell.Value = 0 Then
ActiveCell.Offset(0, 1).Value = "zero"

ElseIf ActiveCell.Value > 0 Then


ActiveCell.Offset(0, 1).Value = "positive"

ElseIf ActiveCell.Value < 0 Then


ActiveCell.Offset(0, 1).Value = "negative"

End If
Else
ActiveCell.Offset(0, 1).Value = "text"

End If
End If
End Sub
為了使過程 TestConditions 更容易理解,每個 If…Then 語句都顯示為不同的格式,現在你可以清楚
地看到過程使用了三個 If…Then 區塊。
第一個 If 塊(粗體)檢查目前的儲存格是否為空,如果為真,就會顯示資訊,然後 VB 將跳過 Else 部分
找到相應的 End If,該語句位於關鍵字 End Sub 之前。
如果目前的儲存格不為空,IsEmpty (ActiveCell)條件返回假,並且 VB 運行粗體 Else 下面的單底線
的 If 塊。該單底線的 If…Then…Else 語句就是嵌套在第一個 If 塊(粗體)的。該語句檢查當前單
元格是否是個數字。注意,我們通過另一個內置函數 IsNumeric 來做這個。如果目前的儲存格的值不
是一個數字,條件就為假,因此,VB 跳到單底線的 Else 處,並且在 B1 裡輸入“text”。然而,如
果目前的儲存格包含個數字時,VB 就會運行雙底線的 If 塊,評估每種情況並作出相應的決定。
第一個 If 塊(粗體)被稱為外部 If 語句,這個外部語句包含兩個內部的 If 語句(單底線和雙下劃
線)。
技巧 5-5 嵌套語句
嵌套是指將一種控制結構放在另外一控制結構裡面。你將在第六章裡的迴圈結構裡看到更多的嵌套
的例子。
7.Select Case 語句
為了避免難以弄清的複雜的嵌套的 If 語句,你可以使用 Select Case 語句代替。它的語法為:
110
Select Case 測試運算式
Case 運算式 1
如果運算式 1 匹配測試運算式的語句
Case 運算式 2
如果運算式 2 匹配測試運算式的語句
Case 運算式 N
如果運算式 N 匹配測試運算式的語句
Case Else
如果沒有運算式匹配測試運算式要執行的語句
End Select
你在關鍵字 Select Case 和 End Select 之間放置任意多個條件以測試。子句 Case Else 是可選的,當
你希望可能有條件運算式返回假時使用它。在 Select Case 語句裡,VB 將每個運算式和測試運算式
相比較。
這裡是 Select Case 語句背後的邏輯。當 VB 遇到 Select Case 子句,它記下測試運算式的值。然後它
前進到下面的第一個 Case 子句,如果這個運算式的值和測試運算式的值匹配的話,VB 就會執行語句
直到遇到另外一個 Case 子句並且跳到 End Select 語句。然而,如果第一個 Case 子句後面的運算式測
試結果和測試運算式不匹配時,VB 就會檢查每一個 Case 子句,直到它找到一個匹配的為止。如果沒
有一個 Case 子句後面的運算式匹配測試運算式的值的話,VB 就會跳到 Case Else 子句並執行該語句
直到遇到關鍵字 End Select。注意,Case Else 子句是可選的,如果你的程式裡面沒有使用 Case Else
並且沒有一個 Case 子句的運算式和測試運算式相匹配,VB 就會跳到 End Select 後面的語句,並且繼
續執行你的程式。
我們來一個使用 Select Case 語句的程式例子。在第四章裡,你學習了 MsgBox 函數允許你顯示帶有
一個或多個按鈕的資訊,你也學習了 MsgBox 函數的結果可以賦予一個變數。使用 Select Case 語句,
你現在可以基於使用者按下的按鈕決定採取哪個行動。
1. 在當前工程裡插入一新模組
2. 重命名新模組 SelectCase.
3. 輸入下述過程 TestButtons:
Sub TestButtons()
Dim question As String
Dim bts As Integer
Dim myTitle As String
Dim myButton As Integer
question = "Do you want to open a new workbook?"

bts = vbYesNoCancel + vbQuestion + vbDefaultButton1

myTitle = "New Workbook"


myButton = MsgBox(prompt:=question, buttons:=bts, _ title:=myTitle)

Select Case myButton


Case 6
Workbooks.Add
Case 7
MsgBox "You can open a new book manually later."

Case Else
MsgBox "You pressed Cancel."
End Select
End Sub
過程 TestButtons 的第一部分顯示一個帶有三個按鈕的資訊框:是,否和取消。使用者選擇按鈕的值
賦予變數 myButton。
如果使用者點擊“是”,那麼變數 myButton 就會被賦值常量 vbYes 或它對應的值 6;如果用戶點擊“否”,
那麼變數 myButton 則賦值為常量 vbNo 或它對應的值 7;最後,如果點擊了“取消”,變數 myButton
的內容就等於 vbCancel 或 2。
111
Select Case 語句對照儲存在變數 myButton 裡的值檢查 Case 子句提供的值。當有匹配時,就會執行
適當的 Case 語句。
如果你使用常量,而不是按鈕值,過程 TestButtons 同樣會運行一致。
Select Case myButton
Case vbYes
Workbooks.Add
Case vbNo
MsgBox "You can open a new book manually later."

Case Else
MsgBox "You pressed Cancel."
End Select
你可以忽略 Else 子句,可以按下述方法修改一下 Select Case 語句:
Select Case myButton
Case vbYes
Workbooks.Add
Case vbNo
MsgBox "You can open a new book manually later."

Case vbCancel
MsgBox "You pressed Cancel."
End Select
4. 運行過程 TestButtons 三次,每次選擇一個不同的按鈕。
技巧 5-6 通過 Case Else 捕捉錯誤
儘管在 Select Case 語句裡使用 Case Else 不是強制的,使用它總是很好的,以防止萬一測試有沒有
預料到的值。Case Else 子句是個放置錯誤資訊的好地方。
8.和 Case 子句一起使用 Is
有時候,作決定是基於測試運算式的條件,例如它是否大於,小於,等於或使用一些其它的關係運
算符(參見表 5-1)。關鍵字 Is 使你能夠在 Case 子句裡使用條件運算式。使用關鍵字 Is 的 Select Case
語句的語法如下:
Select Case 測試運算式
Case Is 條件 1
如果條件 1 為真時執行的語句
Case Is 條件 2
如果條件 2 為真時執行的語句
Case Is 條件 N
如果條件 N 為真時執行的語句
End Select
例如,我們來比較幾個數字:
Select Case myNumber
Case Is <10
MsgBox "The number is less than 10"

Case 11
MsgBox "You entered eleven."
Case Is >=100
MsgBox "The number is greater than or equal to 100."

Case Else
MsgBox "The number is between 12 and 99."

End Select
假設變數 myNumber 為 120,那麼第三個 Case 子句為真,並且只有 Case Is >=100 和 Case Else 之間的
語句會被執行。
112
9.確定 Case 子句裡數值的範圍
在前面的例子裡,你看到了在每個 Case 子句裡使用一個簡單運算式。然而,很多時候,你可能需要
在 Case 子句裡確定一個數值範圍。可以通過關鍵字 To 用於運算式的數值之間來實現它,如下所示:
Select Case unitsSold
Case 1 to 100
Discount = 0.05
Case Is <= 500
Discount = 0.1
Case 501 to 1000
Discount = 0.15
Case Is >1000
Discount = 0.2
End Select
我們來分析一下上面的 Select Case 代碼塊,假設變數 unitsSold 當前值為 99。VB 將變數 unitsSold
的值與 Case 子句的條件運算式進行比較。第一和第三條 Case 子句示範如何通過使用關鍵字 To 在條件
運算式裡使用數值範圍。因為 unitsSold=99,第一個 Case 子句裡的條件運算式為真,因此,VB 將 0.05
賦給變數 Discount。第二個 Case 子句如何呢?它也為真。儘管,很明顯 99 小於等於 500,VB 不會執
行相關的語句 Discount=0.1。原因是,一旦 VB 找到了一個真條件的 Case 子句,它就不會去管其它的
Case 子句,它將跳過那些代碼,繼續執行 End Select 語句後面可能有的語句。
我們來練練使用 Select Case 語句,在函數過程裡使用它。回想在第四章裡,函數過程允許你將結
果返回給一個子過程。假設該子過程必須根據銷售的套數來顯示一個折扣,你可以從用戶那裡獲得
銷售套數,然後允許一個函數來確定需要的折扣:
1. 在模組 SelectCase 裡輸入下列子過程:
Sub DisplayDiscount()
Dim unitsSold As Integer
Dim myDiscount As Single
unitsSold = InputBox("Enter the number of sold units:")

myDiscount = GetDiscount(unitsSold)

MsgBox myDiscount
End Sub
2. 輸入下列函數過程:
Function GetDiscount(unitsSold As Integer)

Select Case unitsSold


Case 1 To 200
GetDiscount = 0.05
Case Is <=500
GetDiscount = 0.1
Case 501 To 1000
GetDiscount = 0.15
Case Is >1000
GetDiscount = 0.2
End Select
End Function
3. 將游標放在過程 DisplayDiscount 的任意地方並且按下 F5 來運行它。
過程 DisplayDiscount 將儲存於變數 unitsSold 的值傳遞給函數 GetDiscount。當 VB 遇到 Select Case
語句時,它檢查第一個 Case 子句裡的值是否合儲存於 unitsSold 裡面的值是否匹配。如果匹配,VB
給函數名稱賦值百分之五(0.05),並且跳到關鍵字 End Select。因為,在函數過程裡面沒有更多需
要運行的語句,VB 就返回主調過程——DisplayDiscount,在這裡,它將函數的結果賦予變數
myDiscount。最後的語句用資訊框來顯示獲得的折扣。
113
10.在 Case 子句裡確定多個運算式
你可以使用逗號明確單一 Case 子句裡的多個運算式:
Select Case myMonth
Case "January", "February", "March"

Debug.Print myMonth & ": 1st Qtr."


Case "April", "May", "June"
Debug.Print myMonth & ": 2nd Qtr."
Case "July", "August", "September"
Debug.Print myMonth & ": 3rd Qtr."
Case "October", "November", "December"

Debug.Print myMonth & ": 4th Qtr."


End Select
技巧 5-7 Case 子句的多個條件
用來分隔開 Case 子句裡面多個條件的逗號,和用於 If 語句裡的運算子 OR 意義一樣。只要這些條件有
一個為真,Case 子句就為真。
11.接下來…
在本章介紹的條件陳述式,讓你控制你的過程走向。通過測試條件的真假,你可以決定哪些語句需要
執行,哪些要跳過。換句話說,不必從上到下,一行一行地運行你的過程,你可以只執行某些行,
如果你猶豫應該使用哪種條件陳述式,這裡是一些指南:
如果你只要提供一個條件,簡單的 If…Then 語句是最好的選擇
如果你要決定運行兩個條件中的一個,那麼使用 If…Then…Else 語句
如果你的程式需要兩個或多個條件,那麼使用 If…Then…ElseIf 或者 Select Case 語句
如果你的程式有很多條件,那麼就使用 Select Case 語句。這個語句比 If…Then…ElseIf 語句
更靈活並且更容易理解。
有些決定是需要重複的,例如,你可能需要在工作表裡的每個儲存格裡或者一個工作簿裡的每個表
裡重複同樣的操作。下章將教你如何一次又一次地做同樣的操作。
第六章在 VBA 中重複操作
作者:Julitta Korol 翻譯:Tiger Chen Feb 1’ 2005
既然你已經學習了條件陳述式如何賦予你的 VBA 過程作決定的能力,是時候深入了。不是所有的決定
都容易,有時候你將需要運行一些語句好幾次才能達到某個條件。然而,另一方面,當你達到這個
決定後,你可能需要一直運行某些語句,只要條件為真,或直到條件變為真。在程式設計中,重複地執
行任務被稱為迴圈。VBA 有好些個迴圈結構,允許你多次重複一系列的語句。你將在本章裡學習如
何迴圈你的代碼。
1.Do Loops: Do…While 和 Do…Until
VB 有兩種 Do 迴圈語句,只要或者直到某個條件為真,它們就會重複一系列的語句。只要條件為真,
Do…While 迴圈就允許你重複某個操作。這個迴圈的語法如下:
Do While 條件
語句 1
語句 2
語句 N
Loop
當 VB 遇到這個迴圈時,它首先條件的真假,如果條件為假,迴圈內部的語句就不會被執行,VB 將繼
續執行關鍵字 Loop 後面的第一條語句。如果條件為真,迴圈裡面的語句則會被一條一條地執行,直
到遇到 Loop 語句。Loop 語句告訴 VB 重複這個過程,只要 Do While 語句裡的條件為真的話。
現在,我們來看看如何在 Excel 裡面好好利用 Do…While 迴圈語句。在第四章裡,你學習了如何根據
一個儲存格的內容來作決定。讓我們再進一步,看看如何在一系列儲存格上作同樣的決定。該決定
是給一列中的非空儲存格設置粗體格式。
114
1. 打開一個空工作簿,並且命名為 Chap06.xls
2. 切換到 VB 編輯螢幕,並且將新工程改名為 Repetition (Chap06.xls)
3. 在工程 Repetition 裡插入一新模組,並重命名為 DoLoops
4. 輸入如下過程:
Sub ApplyBold()
Do While ActiveCell.Value <>""
ActiveCell.Font.Bold = True
ActiveCell.Offset(1, 0).Select
Loop
End Sub
5. 在儲存格 A1:A7 裡輸入任意資料(文本或數位)
6. 選擇儲存格 A1
7. 選擇“工具”-“巨集”-“運行巨集”。在宏對話方塊裡,按兩下過程 ApplyBold(或者選中該過程然後
點擊運行)
當運行過程 ApplyBold 時,VB 首先評估 Do While 語句裡的條件——ActiveCell.Value<>””,該條
件意思是:只要目前的儲存格的值不是一個空字串(””),就執行下列語句。因為你已經在單元
格 A1 裡輸入了資料並且啟動了該儲存格(見第六步),第一個測試返回真,所以 VB 執行語句
ActiveCell.Font.Bold = True,它的意思是給目前的儲存格設置粗體格式。接下來,VB 選擇了下一
行的儲存格(參見第二章裡的 Offset 屬性)。因為該語句之後就是關鍵字 Loop,VB 返回到 Do While
語句,並且再次檢查條件。如果新選中的儲存格(當前啟動的儲存格)不為空,那麼 VB 就會重複循
環內部的語句。該過程會繼續,直到檢查到儲存格 A8 的內容為空,測試條件的結果為假,因此,VB
就跳過迴圈內部的語句。並且在關鍵字 Loop 後面沒有其它的語句了,所以該過程就結束了。
我們來看看另外一個 Do…While 迴圈的例子。是不是很想知道如何在 Excel 的狀態列裡顯示今天的日
期和時間?這裡有個例子,如何讓它顯示十秒鐘:
Sub TenSeconds()
Dim stopme
stopme = Now + TimeValue("00:00:10")

Do While Now < stopme


Application.DisplayStatusBar = True

Application.StatusBar = Now
Loop
Application.StatusBar = False
End Sub
在上面的程式裡,只要函數 Now 返回的時間小於變數 stopme 的值,Do…While 迴圈裡的語句就會被執
行。變數 stopme 儲存值為當前時間加上十秒(參見線上說明裡的另外一個使用內置函數 TimeValue
的例子)。
語句 Application.DisplayStatusBar 告訴 VB 打開狀態列的顯示,下條語句則是將當前日期和時間放
在狀態列上。當顯示時間時(只顯示 10 秒)使用者無法使用系統(游標變成了沙漏)。十秒鐘後(也
就是,當條件 Now < stopme 為真),VB 跳出迴圈並且執行關鍵字 Loop 後面的語句,該語句將狀態列
返回到預設資訊“就緒”。
技巧 6-1 什麼是迴圈?
迴圈是一種導致一部分程式碼重複執行的程式設計結構。VBA 提供了多種結構在你的過程裡執行迴圈:
Do…While, Do…Until, For…Next, For…Each, and While…Wend
Do…While 迴圈還有另外一種語法,你可以在迴圈的底部測試條件,例如:
Do
語句 1
語句 2
語句 N
Loop While 條件
當你在迴圈的底部測試條件時,意味著迴圈裡面的語句至少運行了一次。看一下這個例子:
115
Sub SignIn()
Dim secretCode As String
Do secretCode = InputBox("Enter your secret code:")

If secretCode = "sp1045" Then Exit Do

Loop While secretCode <> "sp1045"


End Sub
注意,在條件被測試之時,VB 至少已經執行了一次迴圈裡的語句。除了將條件放在迴圈之後外,過
程 SignIn 示範如何使用條件跳出迴圈。當 Exit Do 語句執行時,迴圈便立即停止。
技巧 6-2 避免無限迴圈
如果你沒有正確地設計你的迴圈,你將導致一無限迴圈——永無休止的迴圈。你將無法使用 Esc 鍵
來停止該迴圈。在下面的過程裡,因為用戶忘了放置測試條件而導致了永無休止的迴圈:
Sub SayHello()
Do
MsgBox "Hello."
Loop
End Sub
你必須按下 Ctrl+Break 鍵(譯者:現在,有些電腦使用別的複合鍵來中斷程式。例如我的手提電腦
就是 Fn+Break)才能終止該無限迴圈,當 VB 顯示資訊“代碼執行被中斷”時,點擊結束以退出過程。
另外一種方便的迴圈 Do…Until,也可以讓你重複一條或多條語句,直到條件為真。換句話說,
Do…Until 語句是只要當某個條件為假的時候重複一塊代碼。這是它的語法:
Do Until 條件
語句 1
語句 2
語句 N
Loop
使用上面的語法,你可以將前面的過程 ApplyBold 重新寫成下面的方式:
Sub ApplyBold2()
Do Until IsEmpty(ActiveCell)
ActiveCell.Font.Bold = True
ActiveCell.Offset(1, 0).Select
Loop
End Sub
該過程的第一條語句意思是執行下列語句,直到遇到第一個空儲存格。結果上,如果目前的儲存格不
為空,VB 就執行迴圈內部的那兩條語句。只要條件 IsEmpty(ActiveCell)測試為假,這個過程就反
複繼續著。因為過程 ApplyBold2 在迴圈的前面就測試條件,如果第一個儲存格就為空的話,迴圈內
部的語句就不會運行。在下一段,你將有機會試驗它。
和 Do…While 迴圈類似,Do…Until 迴圈也有第二種語法讓你在迴圈的底部測試條件:
Do
語句 1
語句 2
語句 N
Loop Until 條件
如果你想要程式至少執行一次,那麼就將條件放置于 Loop 語句一行,無論條件的值是什麼。
我們來試驗一下下面的例子,該例子將工作簿裡的空工作表刪除。
1. 在你前面創建的 DoLoop 模組裡輸入下面的過程 DeleteBlankSheets:
Sub DeleteBlankSheets()
Dim myRange As Range
Dim shcount As Integer
shcount = Worksheets.Count
Do
116
Worksheets(shcount).Select
Set myRange = ActiveSheet.UsedRange

If myRange.Address = "$A$1" And _


Range("A1").Value = "" Then
Application.DisplayAlerts = False
Worksheets(shcount).Delete
Application.DisplayAlerts = True
End If
shcount = shcount - 1
Loop Until shcount = 1
End Sub
2. 手動在當前工作簿裡面插入一些工作表。在一個工作表裡輸入一些資料與儲存格 A1;另一個工
作表的儲存格 B2 和 C10 裡輸入一些資料;第三個工作表裡不要輸入任何資料。
3. 運行過程 DeleteBlankSheets。當你運行該過程時,無論何時,只要兩個條件都為真——屬性
UsedRange 返回儲存格 A1 並且 A1 為空,VB 就會刪除所選的工作表。屬性 UsedRange 應用於物件
Worksheet,包含工作表中的每個非空儲存格以及他們之間的空儲存格。例如,如果你在單元
格 B2 和 C10 裡輸入裡東西(譯者:包括格式),使用了的區域為$B$2:$C$10。如果你後面又在 A1
裡輸入了資料,那麼 UsedRange 將會是$A$1:$C$10。已使用區域是一個從左上角最遠的地方到
右下角最遠的地方包圍起來的區域。因為工作簿至少要保留一個工作表,所以代碼執行到變數
shcount 等於 1 時就停止了。語句 shcount = shcount-1 確保變數 shcount 在迴圈裡面的代碼每執
行一次就減少 1。變數 shcount 的值在過程的開始處用下列語句:Worksheets.Count 初始化了。
注意,當刪除工作表的時候,Excel 通常會顯示一個確認對話方塊,如果你不想看到這個確認提
示框的話,就是要下列語句:
Application.DisplayAlerts = False
當你完成任務時,使用下列語句,再打開系統資訊。
Application.DisplayAlerts = True
技巧 6-3 計數器
計數器是個數位變數,用來追蹤已進行的項目次數。上面的過程 DeleteBlankSheets 聲明了變數
shcount 來追蹤檢查的工作表個數。計數器變數必須在程式的開始就被初始化(賦值),這可以確保
你總能在開始使用之前知道計數器的確切值。計數器可以按照確定的值增加或減少。參見本章後面
的使用計數器的 For…Next 迴圈。
2.觀察過程執行
當你使用迴圈結構運行過程時,有時很難看到該過程會按預期地執行。有時,你很想觀察程式慢慢
地運行,這樣你就能夠檢查該程式的邏輯。我們來看看 VB 如何讓你一行接一行地執行程式。
1. 在儲存格區域 A1:A5 裡面輸入任何資料
2. 選擇儲存格 A1
3. 在 Excel 視窗,選擇“工具”-“巨集”-“運行巨集”
4. 在宏對話方塊裡,選擇 ApplyBold2 並點擊“單步執行”按鈕。VB 編輯螢幕將出現,過程的名稱被
黃色加亮(參加圖 6-1)。注意代碼視窗左邊的黃色箭頭。
117
圖 6-1 觀察程式一行接一行地執行
5. 使 VB 視窗縮小一些,可以點擊 VB 標題列的“還原”按鈕縮小視窗
6. 按下 F8,黃色加亮區將跳到 DoUntil IsEmpty(ActiveCell)行
7. 繼續按 F8,同時觀察代碼和工作表視窗
3.While…Wend 迴圈
While…Wend 迴圈功能上和 Do…While 迴圈一樣,它是從 Microsoft Basic 的早期版本遺留下來的並
且 VBA 保留它也是為了支持相容性。該迴圈以關鍵字 While 開始以關鍵字 Wend 結束。這是它的語法:
While 條件
語句 1
語句 2
語句 N
Wend
條件在迴圈的上面就被測試,只要提供的條件為真,這些語句就會被執行。一旦條件為假,VB 就將
退出該迴圈。我們來看一個使用 While…Wend 迴圈結構的過程:
1. 在當前工程裡插入一新模組,重命名為 WhileLoop
2. 輸入下述過程:
Sub ChangeRHeight()
While ActiveCell <>""
ActiveCell.RowHeight = 28
ActiveCell.Offset(1, 0).Select
Wend
End Sub
3. 在儲存格區域 B1:B4 裡輸入一些資料
4. 選擇儲存格 B1 並且運行過程 ChangeRHeight 。當目前的儲存格不為空時, 上
面的過程
ChangeRHeight 將設置行高位 28。
118
4.For…Next 迴圈
當你知道你需要重複運行多少次某段語句時,可以使用 For…Next 語句。它的語法如下:
For 計數器= 開始 To 結束[步長]
語句 1
語句 2
語句 N
Next [計數器]
括弧裡面的代碼是可選的。計數器是個儲存反復次數的數字型變數,開始是你期望的起始計數點,
結束則表明迴圈應該執行多少次。
例如,你想要重複執行迴圈裡的語句 5 次,使用下述 For 語句:
For counter = 1 To 5
你的語句
Next
當 VB 遇到關鍵字 Next 時,它將回到迴圈的開始處,並且再次執行迴圈裡面的代碼,直到計數器到達
結束值。一旦計數器的值大於關鍵字 To 後面的數值,VB 就會跳出迴圈。因為計數器變數在每次執行
迴圈後會自動地變化,它早晚會超出結束的值。每次 VB 執行迴圈裡的語句後,變數計數器的值會默
認地增加 1,你可以使用 Step 語句來改變這個默認設置。例如,要使計數器每次增加 3,就可以使用
以下語句:
For counter = 1 To 5 Step 3
你的代碼
Next counter
當 VB 遇到上面的語句,它會執行迴圈裡的語句兩次。在第一次的迴圈裡,counter 等於 1,第二次則
等於 4(3+1)。在執行兩次迴圈後,counter 就等於 7(4+3),這導致 VB 退出該迴圈。
注意,步長(Step)是可選的。可選語句總是顯示則方括號裡面(參加本段開頭部分的語法)。步
長不需要明確,除非它不等於 1。你可以在 Step 後面放個負值作為步長,那麼 VB 每次遇到關鍵字 Next
後都會將計數器減小。
關鍵字 Next 後面的變數名稱(counter)也是可選的,然而,好的程式設計習慣是要強制在關鍵字 Next
後面加上計數器。
如何將 For…Next 迴圈使用在 Excel 裡面呢?假使你只想要在你的銷售報告裡面包括某幾個特定月
份的產品銷售,當你從 Access 導入資料時,你同樣也會將那些銷售額為 0 的資料行一起導入。你如
何快速取出資料為 0 的行呢?儘管,有很多種方法可以解決這個問題,但是,我們來看看如何使用
For…Next 迴圈來處理這個問題吧。
1. 在 VB 視窗,在當前工程裡插入一個模組並且命名為 ForNextLoop
2. 在 ForNextLoop 模組裡輸入下列過程:
Sub DeleteZeroRows()
Dim totalR As Integer
Dim r As Integer
Range("A1").CurrentRegion.Select
totalR = Selection.Rows.Count
Range("B2").Select
For r = 1 To totalR-1
If ActiveCell = 0 Then
Selection.EntireRow.Delete
totalR = totalR – 1
Else
ActiveCell.Offset(1, 0).Select
End If
Next r
End Sub
3. 切換到 Excel 介面,並且準備下述表格:
119
A
1 Product Name
2 Apples
3 Pears
4 Bananas
5 Cherries
6 Blueberries
7 Strawberries
4. 運行過程 DeleteZeroRows。
B
Sales (in Pounds)
120
0
100
0
0
160
我們來一行接一行地檢查一下過程 DeleteZeroRows。開始兩語句計算當前區域的總行數,並且將該
值儲存於變數 totalR,接下來,VB 選擇儲存格 B2 然後遇到關鍵字 For。因為試算表的第一行包含
了列標題,所以要從總行數裡減掉 1(totalR-1)。VB 將需要執行迴圈裡面的指令 6 次。
嵌套在迴圈裡面的條件陳述式(If…Then…Else)告訴 VB 根據當前作用儲存格的值作出決定。如果該
值為 0,VB 就刪除當前行,並且將總行數減掉 1。否則,條件為假,因此,VB 將選擇下一行的儲存格。
VB 每完成一次迴圈,它就跳到關鍵字 For 來比較 r 的值和 totalR-1 的值。當過程結束後,銷售表裡就
不會包含沒有銷售的產品了。
技巧 6-4 成對語句
For 和 Next 必須是成對的,如果有一個漏掉了,VB 就將產生一個錯誤資訊“For 沒有 Next”
5.For Each…Next 迴圈
當你的過程需要在一個集合的所有物件或者一個陣列的所有元素(陣列將在第七章裡涉及)之間循
環時,應該使用 For Each…Next 迴圈。該迴圈不需要計數器變數,VB 自己知道應該執行幾次迴圈。
我們拿工作表集合作個例子,要刪除工作簿裡面的工作表,你首先不得不要選擇它,再選擇“編輯”
-“刪除工作表”。如果要只留一個工作表在工作簿裡面的話,你就不得不使用同樣的命令,次數取
決於工作表的總數。因為每個工作表都是工作表集合裡的一個物件,所以使用 For Each…Next 迴圈
來加速刪除工作表。該迴圈的形式是:
For Each 元素 In 組合
語句 1
語句 2
語句 N
Next [元素]
在上面的語法中,元素一個陣列或者集合的所有元素都將被賦予的變數,如果是陣列的話,該變數
必須為 Variant 資料類型;如果是集合的話,則必須是個物件資料類型。組合是集合的名稱或者數
組的名稱。
現在,我們來使用 For Each…Next 迴圈刪除工作表。
1. 在當前工程裡插入新模組並且重命名為 ForEachNextLoop
2. 在模組 ForEachNextLoop 裡輸入下列過程:
Sub RemoveSheets()
Dim mySheet As Worksheet
Application.DisplayAlerts = False
Workbooks.Add
Worksheets("Sheet2").Select
For Each mySheet In Worksheets
ActiveWindow.SelectedSheets.Delete
Next mySheet
End Sub
3. 運行過程 RemoveSheets。
VB 將會打開一個新工作簿並且刪除除 Sheet1 之外的所有工作表。注意,變數 mySheet 代表工作表集
合裡的所有物件。除了按通常的方法將物件變數聲明為 Object 類型,你還可以將它聲明為更具體的
120
物件類型,這樣會更好。在這個具體的例子裡,你可以使用下面的聲明:
Dim mySheet As Worksheet
而不是:
Dim mySheet As Object
第一條指令 Application.DisplayAlerts = False 讓 Excel 在過程運行的時候不要顯示警告和資訊。
如果你忽略了它,Excel 將會要你確認是否刪除所選的工作表。接下來,過程打開一個新工作簿並
且選擇 Sheet2。For Each…Next 迴圈遍歷每個工作表(從所選的 Sheet2 開始)並且刪除它們。當過
程結束的時候,該工作簿只剩一個工作表 Sheet1 了。
這裡是另外一個檢查某個工作表是否存在於一工作簿中:
Sub IsSuchSheet()
Dim mySheet As Worksheet
Dim counter As Integer
counter = 0
For Each mySheet In Worksheets
If mySheet.name = "Sheet2" Then
counter =counter + 1
End If
Next mySheet
If counter = 1 Then
MsgBox "This workbook contains Sheet2."

Else
MsgBox "Sheet2 was not found."
End if
End Sub
7.提前跳出迴圈
有時候,你並不想等到迴圈自己結束,可能是使用者輸入了錯誤的資料,過程遇到了錯誤或者可能是
任務已經完成並且沒有必要作更多的迴圈。你可以提前跳出迴圈,而不必等到條件正常結束。VB
有兩種 Exit 語句:
Exit For 語句用來提前退出 For…Next 或者 For Each…Next 迴圈
Exit Do 語句立即退出任何 VBA Do 迴圈
下面的過程示範如何使用 Exit For 語句提前跳出 For Each…Next 迴圈:
1. 在當前模組裡輸入下列過程:
Sub EarlyExit()
Dim myCell As Range
For Each myCell in Range("A1:H10")
If myCell.Value = "" Then
myCell.Value = "empty"
Else
Exit For
End If
Next myCell
End Sub
EarlyExit 過程檢查特定區域 A1:H10 裡每個儲存格的內容,如果目前的儲存格為空,VB 就會在當前單
元格力輸入文本“empty”。當 VB 遇到第一個非空儲存格,它就會跳出迴圈。
2. 打開一個新工作簿並且在儲存格區域 A1:H10 的任意儲存格裡輸入資料
3. 運行過程 EarlyExit
技巧 6-5 退出過程
如果你想提前退出子過程,那麼可以使用 Exit Sub 語句。如果該過程是一個函數的話,就使用 Exit
Function 語句代替就行。
121
8.迴圈嵌套
到目前為止,你已經在本章裡嘗試了很多種迴圈了,每種過程示範每個迴圈結構的使用。然而,在
程式設計中,一迴圈總是放在另外一迴圈中的。VB 允許你將不同類型的迴圈(For 和 Do 迴圈)“嵌套”在
同一個過程裡。當你編寫迴圈嵌套時,請確保每個內部的迴圈在外部迴圈裡面已經完成。另外,每
個迴圈都必須有其自己獨特的計數器變數。如果使用迴圈嵌套,你可以更有效地執行特定的任務。
下面顯示的過程 ColorLoop 示範如何嵌套一個 For…Next 迴圈在另一個 For…Next 迴圈裡面:
Sub ColorLoop()
Dim myRow As Integer
Dim myCol As Integer
Dim myColor As Integer
myColor = 0
For myRow = 1 To 8
For myCol = 1 To 7
Cells(myRow, myCol).Select
myColor = myColor + 1
With Selection.Interior
.ColorIndex = myColor
.Pattern = xlSolid
End With
Next myCol
Next myRow
End Sub
上面的過程 ColorLoop 使用了兩個 For…Next 迴圈來改變工作表中前面八行和七列裡的每個儲存格
的顏色。當外部的迴圈在追蹤行號的時候,內部的迴圈在做更多的事情,它首先確定當前的列號,
基於當前的行號的列號選擇適當的儲存格,然後給所選的儲存格設置顏色。
內部的 For…Next 迴圈給工作表的第一行的七個儲存格(A1, B1, C1, D1, E1, F1 和 G1)設置不同
的顏色。當變數 myCol 大於 7 時,VB 跳回外部迴圈並且變數 myRow 增加 1,再回到內部迴圈去設置下一
行儲存格的顏色。當過程結束時,56 個儲存格(8*7)被設置了當前調色板上可用的所有顏色。第
一個儲存格,A1,被設置了黑色(色彩索引號為 1),第二個儲存格 B1 則被設置為白色了(色彩索引
號為 2)。每次儲存格位址變化——Cells(myRow, myCol).Select——變數 myColor 的內容也會改變
——myColor = myColor + 1
9.接下來…
在本章裡,你學習了如何在迴圈裡重複一組代碼。通過使用好幾種類型的迴圈,你看到了每種迴圈
稍稍不同地進行重複。你有了經驗後,你將更容易地選擇合適的控制結構來執行你的任務。
在本書的後續章節中,將會有更多的使用迴圈的例子。例如,在下章裡,你將看到如何使用數組合
嵌套的迴圈來創建一個 VBA 過程,該過程將幫你選擇彩票號碼。在下章裡,你將學習如何處理大量
的資料,而不會迷失在變數的海洋裡。
第七章利用 VBA 陣列管理資料清單和表格
作者:Julitta Korol 翻譯:Tiger Chen Feb 1’ 2005
在前面的章節裡,你在很多 VBA 過程裡使用變數來儲存特定的物件資訊,屬性或者數值。對於你想
要處理的單個數值,你可以聲明變數,但是,對於一系列的數值呢?如果你不得不編寫 VBA 過程來
處理大量的資料,你就得聲明足夠的變數來處理所有的資料。你能想像將世界上所有國家的貨幣交
換利率儲存在你的程式的噩夢嗎?要創建一個表格來儲存這些必要的資料的話,你至少要給每個國
家創建三個變數:國家名稱,貨幣名稱和交換比率。幸運的是,VB 有方法來解決該問題。將相關的
變數歸為一類,你的 VBA 過程可以輕鬆處理大量的資料。在本章裡,你將學習如何使用陣列來操作
資料清單和資料表。
122
1.瞭解陣列
在 VB 裡,陣列一種特殊的變數,代表擁有相同資料類型(字串,整型,貨幣,日期,等等)的一
組相似的數值。兩種最通常的陣列是一維陣列(清單)和二維陣列(表格)。有時,一維陣列被稱
為清單。一維陣列或編號清單的例子有:購物清單,星期名稱的清單或員工清單。清單裡面的每個
值都有一個索引。下面是一個含有六個成員的清單的圖解:
項目(1)
項目(2)
項目(3)
項目(4)
項目(5)
項目(6)
注意,列代表一維的當前為空的陣列。如果你想用資料填充這個陣列,只要使用一個變數名稱,附
帶括符編號就行,而不需要使用六個不同的標籤。在上面的圖解裡,“項目”一變數名稱,括弧裡
的數位明確陣列裡的每個成員。
陣列的所有成員都必須具有相同的資料類型,換句話說,一個陣列不能同時儲存字串和整型資料。
接下來的圖解是一維陣列的兩個例子:第一個叫做 cities 的一維陣列由文本組成(字串資料型別
——$),第二個叫做 lotto 的一維陣列則包含六個抽獎號碼(整數資料類型——%)。
一維陣列 cities$ (字串資料型別)
Cities(1) Baltimore
Cities(2) Atlanta
Cities(3) Boston
Cities(4) Washington
Cities(5) New York
Cities(6) Trenton
一維陣列 lotto% (整數資料類型)
Lotto(1) 25
Lotto(2) 4
Lotto(3) 31
Lotto(4) 22
Lotto(5) 11
Lotto(6) 5
正如你看到的,每個陣列成員的內容和變數的資料類型是相匹配的。如果你想要在同一個陣列裡面
儲存不同資料類型的資料,那麼你必須將資料聲明為 Variant。
二維陣列是由行和列代表的資料表。表中每個成員的位置是由它的行和列號碼決定的。下面是一個
空的二維陣列的圖解。
行號 1
1 (1,1)
2 (2,1)
3 (3,1)
4 (4,1)
5 (5,1)
2
(1,2)
(2,2)
(3,2)
(4,2)
(5,2)
3
(1,3)
(2,3)
(3,3)
(4,3)
(5,3)
列號
注意,二維陣列裡的專案是如何有行和列索引指定的?在該圖解裡,陣列裡的第一個成員位於第一
行和第一列裡(1,1),而最後一個成員則位於第五行和第三列裡的(5,3)。下面,我們來給該陣列填
充一些資料。下面顯示的二維陣列儲存了國家名稱,它的貨幣名稱以及和美元的匯率。
Japan
(1,1)
Mexico
(2,1)
Canada
(3,1)
Norway
(4,1)
Japanese Yen
(1,2)
Mexican Peso
(2,2)
Canadian Dollar
(3,2)
Norwegian Krone
(4,2)
128.2
(1,3)
9.423
(2,3)
1.567
(3,3)
8.351
(4,3)
123
Hungary
(5,1)
Hungarian Forint
(5,2)
266.7
(5,3)
儘管 VBA 陣列最大可以擁有 60 維,但是,絕大多數人發現非常困難去想像超過三維的陣列。三維的
陣列是一個具有相同行數和列數的表格的集合。在三維陣列裡的每個成員由下面三個資料決定:行
號,列號和表格號。
技巧 7-1 陣列變數是什麼?
陣列是擁有共同名稱的變數的集合。一個典型的變數只能儲存一個資料,然而,一個陣列變數卻能
夠儲存大量的變數。你可以使用變數名稱和索引號來指向陣列中某個確定的資料。
技巧 7-2 下標變數
陣列變數的括弧裡的數位成為下標,而每個單獨的變數則稱為下標變數或成員。例如, cities(6)
是 cities 陣列裡的第六個下標變數(成員)。
2.聲明陣列
因為陣列也是變數,所以,你必須用聲明其它變數的類似方法聲明陣列——使用 Dim 語句。當你聲
明一個陣列時,你便設定了該陣列儲存資料所需要的記憶體空間。
我們來看看一個陣列聲明的例子:
Dim cities(6) As String
Dim daysOfWeek(7) As String
Dim lotto(6) As Integer
Dim exchange(5, 3) As Variant
注意,變數名稱後面帶有括弧以及括弧裡有數位。一維陣列要求括弧裡帶一個數位,這個數位決定
了這個陣列能夠儲存的最大成員數。二維陣列後面總是帶有兩個數位——第一個數位是行索引號,
而第二個數位是列索引號。在上面的例子裡,陣列 exchange 最多可以儲存 15 個資料(5*3=15)。
陣列聲明的最後一部份是定義陣列將要儲存資料的資料類型。陣列可以儲存下列任何一種資料類
型:Integer, Long, Single, Double, Variant, Currency, String, Boolean, Byte, or Date。
當你聲明了一個陣列,VB 會自動佔據足夠的記憶體空間,分配的記憶體空間取決於該陣列的大小和資料
類型。當你聲明一個名叫 lotto 的帶有 6 個成員的一維陣列時,VB 將留出 12 個位元組——陣列的每個成
員各占 2 個位元組(回想整型資料類型為 2 個位元組,因此 2*6=12)。陣列越大,儲存資料需要的記憶體空
間就越大。因為陣列會吃掉很多記憶體,並因此影響你電腦的運行,因此,建議你僅僅根據你可能使
用的成員數來聲明陣列。
3.陣列的上界和下界
VBA 預設將陣列的第一個成員設置為 0(譯者:索引號),因此,數位 1 代表陣列中的第二個成員,而
數字 2 則代表第三個,等等。因為數字編號起始於 0,所以,一維陣列 cities(6)包含從 0 到 6 的七個
成員。如果你寧願從 1 開始計數你陣列裡的成員,那麼你可以使用 Option Base 1 語句來強制指定該
陣列的下界。該指令必須置於 VBA 模組任何 Sub 語句上面的聲明部分。如果你不明確 Option Base 1,
那麼 VBA 在使用陣列是就會假定使用 Option Base 0 來從 0 開始編號你的陣列成員。
你也可以讓陣列從除 0 或 1 之外的數位開始編號,要達到該目的,你在聲明陣列變數時就必須明確該
陣列的邊界。陣列的邊界是指它最小和最大的索引號。我們來看看下面的例子:
Dim cities(3 To 6) As Integer
上面的語句聲明了一個帶有四個成員的一維陣列。陣列名稱稱後面括弧裡的數位明確了陣列的下界
(3)和上界(6)。該陣列的第一個成員編號為 3,第二個為 4,第三個為 5,以及第四個為 6。注意
下界和上界之間的關鍵字 To。
技巧 7-3 陣列範圍
Dim 語句明確的陣列的下標區間就稱為陣列的範圍,例如:Dim mktgCodes(5 To 15)
4.在 VBA 過程裡使用陣列
你聲明了陣列後,就必須給該陣列的每個成員賦值,這也經常成為“填充陣列”。我們來嘗試使用
一維陣列有規劃地顯示六個美國城市的清單:
1. 打開一個新工作簿,並保存為 Chap07.xls
2. 切換到 VB 編輯器視窗,並重新命名 VBA 工程為 Tables
124
3. 插入一新模組,重新命名為 StaticArrays
4. 輸入下列過程 FavoriteCities:
' start indexing array elements at 1 從 1 開始給陣列成員編號
Option Base 1
Sub FavoriteCities()
'now declare the array
Dim cities(6) As String
'assign the values to array elements

cities(1) = "Baltimore"
cities(2) = "Atlanta"
cities(3) = "Boston"
cities(4) = "Washington"
cities(5) = "New York"
cities(6) = "Trenton"
'display the list of cities
MsgBox cities(1) & Chr(13) & cities(2) & Chr(13) _
& cities(3) & Chr(13) & cities(4) & Chr(13) _

& cities (5) & Chr(13) & cities(6)


End Sub
在 FavoriteCities 過程開始之前,缺省的索引編號方式改變了,注意,Option Base 1 語句是位於
模組視窗 Sub 語句之上的。該語句告訴 VB 給陣列的第一個成員賦值數字 1,而不是缺省的 0。
陣列 cities()聲明為帶六個成員的字串類型變數。然後,給陣列的每個成員都賦上了值。最後的
語句使用 Msgbox 函數顯示城市清單。當你運行該過程時,城市名稱將會出現在分開的行上(參見圖
7-1)。你可以改變顯示資料的順序,改變索引號。
圖 7-1 你可以用 Msgbox 函數來顯示一維陣列的成員
5. 運行 FavoriteCities 過程並且檢查結果
6. 修改 FavoriteCities 過程,讓它逆序顯示城市名稱(從 6 到 1)
技巧 7-4 陣列成員的初始值
在給陣列成員賦值之前,該成員具有缺省值。數位變數的缺省值為 0,而字串變數的缺省值為空
字串。
5.陣列和迴圈語句
現在要執行一些例如填充陣列或顯示陣列成員的任務了,你在第六章裡學過的好些個迴圈語句(參
見 For…Next 和 For Each …Next 迴圈)就變得非常方便了。現在是時候將你所學到的技巧結合起來
使用了。如何重新編寫 FavoriteCities 過程,讓每個城市名稱在不同的資訊框裡顯示出來?
下面顯示的過程 FavoriteCities2 將原來過程的最後部分取代為 For Each…Next 迴圈:
Sub FavoriteCities2()
'now declare the array
Dim cities(6) As String
Dim city As Variant
'assign the values to array elements

125
cities(1) = "Baltimore"
cities(2) = "Atlanta"
cities(3) = "Boston"
cities(4) = "Washington"
cities(5) = "New York"
cities(6) = "Trenton"
'display the list of cities in separate messages

For Each city in cities


MsgBox city
Next
End Sub
注意 For Each…Next 迴圈使用的是 Variant 資料類型的變數 city。回想在前面的章節裡,For
Each…Next 讓你在一個集合的所有物件間或者一個陣列的所有的成員間迴圈,並且對每個物件或成
員執行同樣的操作。當你運行過程 FavoriteCities2 時,陣列裡有幾個成員迴圈就會執行幾次。
我們來看一下過程 FavoriteCities 的另一種變化。在第四章裡,你練習了將參數作為變數傳遞給子
過程和函數。過程 FavoriteCities3 示範了如何將陣列的成員傳遞給另一個過程。
1. 在當前模組裡,輸入下述兩個過程:
Sub FavoriteCities3()
'now declare the array
Dim cities(6) As String
'assign the values to array elements

cities(1) = "Baltimore"
cities(2) = "Atlanta"
cities(3) = "Boston"
cities(4) = "Washington"
cities(5) = "New York"
cities(6) = "Trenton"
'call another procedure and pass the array as argument

Hallo cities()
End Sub
Sub Hallo (cities() As String)
Dim counter As Integer
For counter = 1 to 6
MsgBox "Hello " & cities(counter)
Next
End Sub
過程 Hallo 的聲明裡有一個陣列類型的參數——cities()。
2. 運行過程 FavoriteCities3。將一個子過程的陣列成員傳遞給另一個子過程或者函數過程讓你
可以在許多過程裡使用相同的陣列,而不需要重複的程式碼。
技巧 7-5 在過程之間傳遞陣列
當一個陣列在一個過程裡被聲明時,它是局部的,並且是不為其他過程所知的。然而,你可以將局
部陣列傳遞給其它的過程,通過在聲明語句裡,寫上陣列名稱稱,並且後面緊跟一對空括弧。例如,
語句 Hallo cities() 調用一個名叫 Hallo 的過程,並且將陣列 cities()傳遞給它。
這裡有個例子,如何將你新學到的關於陣列的知識和迴圈運用到現實生活中。如果你是個狂熱的彩
票玩家的話,當你厭倦了選擇你的幸運號碼,你可以讓 VB 為你選擇。下面的過程 Lotto 使用 1 到 51
的六個數位填充陣列:
Sub Lotto()
Const spins = 6
Const minNum = 1
Const maxNum = 51
126
Dim t As Integer ‘looping variable in outer loop 外部迴圈變數
Dim i As Integer ‘looping variable in inner loop 內部迴圈變數
Dim myNumbers As String ‘string to hold all picks 儲存選號的字串
Dim lucky(spins) As String ‘array to hold generated picks 儲存產生的選號的陣列
myNumbers = ""
For t = 1 To spins
Randomize
lucky(t) = Int((maxNum-minNum+1) * Rnd )+ minNum)

'see if this number was picked before 檢查本數字是否之前被選出來過


For i = 1 To (t-1)
If lucky(t)=lucky(i) Then
lucky(t) = Int((maxNum–minNum+1) * Rnd)+ minNum) i = 0
End If
Next i
MsgBox "Lucky number is " & t & lucky(t)

myNumbers = myNumbers & " –" & lucky(t)


Next t
MsgBox "Lucky numbers are " & myNumbers

End Sub
Randomize 語句將亂數字發生器初始化。指令 Int((maxNum-minNum+1) * Rnd + minNum)使用函數
Rnd 來產生一個在 minNum 和 maxNum 之間的亂數值。函數 Int 將亂數轉變為一個整數。除了給
minNum 和 maxNum 賦予常量之外,你也可以使用函數 InputBox 從使用者那裡獲得資料。
內部 For…Next 迴圈確保每個選出的數字是唯一的——它不能是之前選出的任何一個數字。如果你
忽略了內部迴圈並且多次運行該過程,你很可能看到重複的號碼。
6.使用二維陣列
既然你已經知道了如何有規劃地產生一個清單(一維陣列),是時候仔細看一下如何使用資料表了。
下面的過程產生一個二維陣列,儲存國家名稱,貨幣名稱和交換匯率。
Sub Exchange()
Dim t As String
Dim r As String
Dim Ex(3, 3) As Variant
t = Chr(9) 'tab
r = Chr(13) 'Enter
Ex(1, 1) = "Japan"
Ex(1, 2) = "Yen"
Ex(1, 3) = 128.2
Ex(2, 1) = "Mexico"
Ex(2, 2) = "Peso"
Ex(2, 3) = 9.423
Ex(3, 1) = "Canada"
Ex(3, 2) = "Dollar"
Ex(3, 3) = 1.567
MsgBox "Country " & t & t & "Currency" & t & "per US$" _

&r&r_
& Ex(1, 1) & t & t & Ex(1, 2) & t & Ex(1, 3) & r _

& Ex(2, 1) & t & t & Ex(2, 2) & t & Ex(2, 3) & r _

& Ex(3, 1) & t & t & Ex(3, 2) & t & Ex(3, 3), , _

"Exchange"
End Sub
127
當你運行過程 Exchange 時,你將看到一個資訊框,顯示三列資訊(見圖 7-2)
圖 7-2 顯示在資訊框上的文本是可以自訂格式的。
7.靜態和動態陣列
到目前為止,本章介紹的都是靜態陣列。靜態陣列是具有確定大小的陣列。當你事先知道陣列的大
小時使用靜態陣列。靜態陣列的大小是在陣列的聲明語句裡確定的,例如,語句 Dim Fruits(10) As
String 聲明了一個由 10 個成員組成的叫做 Fruits 的靜態陣列。
但是,萬一你不肯定你的陣列會包含多少個成員呢?如果你的過程由使用者輸入決定,每次程式執行
時,用戶提供的成員數可能會變化的。你如果確保你聲明的陣列不會浪費記憶體呢?
回想你聲明了一個陣列後,VBA 會留出足夠的記憶體來儲存陣列。如果你聲明一個比你需要的更多成
員的陣列的話,你將浪費電腦資源。這個問題的解決方法是讓你的陣列變為動態的。動態陣列是
大小可以改變的陣列。如果陣列的大小每次都由程式運行而決定的話,就使用動態陣列。
技巧 7-6 固定大小的陣列
靜態陣列包含固定成員個數。靜態陣列的成員個數在它被聲明後就再也不能改變了。
要聲明動態陣列,那麼不要在陣列名稱稱後面的括弧裡放置數字:
Dim Fruits( ) As String
動態陣列通過在陣列名稱稱後面附帶空括弧來聲明。在你使用動態陣列於過程裡之前,你必須使用
ReDim 語句來動態地設置陣列的上界和下界。ReDim 語句隨著程式碼的執行重新設定陣列大小,
ReDim 語句通知 VB 陣列的新大小,這個語句可以在同一個過程裡多次使用。現在,我們來看看如何
使用動態陣列。
1. 在當前工程裡插入一個新模組並且重新命名為 DynamicArrays
2. 輸入下列過程 DynArray:
Sub DynArray( )
Dim counter As Integer
'declare a dynamic array
Dim myArray( ) As Integer
'specify the initial size of the array

Redim myArray(5)
Workbooks.Add
'populate myArray with values
For counter = 1 to 5
myArray(counter) = counter +1
ActiveCell.Offset(counter-1, 0).Value = myArray(counter)

Next
'change the size of myArray to hold 10 elements

Redim Preserve myArray(10)


'add new values to myArray
For counter = 6 To 10
myArray(counter) = counter * counter

With ActiveCell.Offset(counter-1, 0)

.Value = myArray(counter)
.Font.Bold = True
128
End with
Next counter
End Sub
3. 將你的 Excel 視窗和 VB 編輯器視窗並排顯示
4. 逐步運行過程 DynArray。你可以將滑鼠置於代碼中間,並且按下 F8 來執行逐條語句。程式
DynArray 的結果如下圖所示。
圖 7-3 顯示 10 個資料的陣列
在過程 DynArray 裡,Dim myArray() As Integer 語句聲明了一個叫做 myArray 的動態陣列。儘管該
語句聲明了陣列,但是,沒有分配任何記憶體給該陣列。第一條 ReDim 語句明確了 myArray 的開始大小
並且佔據了 10 個位元組的記憶體讓它儲存 5 個成員,正如你所知,每個整型資料需要兩個位元組的記憶體。
語句 Workbooks.Add 打開一新工作簿,然後 For…Next 迴圈用資料填充陣列 myArray 並且將陣列的成
員寫入工作表。在迴圈開始之前,變數 counter 等於 1。迴圈裡的第一條語句:
myArray(counter) = counter + 1
分配數值 2 給 myArray 的第一個成員。第二條語句:
ActiveCell.Offset(counter-1, 0).Value = myArray(counter)

將 myArray 成員的值輸入到目前的儲存格裡。目前的儲存格為 A1。因為變數 counter 等於 1,所以上面的


語句就等於:
ActiveCell.Offset(1-1, 0).Value = myArray(1)

或者
ActiveCell.Offset(0,0).Value = myArray(1)

上面的語句在儲存格 A1 裡輸入資料。迴圈裡面的語句被執行 5 次。VB 在合適的工作表儲存格裡馬輸


入資料並且進行到下一語句:
ReDim Preserve myArray(10)
通常,當你改變一個陣列的大小時,你將失去該陣列原來的所有資料。語句 ReDim 將陣列重新初始
化。
然而,你可以將新成員加入到現存的陣列裡去,通過在語句 ReDim 後面帶上關鍵字 Preserve。換句
話說,關鍵字 Preserve 保證重新改變大小的陣列不會弄丟現有的資料。如果你忽略它,新陣列將會
是空的。
第二個 For…Next 迴圈給陣列 myArray 的第六,第七,第八,第九和第十個成員賦值。這次,陣列成
員的數值是相乘的:counter * counter。VB 使用粗體將陣列其它的數值輸入到合適的工作表的單
元格裡面。
技巧 7-7 確定陣列大小
在使用陣列之前,必須在 Dim 或 ReDim 語句裡確定陣列的大小。這意味著你不可以給陣列成員賦值,
直到你使用 Dim 或者 ReDim 語句聲明了該陣列。
8.陣列函數
你可以通過五個 VBA 內置函數來運算元組:Array, IsArray, Erase, LBound 和 UBound。接下來的章
129
節將示範每個函數在 VBA 過程裡的使用。
9.Array 函數
Array 函數允許你在代碼執行中間創建一個陣列,而不必事先確定其大小。該函數總是返回一個
Varant 陣列。使用函數 Array 你可以快速地將一系列資料放置在一個清單裡面。下面的過程 CarInfo
創建了一個叫做 auto 的固定大小,一維的三個成員的陣列。
1. 在當前工程裡插入一新模組,重命名為 Array_Function
2. 輸入下列過程 CarInfo:
Option Base 1
Sub CarInfo()
Dim auto As Variant
auto = Array("Ford", "Black", "1999")

MsgBox auto(2) & " " & auto(1) & ", " & auto(3)

auto(2) = "4-door"
MsgBox auto(2) & " " & auto(1) & ", " & auto(3)

End Sub
另外一個例子,示範如何使用 Array 函數將列標輸入到工作表裡:
Sub ColumnHeads()
Dim heading As Variant
Dim cell As Range
Dim i As Integer
i=1
heading = Array("First Name", "Last Name", "Position", _

"Salary")
Workbooks.Add
For Each cell in Range("A1:D1")
cell.Formula = heading(i)
i = i+1
Next
Columns("A:D").Select
Selection.Columns.AutoFit
Range("A1").Select
End Sub
10.IsArray 函數
使用 IsArray 函數你可以測試某個變數是否陣列。如果該變數是個陣列,那麼 IsArray 函數返回 True,
否則返回 False。請看例子:
1. 在當前工程裡插入模組,命名為 IsArray_Function
2. 輸入如下過程 IsThisArray:
Sub IsThisArray()
'declare a dynamic array 聲明一動態陣列
Dim sheetNames() As String
Dim totalSheets As Integer
Dim counter As Integer
'count the sheets in the current workbook 計數當前工作簿裡的工作表數目
totalSheets = ActiveWorkbook.Sheets.Count

'specify the size of the array 明確陣列大小


ReDim sheetNames(1 To totalSheets)
'enter and show the names of sheets 輸入和顯示工作表名稱
For counter = 1 to totalSheets
sheetNames(counter) = ActiveWorkbook.Sheets(counter).Name

130
MsgBox sheetNames(counter)
Next counter
'check if this is indeed an array 檢查它是否確實為陣列
If IsArray(sheetNames) Then
MsgBox "The sheetNames is an array."

End If
End Sub
11.Erase 函數
當你要清除陣列裡的資料時,應該使用 Erase 函數。該函數刪除靜態或動態陣列儲存的所有資料,
另外,對於動態陣列,Erase 函數將重新分配原來分配給該陣列的所有記憶體。下面的例子教你如何
刪除陣列 cities 裡的資料。
1. 在當前工程裡插入一新模組,重命名為 Erase_Function
2. 輸入如下過程 FunCities:
' start indexing array elements at 1

Option Base 1
Sub FunCities()
'declare the array
Dim cities(1 to 5) As String
'assign the values to array elements

cities(1) = "Las Vegas"


cities(2) = "Orlando"
cities(3) = "Atlantic City"
cities(4) = "New York"
cities(5) = "San Francisco"
'display the list of cities
MsgBox cities(1) & Chr(13) & cities(2) & Chr(13) _

& cities(3) & Chr(13) & cities(4) & Chr(13) _

& cities (5)


Erase cities
'show all that was erased
MsgBox cities(1) & Chr(13) & cities(2) & Chr(13) _

& cities(3) & Chr(13) & cities(4) & Chr(13) _

& cities (5)


End Sub
在函數 Erase 清除陣列裡的資料後,函數 MsgBox 就顯示一個空資訊框了。
12.LBound 函數和 UBound 函數
LBound 函數和 UBound 函數分別返回表明陣列的下界和上界的數字。
1. 在當前工程裡插入模組,命名為 L_and_UBound_Function
2. 輸入如下代碼 FunCities2:
Sub FunCities2()
'declare the array
Dim cities(1 to 5) As String
'assign the values to array elements

cities(1) = "Las Vegas"


cities(2) = "Orlando"
cities(3) = "Atlantic City"
cities(4) = "New York"
cities(5) = "San Francisco"
'display the list of cities
131
MsgBox cities(1) & Chr(13) & cities(2) & Chr(13) _

& cities(3) & Chr(13) & cities(4) & Chr(13) _

& cities (5)


'display the array bounds
MsgBox "The lower bound: " & LBound(cities) & Chr(13) _

& "The upper bound: " & UBound(cities)

End Sub
當你要確定一個二維陣列的上下界時,你就必須明確維數:1 表示第一維,2 表示第二維。
在本章早先時候將的 Exchange 過程裡的後面加上如下語句,可以確定該二維陣列的上下界(將下列
代碼加入到關鍵字 End Sub 之前):
MsgBox "The lower bound (first dimension) is " _

& LBound(Ex, 1) & "."


MsgBox " The upper bound(first dimension) is " _
& UBound(Ex, 1) & "."
MsgBox "The lower bound (second dimension) is " _

& LBound(Ex, 2) & "."


MsgBox " The upper bound(second dimension) is " _

& UBound(Ex, 2) & "."


13.陣列中的錯誤
使用陣列時,出錯是很容易的。如果你試圖給陣列賦予比聲明陣列時更多的成員的話, VBA 就會顯
示錯誤資訊“下標越界”
圖 7-4 該錯誤出現於試圖訪問並不存在的陣列成員
假設你聲明了一個包含 6 個成員的一維陣列,而你卻試圖給第八個成員賦值,當你運行該過程時,
VB 無法找到第八個成員,所以顯示錯誤資訊。點擊調試按鈕,VB 將導致錯誤的代碼行(見圖 7-5)
加亮。檢查陣列的聲明語句,並且更改被加亮代碼行括弧裡的索引號。
“下標越界”錯誤經常是由使用迴圈的過程引發的。下面的過程 Zoo1 就是這種情況的一個例子。在
使用者取消在輸入框裡輸入資料之前,迴圈裡的語句反復被執行。在執行該過程時,當變數 i 等於 4
的時候,VB 無法在這個只有三個成員的陣列裡找到第四個成員,那麼錯誤資訊就出現了。修改後的
過程 Zoo2 示範了前面章節裡介紹的 LBound 和 UBound 函數如何能夠避免試圖訪問不存在的陣列成員
的錯誤。
132
圖 7-5 當你點擊錯誤資訊的調試按鈕,VB 就會加亮引發錯誤的語句
1. 在當前工程裡插入新模組,命名為 Errors_In_Arrays
2. 輸入下列過程 Zoo1 和 Zoo2:
Sub Zoo1()
'this procedure triggers an error "Subscript out of range" 本過程引發“下標越界”錯

Dim zoo(3) As String
Dim i As Integer
Dim response As String
i=0
Do
i = i +1
response = InputBox("Enter a name of animal:")

zoo(i) = response
Loop until response = ""
End Sub
Sub Zoo2()
'this procedure avoids the error "Subscript out of range"本過程避免“下標越界”錯誤
Dim zoo(3) As String
Dim i As Integer
Dim response As String
i=1
Do While i>=LBound(zoo) And i <=UBound(zoo)

response = InputBox("Enter a name of animal:")

If response = "" Then Exit Sub


zoo(i) = response
i=i+1
Loop
For i = LBound(zoo) To UBound(zoo)
MsgBox zoo(i)
Next
End Sub
133
另外一個使用陣列時經常碰到的錯誤是類型不匹配。要避免這類錯誤,就要牢記一個陣列的每個成
員都必須具有相同的資料類型。如果你試圖給陣列成員賦予和陣列聲明的資料類型矛盾的資料的
話,你就將在執行代碼時收到“類型不匹配”的錯誤。要讓一個陣列出錯不同類型的資料類型的話,
你就得聲明陣列為 Variant 類型。
14.陣列作為參數
在第四章裡面,你學習了資料可以在子過程或者函數過程之間作為必須或者可選參數傳遞。如果傳
遞的參數不是過程執行一定要的話,那麼這個參數名稱就應該在前面加關鍵字 Optional。然而,有
些時候,你事先並不知道你要傳遞多少個參數。一個典型的例子就是加法。你可能想要將兩個數字
加和,或者,你也許要加和 3 個,10 個,或者 15 個數字。使用關鍵字 ParamArray,你就可以將一個
包含任意個成員的陣列傳遞給你的子過程和函數過程。下面的函數過程 AddMultipleArgs 將加和你
所需要的任何多個數位。該函數以陣列 myNumbers 的聲明開始,注意關鍵字 ParamArray 的使用。該
陣列必須聲明為 Variant 類型,並且它必須是函數過程的最後一個參數。
1. 在當前工程裡插入一新模組,命名為 ParameterArrays
2. 輸入如下函數過程 AddMultipleArgs:
Function AddMultipleArgs(ParamArray myNumbers() As Variant)

Dim mySum As Single


Dim myValue As Variant
For each myValue in myNumbers
mySum=mySum+myValue
Next
AddMultipleArgs = mySum
End Function
3. 啟動立即視窗來試驗上面的函數,在立即視窗裡輸入指令:
?AddMultipleArgs(1, 23.24, 3, 24, 8, 34)

當你按下回車鍵,VB 就會返回上面參數的總和:93.24。你可以提供無限制的參數數目。注意,每
個函數的參數之間要用逗號分開。
15.接下來…
在本章裡,你學習了通過創建陣列,你可以編寫需要大量變數的過程。通過例子,示範了如何聲明
和使用一維陣列(清單)和二維陣列(表)。你也學習了靜態陣列和動態陣列之間的差別。本章的
結尾介紹了五個 VBA 內置函數和關鍵字 ParamArray,它們經常使用於陣列。
現在你已經知道了可以使你的程式更智慧的控制結構了:條件陳述式,迴圈和陣列。在本書的後面,
你將學習如何使用集合代替陣列來操作更大量的資料。通過使用從第一到第七章學到的知識,你現
在可以開始編寫 VBA 過程讓你的任務自動化了,而這些在你開始學習本書之前似乎是不可能的事情。
接下來的一章將講述文件和資料夾的管理。
第八章利用 VBA 操縱檔和資料夾
作者:Julitta Korol 翻譯:Tiger Chen Mar 5’ 2005
在工作過程中,你肯定訪問、創建、複製和刪除過成百上千的檔和資料夾。然而,你可能從未用
程式執行過這些任務。所以,現在就是機會。本章側重於專門處理檔和資料夾的 VBA 函數和指令。
通過使用這些函數,你將能夠:
• 獲得當前資料夾的名稱(CurDir 函數)
• 更改檔和資料夾名稱(Name 函數)
• 檢查某檔或資料夾是否存在於某硬碟上(Dir 函數)
• 獲取某檔最後修改的時間和時間(FileDateTime 函數)
• 獲取檔大小(FileLen 函數)
• 檢查和更改檔案屬性(GetAttr 和 SetAttr 函數)
• 更改缺省資料夾或者硬碟(ChDir 和 ChDrive 語句)
• 創建和刪除資料夾(MkDir 和 RmDir 語句)
• 複製和刪除檔或資料夾(FileCopy 和 Kill 語句)
134
此外,本章也給你往三類檔寫入或者讀取資料的知識:連續的,隨機的和二進位的檔。除了使
用 Excel 應用軟體介面之外,你將學習如何直接操作檔。在本章的最後,將給你介紹最新的操作
檔和資料夾的方法,通過利用稱為 Windows Scripting Host (WSH)的工具來操作檔和資料夾。
操作檔和資料夾
本節將討論多種操作檔和資料夾的函數。
1.獲取當前資料夾的名稱(CurDir 函數)
當你使用檔時,經常會需要知道當前資料夾的名稱,你使用 CurDir 函數輕易地獲取該資訊:
CurDir([drive])
Drive 是一可選參數,如果你忽略它,VBA 將使用當前驅動(drive)。
CurDir 函數返回一個檔路徑作為 Variant(變數)。如果要返回作為字串(String)的路徑的話,
就得使用 CurDir$(這裡的$是字串的型別宣告字元)。讓我們在立即視窗裡做些練習,練習使用
這些函數吧:
1. 打開一個新工作簿,並且切換到 VB 編輯器視窗
2. 啟動立即視窗並敲入下述代碼:
?CurDir
當你按下回車,VB 就顯示當前資料夾名稱,例如:
C:\
如果你有第二個硬碟(或者光碟機)的話,你可以獲取 D 盤上的當前資料夾,例如:
?CurDir(“D:\”)
如果你提供了一個並不存在的驅動字母的話,VB 就將顯示下述錯誤資訊:“設備不可用”
3. 要儲存當前驅動名稱到變數 myDrive,可以輸入下述指令:
myDrive = Left(CurDir$,1)
當你按下回車鍵時,VB 將當前驅動器的字母儲存到變數 myDrive
敲入下述指令並回車,可以檢查變數 myDrive 的內容:
?myDrive
你還可以將上面的指令改為如下:
myDrive = left(CurDir$,1) & ":"
VB 將返回驅動器字母,後面帶有一個冒號。
2.更改檔或資料夾名稱(Name 函數)
使用函數 Name 可以重命名檔或者資料夾,例如:
Name old_pathname As new_pathname
Old_pathname 是你想用重命名的檔或資料夾的名稱和路徑,New_pathname 則明確檔或資料夾的
新名稱和位置。使用函數 Name,你可以將一個檔從一個資料夾移動到另外一個資料夾,但是,你
不可以移動資料夾。
請在立即視窗裡試演該函數(用你檔的實際名稱替換示例名稱)。這裡有些需要考慮的注意事項:
• 在 New_pathname 裡的檔案名稱不要指向已經存在的檔
Name "C:\System.1st " As "C:\test.txt"

因為檔 C:\test.txt 已經存在於 C 盤,VB 將顯示錯誤資訊:“檔已存在”,同樣,如果你要重命名


的檔不存在的話,就會出現“檔未找到”的錯誤資訊。
• 如果 New_pathname 已經存在,並且和 Old_pathname 不同,函數 Name 必要時將檔移動到新文件
夾並且更改它的名稱。
Name "C:\System.1st " As "D:\test.txt"

因為檔 test.txt 在 D 盤的根目錄下並不存在,VB 將 C:\System.1 移動到指定的驅動盤,然而,並


st

不重命名該檔。(譯者:本段與上面的內容似乎矛盾,而且未能試驗成功,未知是原書失誤與否。
讀者應仔細驗證)
• 如果 New_pathname 和 Old_pathname 指向不同的目錄,以及提供的檔案名稱相同,那麼 Name 函數
將指定的檔移到新地址,不用更改檔案名。
Name "D:\test.txt " As "C:\DOS\test.txt"

上面的指令將 test.txt 移動到 C 盤下的 DOS 資料夾裡。


135
技巧 8-1 你不能重命名開啟的檔
在重命名檔之前,你必須關閉該檔。檔案名稱裡不能包含萬用字元“*”或者“?”。
3.檢查檔或資料夾是否存在(Dir 函數)
Dir 函數,返回檔或者資料夾名稱,語法如下:
Dir[(pathname[, attributes])]
Dir 函數的兩個參數都是可選的,pathname 是檔或資料夾名稱,對於參數 attributes,你可以下
列常量或者數值之一:
表 8-1 檔案屬性
常量
vbNormal
vbHidden
vbSystem
vbVolume
vbDirectory
數值
0
2
4
8
16
屬性名稱
Normal 普通檔
Hidden 隱藏文件
System 系統檔
Volume label 標籤
Directory or Folder 目錄或資料夾
Dir 函數常用來檢查某個檔或資料夾是否存在,如果不存在,那麼就返回空字串(””)。我們
到立即視窗裡試驗幾個 Dir 函數的練習:
1. 在立即視窗,輸入下述指令:
?Dir("C:\", vbNormal)
你一旦按下回車鍵,VB 就會返回該資料夾下的第一個檔案名。普通檔(vbNormal)就是除隱藏,
標籤,目錄,資料夾或系統檔之外的任何檔。
要返回目前的目錄下的其它檔案名稱的話,就使用不帶參數的 Dir 函數:
?Dir (並且回車)
2. 在立即視窗裡輸入下列指令,並且在你回車時檢查其結果:
mfile = Dir("C:\", vbHidden)
?mfile
mfile = Dir
?mfile
mfile = Dir
?mfile
3. 在立即視窗輸入下述指令:
If Dir("C:\stamp.bat") = "" Then Debug.Print "文件未找到。"
因為 stamp.bat 檔不在 C 盤上,所以 VB 就在立即視窗裡寫下文本資訊“檔未找到。”
4. 在立即視窗輸入下述語句,可以檢查某檔是否存在於某驅動盤上:
If Dir ("C:\Autoexec.bat") <>"" Then Debug.Print "該檔不在 C 盤上。"
函數 Dir 允許你在檔路徑名中使用萬用字元——星號(*)代表多個字元,問號(?)代表單個字元:
例如,要在 WINDOWS 資料夾中查找所有配置設置的檔,你可以查找所有的 INI 檔,如下:
?Dir("C:\WINNT\*.ini", vbNormal)
system.ini
?dir WIN.INI
?dir
WINFILE.INI
?dir control.ini
?dir EQUIP32.INI
?dir
sxpwin32.ini
下面顯示的過程在立即視窗裡寫上了確定目錄下的檔案名稱。函數 LCase$讓檔案名稱顯示為小寫字
母。
1. 打開一新工作簿,並保存為 Chap08.xls
136
2. 切換到 VB 編輯器視窗並重命名 VBA 工程為 FileMan
3. 插入新模組,重命名為 DirFunction
4. 輸入下述 VBA 過程:
Sub MyFiles()
Dim mfile As String
Dim mpath As String
mpath = InputBox("Enter pathname,e.g., C:\Excel")

If Right(mpath, 1) <> "\" Then mpath = mpath & "\"

mfile = Dir(mpath & "*.*")


If mfile <> "" Then Debug.Print "Files in the " & mpath _

& "folder"
Debug.Print LCase$(mfile)
If mfile = "" Then
MsgBox "No files found."
Exit Sub
End If
Do While mfile <> ""
mfile = Dir
Debug.Print LCase$(mfile)
Loop
End Sub
上面的過程 myFiles 向用戶詢問檔路徑名。如果該路徑結尾沒有反斜線,函數 Right 就會將反斜線
附加在路徑名字串上。接下來,VB 在該確定的資料夾裡搜索所有檔(*)。如果沒有檔的話,
就會有資訊顯示,如果檔存在,那麼檔案名就會被寫入立即窗口。
5. 在同一個模組裡輸入另外一個過程:
Sub GetFiles()
Dim nfile As String
Dim nextRow As Integer 'next row index
nextRow = 1
With Worksheets("Sheet1").Range("A1")

nfile = Dir("C:\", vbNormal)


.Value = nfile
Do While nfile <> ""
nfile = Dir
.Offset(nextRow, 0).Value = nfile
nextRow = nextRow + 1
Loop
End With
End Sub
過程 GetFiles 獲取 C 盤根目錄下的所有檔案名並且將每個檔案名寫入工作表。
4.獲得檔修改的日期和時間(FileDateTime 函數)
如果你的過程需要知道某檔的最後修改的時間的話,可以使用函數 FileDateTime:
FileDateTime(文件路徑名)
檔路徑名是個字串,明確你要用的檔,並且需要包括驅動和資料夾的名稱。該函數返回某文
件的日期和時間印記。日期和時間的格式取決於視窗控制台的原始設置。
我們在立即視窗裡來練習使用該函數:
1. 在立即視窗裡輸入:
?FileDateTime("C:\config.sys")
回車後,VB 返回下述格式的日期和時間
137
5/4/2001 10:52:00 AM
要分開獲取日期和時間時,可以將函數 FileDateTime 作為函數 DateValue 或 TimeValue 的參數來使
用。例如:
?DateValue(FileDateTime("C:\config.sys"))

?TimeValue(FileDateTime("C:\config.sys"))

2. 在立即視窗裡將下述語句在一行輸入:
If DateValue(FileDateTime("C:\config.sys"))< Date then Debug.Print "This file was not

modified today.”
Date 函數返回當前系統日期,也是視窗控制台的日期/時間對話方塊裡設定的。
5.獲得檔大小(FileLen 函數)
如果你需要檢查某檔是否能夠存在某磁片上,那麼你應該按照下述方式使用 FileLen 函數:
FileLen(文件路徑名)
FileLen 函數一位元組方式返回檔的大小。如果該檔已打開,那麼 VB 將返回該檔最後一個保存
時的大小。
假設你想要獲取 Windows 目錄下進行配置設置的所有檔的總大小:
1. 在當前工程裡插入新模組,並重命名為 FileLenFunction
2. 在代碼視窗了輸入過程 TotalBytesIni:
Sub TotalBytesIni()
Dim iniFile As String
Dim allBytes As Long
iniFile = Dir("C:\WINDOWS\*.ini")
allBytes = 0
Do While iniFile <> ""
allBytes = allBytes + FileLen("C:\WINDOWS\" & iniFile)

iniFile = Dir
Loop
Debug.Print "Total bytes: " & allBytes

End Sub
6.返回和設置檔案屬性(GetAttr 函數和 SetAttr 函數)
檔和檔夾具有類似“唯讀”,“隱藏”,“系統”和“檔案”的特點。這些特點就是屬性。可以使
用 GetAttr 函數來獲得檔或資料夾的屬性。該函數的唯一參數就是檔或資料夾路徑名:
GetAttr(文件路徑名)
上面的函數返回一個整數,代表下面顯示的常量中的一個或多個常量之和。
表 8-2 檔和資料夾屬性
常量
vbNormal
VbReadOnly
vbHidden
vbSystem
vbDirectory
vbArchive
數值
0
1
2
4
16
32
屬性名稱
普通檔(沒有設置其它屬性)
不可修改的檔或資料夾
在普通設置下不可見的檔或資料夾
系統檔
物件為一個目錄
檔案(在最後一次備份後,該檔被修改)
要知道某檔是否具有上述的屬性,可以使用 AND 運算子來比較 GetAttr 函數的結果和常量數值。如
果函數返回一個非零數值,那麼該檔或檔夾具有和你測試的屬性一樣的屬性。
C:\MsDos.sys 的屬性是什麼呢?你可以在立即視窗裡快速獲得:
?getattr("C:\MsDos.sys") AND vbReadOnly

1
?getattr("C:\MsDos.sys") AND vbHidden

138
2
?getattr("C:\MsDos.sys") AND vbSystem

4
?getattr("C:\MsDos.sys") AND vbArchive

32
現在,我們來將這些資訊一起放在一個過程裡:
1. 插入新模組,並重命名為 GetAttrFunction
2. 輸入下述過程 GetAttributes:
Sub GetAttributes()
Dim attr As Integer
Dim msg As String
attr = GetAttr("C:\MSDOS.SYS")
msg = ""
If attr AND vbReadOnly Then msg = msg & "Read-Only (R)"

If attr AND vbHidden Then msg = msg & Chr(10) & "Hidden (H)"
If attr AND vbSystem Then msg = msg & Chr(10) & "System (S)"

If attr AND vbArchive Then msg = msg & Chr(10) & "Archive (A)"

MsgBox msg, , "MSDOS.SYS"


End Sub
3. 當你運行上面的過程時,你將看到如圖 8-1 的資訊框
圖 8-1 使用 GetAttr 函數可以獲得任何檔的屬性
GetAttr 函數的相反函數是 SetAttr 函數,它允許你設置一個檔或資料夾的屬性。語法如下:
SetAttr 檔路徑名, 屬性
檔路徑名確定你要設置的檔或資料夾,第二個參數,屬性,是一個或多個你要設置的屬性常量。
參見表 8-1 本章前面介紹的常量清單。
假設你有一個叫做“C:\stamps.txt”的檔,並且要設置兩個屬性,“唯讀”和“隱藏”。在立即
視窗裡輸入下述指令來設置檔案屬性(可以找個你硬碟上存在的檔來試驗):
SetAttr "C:\stamps.txt", vbReadOnly + vbHidden

技巧 8-2 調用 SetAttr 語句
你不能給打開的檔設置屬性,在使用 SetAttr 函數之前,你必須關閉該檔。
7.更改缺省資料夾或驅動器(ChDir 語句和 ChDrive 語句)
使用 ChDir 語句,你可以輕易更改缺省資料夾,例如:
ChDir Path
在上面的語句中,Path 是新的缺省資料夾名稱。Path 可以包含驅動器名稱。如果 Path 沒有包括驅動
名稱,那麼缺省資料夾將會更改為當前驅動。當前驅動不變。
假設缺省資料夾為“C:\DOS”,語句:
ChDir "D:\MyFiles"
將缺省資料夾更改為“D:\MyFiles”,然而,當前驅動仍然是 C 盤。
要更改當前驅動的話,你就應該使用 ChDrive 語句,按如下格式:
ChDrive 驅動
“驅動”是你將要設置的新的缺省驅動名稱。例如,在立即視窗裡輸入下述指令將缺省驅動設置為
D 驅或者 E 驅:
139
ChDrive "D"
或者
ChDrive "E"
如果你指向一個並不存在的驅動,你就會看到一個資訊框“設備不可用”
8.創建和刪除資料夾(MkDir 語句和 RmDir 語句)
依照下面的 MkDir 語句語法,你可以創建一個新資料夾:
MkDir Path
Path 明確你要創建的新資料夾名稱。如果你沒有寫驅動器的名稱的話,VB 就將在當前的驅動上創建
新資料夾。現在,我們來看幾個例子:
1. 在立即視窗裡輸入指令,在 C 盤上創建一個叫“Mail”的資料夾:
MkDir "C:\Mail"
2. 將缺省資料夾更改為“C:\Mail”:
ChDir "C:\Mail"
3. 獲取當前資料夾名稱:
?CurDir
使用 RmDir 函數來刪除不需要的資料夾。該函數的語法如下:
RmDir Path
Path 明確你要刪除的資料夾名稱。Path 可以包含驅動名稱,如果你忽略了驅動名稱,那麼 VB 就會試
圖刪除當前驅動下的相同名稱的資料夾,如果存在的話;否則,VB 將顯示錯誤資訊:“路徑未找到”
4. 刪除剛才創建的資料夾 C:\Mail:
RmDir "C:\Mail"
技巧 8-3 RmDir 移除空資料夾
如果資料夾裡有東西,你不可以刪除它(使用 RmDir)。你應該先用 Kill 語句刪除這些檔(在本章
後面討論)
9.複製檔(FileCopy 語句)
使用 FileCopy 語句,可以在資料夾之間複製檔:
FileCopy 來源, 目的地
該語句的第一個參數是檔來源,明確你要複製的檔案名稱,該名稱可以包含驅動名稱。第二個參
數是複製的目的地,可以包括驅動和資料夾的位址。兩個參數都是必須的。假設你要將用戶確定的
一個檔複製到一個叫做“C:\Abort”的資料夾,下面的過程示範如何完成它:
Sub CopyToAbort()
Dim folder As String
Dim source As String
Dim dest As String
Dim msg1 As String
Dim msg2 As String
Dim p As Integer
Dim s As Integer
Dim i As Long
On Error GoTo ErrorHandler
folder = "C:\Abort"
msg1 = "The selected file is already in this folder."

msg2 = "was copied to"


p=1
i=1
' get the name of the file from the user 從用戶處獲取檔案名稱
source = Application.GetOpenFilename

' don’t do anything if cancelled 如果取消則不進行任何操作


If source = "False" Then Exit Sub
140
' get the total number of backslash characters "\" in the source 獲取檔來源字元
串中的反斜線數
' variable’s contents
Do Until p = 0
p = InStr(i, source, "\", 1)
If p = 0 Then Exit Do
s=p
i=p+1
Loop
' create the destination file name 創建目的檔案名稱
dest = folder & Mid(source, s, Len(source))

' create a new folder with this name 創建同名資料夾


MkDir folder
' check if the specified file already exists in the 檢查該檔是否在目的地已經存

' destination folder
If Dir(dest) <> "" Then
MsgBox msg1
Else
' copy the selected file to the C:\Abort folder 複製所選檔到資料夾“C:\Abort”
FileCopy source, dest
MsgBox source & " " & msg2 & " " & dest

End If
Exit Sub
ErrorHandler:
If Err = "75" Then
Resume Next
End If
If Err = "70" Then
MsgBox "You can’t copy an open file."
Exit Sub
End If
End Sub
過程 CopyToAbort 使用了 Excel 應用程式的方法 GetOpenFilename 從使用者那裡獲取檔案名稱。該方法
導致彈出內置的打開對話方塊。使用該對話方塊,你可以在任何驅動的任何資料夾裡選擇任何檔。如
果用戶取消了,VB 就返回值“False”並且程式結束。如果用戶選取了某個檔並且點擊了打開,
那麼該選中的檔就會賦值到變數 source。因為複製的目的,你只需要檔案名稱(而不需路徑名),
所以 Do…Until 迴圈用來找到最後一個反斜線(“\”)在變數 source 裡的位置。
接下來,VB 給 FileCopy 語句的第二個參數準備了一個字母字串,並且將其賦值到變數 dest。該變
量儲存的字串是目的檔案夾(C:\Abort)和用戶指定的檔案名前面加反斜線連接起來的。函數
MkDir 創建了一個叫 C:\Abort 的資料夾,如果它不存在於 C 盤上的話。如果這樣的資料夾已經存在的
話,那麼 VB 就需要去處理錯誤 75 了。這個錯誤會被在程式後面的錯誤處理代碼捕獲。注意,錯誤處
理器是一代碼片斷,它用 ErrorHandler 帶冒號標誌。
當 VB 遇到 Resume Next 語句時,就會繼續執行過程裡面導致錯誤的代碼行下面的代碼。這意味著語
句 MkDir folder 不會被執行。在這之後,程式將檢查被選擇的檔是否已經存在於目的資料夾。如
果檔在那,那麼使用者將收到儲存於變數 msg1 裡面的資訊;如果檔不存在於目的資料夾並且該文
件當前沒有打開的話,VB 就會將檔複製到指定的資料夾,並且用相應的資訊通知使用者。如果該文
件被打開了,VB 將遇到執行時間錯誤 70,並且因此而運行 ErrorHandler 裡面的相應指令。
1. 在一名為 FileCopyStatement 的信魔窟裡輸入過程 CopyToAbort
2. 運行該程式幾次,從不同的資料夾裡選擇檔
141
3. 試著複製該程式之前複製過的檔到資料夾 C:\Abort
4. 打開某個檔,並且在其開著的情況下試圖用過程 CopyToAbort 來複製它
5. 運行本章前面準備的過程 MyFiles,在立即視窗裡列出資料夾 C:\Abort 裡面的內容
注意,不要刪除資料夾 C:\Abort 和你複製的檔,你將在下一節裡面使用一個叫 RemoveMe 的 VBA 過
程來同時刪除檔和資料夾。
10.刪除檔(Kill 語句)
你已經從前面的章節裡知道了不能刪除含有檔的資料夾,要從資料夾裡面刪除檔的話,可以使
用下麵的 Kill 語句:
Kill 文件路徑名
檔路徑名明確一個或多個你要刪除的檔的名稱,隨你意,也可以將驅動器和資料夾名稱包括在
裡面。你可以在檔路徑名參數裡使用萬用字元(*或?)來確保快速刪除檔。你不能刪除開啟的文
件。
如果你是跟著前面的練習一步一步過來的,那麼你的硬碟上應該有了資料夾 C:\Abort 和好幾個文件
了裡面了。在下面的練習裡,你將首先刪除資料夾 Abort 裡面的所有檔,然後再刪除資料夾本身:
1. 在當前工程裡插入新模組,並重命名為 KillStatement
2. 在過程 RemoveMe 裡輸入代碼,如下所示:
Sub RemoveMe()
Dim folder As String
Dim myFile As String
‘assign the name of folder to the folder variable
‘notice the ending backslash "\"
folder = "C:\Abort\"
myFile = Dir(folder, vbNormal)
Do While myFile <> ""
Kill folder & myFile
myFile = Dir
Loop
RmDir folder
End Sub
3. 運行過程 RemoveMe,當程式運行結束,點擊 Windows 檔流覽器看看該資料夾是否已經被刪除了。
11.從檔讀取和寫入資料(Input/Output)
你已經從前面的章節裡知道了如何使用 VBA 打開一個試算表,例如指令:
Application.Workbooks.Open Filename:= "C:\Excel\Report.xls"

打開位於資料夾 C:\Excel 裡面的文件 Report.xls。除了使用專門的應用程式打開檔之外,你如果


也想要創建 VBA 過程能夠打開其它類型的檔並使用它們的內容的話,你就應該學習一些關於被稱
為低級別的文件 I/O(input/output)。接下來關於順序,隨機和二進位檔案的章節將會帶你直接接
觸你的數據。
12.檔訪問類型
電腦使用的檔案類型有三種:
• 循序存取檔是指按儲存相同的順序找回資料的檔。例如以 CSV 格式(逗號分割文本),TXT
格式(以 Tab 鍵分割的文本)或者 PRN 格式(以空格分隔的文本)儲存的檔。順序檔訪問經
常用來寫文字檔,例如錯誤日誌,參數設定和報告。順序檔有下列模式: Input, Output 和
Append。模式決定了檔打開後你如何使用它。
• 隨機訪問檔是文字檔,它的資料以同等長度儲存並在一個以逗號分割的區域了。隨機訪問
檔只有一個模式——Random
• 二進位訪問檔是圖形檔和其它非文字檔。二進位檔案只能夠在 Binary 模式下訪問。
142
13.使用順序檔
你的電腦硬碟上有成百上千的順序檔。參數檔,錯誤日誌,HTML 檔以及所有類型的無格式文
本檔都是順序檔。這些檔以字母順序在硬碟上儲存。新文本行的開始以兩個專門的字元表示,
一個叫做 carriage return (回車),另一個叫 line feed(換行)。當你使用順序檔時,你從文
件的開頭始,一個字元一個字元的向前移動,一行接一行,直到檔的結尾。順序檔容易打開和
操作,任何文字編輯器都可以。
技巧 8-4 什麼是順序檔?
順序檔就是訪問它裡面的記錄時必須按它佔據的順序進行的檔,這意味著在你想訪問第三個記
錄之前,你必須先訪問第一個記錄,接著是第二個記錄。
技巧 8-5 使用 Open 語句打開檔
當你使用循序存取來打開一個檔時,該檔必須是已經存在的。
14.讀取儲存於順序檔裡的資料
我們來用一個已經在你電腦上的順序檔並且在 Excel VB 編輯器視窗直接使用 VBA 來讀取它的內
容。要從一個檔讀取資料,你就必須先使用 Open 語句打開該檔。這是它的語法:
Open pathname For mode [Access access][lock] As [#]filenumber [Len=reclength]

Open 語句有三個必須的參數,它們是 pathname, mode, 和 filenumber。上面的語法裡,這三個參


數前面都有用粗體顯示的關鍵字。
• Pathname 是你要打開的檔案名稱
• Pathname 可以包括驅動器和資料夾名稱
• Mode 是個決定文件如何打開的關鍵字。順序檔可以以下列模式之一來打開:Input, Output 或
Append。使用 Input 讀檔,Output 寫檔,將覆蓋任何存在的檔,以及 Append 來寫入檔,
同時附加上任何已經存在的資訊。
• Access 是決定決定檔讀寫的關鍵字,Access 可以是:Shared(共用),Lock Read(鎖定讀),
Lock Write(鎖定寫)或 Lock Read Write(鎖定讀寫)。
• Lock 決定了哪些檔的操作是允許其它過程進行的。例如,如果某檔是在網路環境下打開的,
“鎖定”決定了其他人如何訪問它。下述鎖定關鍵字是可以用的:Read, Write 或者 Read Write。
• Filenumber 是從 1 到 511 的數位,該數位用來指向順序操作中的檔。通過使用 VB 內置函數
FreeFile,你可以獲得一個唯一的檔號碼。
• Open 語句裡的最後一個成員 reclength 明確順序檔裡總字元數,或者是隨機檔裡記錄大小。
考慮一下前面的例子,為了讀取資料,要打開 C:\Autoexec.bat 或者其它順序檔,你應該使用下
面的指令:
Open "C:\Autoexec.bat" For Input As #1

如果某檔已經打開輸入了,那麼從它讀取資料。在打開一格順序檔後,你就可以使用下面的語
句讀取它的內容:Line Input #或者 Input # 或者使用 Input 函數。
15.逐行讀取文件
使用下面的語句來逐行讀取 Autoexec.bat 或者其它任何順序檔裡的內容:
Line Input #filenumber, variableName

#filenumber 是用 Open 語句打開檔時使用的數位,variableName 是個 String 或者 Variant 變數,用


來儲存讀取的行。
Line Input #語句僅讀取一開啟順序檔裡的一行並且儲存在一變數裡。記住,Line Input # 語
句一次讀取順序檔裡的一個字元,直到它遇到回車字元(Chr(13))或者回車-換行字元(Chr(13)
& Chr(10))。這些字元(回車,換行)在讀取過程中返回的文本裡是會忽略掉的。
接下來的過程 ReadMe 示範如何使用 Open 和 Line Input #語句逐行讀取 Autoexec.bat 檔的內容。試
試用同樣的方法來讀取其它順序檔。
1. 在當前工程裡面插入新模組並重命名為 SeqFiles
2. 輸入下列過程 ReadMe:
Sub ReadMe()
Dim rLine As String
143
Dim i As Integer ' line number
i=1
Open "C:\Autoexec.bat" For Input As #1

' stay inside the loop until the end of file is reached

Do While Not EOF(1)


Line Input #1, rLine
MsgBox "Line " & i & " in Autoexec.bat reads: " _

& Chr(13) & Chr(13) & rLine


i=i+1
Loop
MsgBox i & " lines were read."
Close #1
End Sub
3. 按下 F8,逐句運行該過程
為了讀取內容,過程 ReadMe 將檔 Autoexec.bat 在模式 Input 裡作為檔號碼 1 打開。Do…While 循
環告訴 VB 一直執行迴圈裡面的語句,直到到達檔結尾。檔的結尾由函數 EOF 的結果決定。
EOF 函數當下個要讀取的字元已經過了檔結尾時,返回邏輯值 True。注意,EOF 要求一個參數——
你要檢查的打開了的檔號碼,是前面 Open 語句使用的同一個數位。使用 EOF 函數來確保 VB 不會超
出文件結尾處。
Line Input # 語句將每行內容儲存於變數 rLine 裡,然後,資訊框顯示行號和它的內容。之後如果
函數 EOF 的結果還是為假(還未到達檔結尾處)的話,VBA 給行計數器增加 1,並且開始讀取下一
行。當函數 EOF 結果為真是,VB 就會退出迴圈。在 VBA 結束前,還會再運行兩條語句,顯示讀取行的
總數,以及關閉該打開的檔。
16.從順序檔中讀取字元
假設你的程式需要檢查檔 Autoexec.bat 裡出現了多少個冒號,你可以使用函數 Input 來返回特定
的字元數,而不必讀取整行。接下來,If 語句用來比較獲取的字元和你尋找的字元。在寫過程之前,
我們來看看函數 Input 的語法:
Input(number, [#]filenumber)
Input 函數的兩個參數都是必須的,number 明確你要讀取的字元數,而 filenumber 是 Open 語句用來
打開檔的同一個數位。Input 函數返回所有讀取的字元,包括逗號,回車,檔結束字元,引號
和前導空格。
1. 在 SeqFile 模組裡輸入下述過程 Colons:
Sub Colons()
Dim counter As Integer
Dim char As String
counter = 0
Open "C:\Autoexec.bat" For Input As #1

Do While Not EOF(1)


char = Input(1, #1)
If char = ":" Then
counter = counter + 1
End If
Loop
If counter <> 0 Then
MsgBox "Characters found: " & counter

Else
MsgBox "The specified character has not been found."

End If
Close #1
144
End Sub
2. 逐句執行該過程
3. 將冒號換成其它你想尋找的字元並且重新執行該程式。Input 函數允許你返回順序檔的任何字
符。如果你使用 VB 函數 LOF 作為 Input 函數的第一個參數時,你將能夠快速地讀取順序檔裡的
內容,而不需要在整個檔上迴圈。LOF 函數返回一個檔上的位元組數。每個位元組對應了文本文
件裡的一個字元。過程 ReadAll 將檔 System.ini 的內容讀取到立即視窗裡:
Sub ReadAll()
Dim all As String
Open "C:\WINNT\System.ini.bat" For Input As #1

all = Input(LOF(1), #1)


Debug.Print all
Close #1
End Sub
除了將檔內容列印到立即視窗之外,你還可以將其讀取到一個文字方塊並且放置到工作表中去(見
圖 8-2):
Sub WriteToTextBox()
Dim mysheet As Worksheet
Set mysheet = ActiveWorkbook.Worksheets(1)

On Error GoTo CloseFile


Open "C:\WINNT\System.ini" For Input As #1

mysheet.Shapes(1).Select
Selection.Characters.Text = Input(LOF(1), #1)

CloseFile:
Close #1
End Sub
圖 8-2 文件 TDate.ini(譯者:原文為 System.ini)的內容顯示在工作表中的文字方塊裡
在你運行上面的程式之前,你得在工作表裡畫一個文字方塊。注意,On Error GoTo CloseFile 語句
啟動錯誤捕捉,如果錯誤在程式的執行過程中發生了,就會立即跳到 CloseFile 標籤處,Close #1
語句無論有無錯誤發生都會被執行。
17.讀取分隔文字檔
在某些文字檔中(檔通常保存為 CSV,TXT 或 PRN 格式)輸入在每行的資料由逗號,Tab 或者空格
分隔。這種類型的檔用 Input # 語句可以比前面介紹的 Line Input #語句讀取更快些。Input #
語句允許你從一個打開的檔中讀取資料到好幾個變數,該函數如下所示:
Input #filenumber, variablelist
Filenumber 是用 Open 語句打開檔時的同一個號碼,variablelist 是一個以逗號分開的變數清單,
145
用來儲存讀取的的資料。你不能使用陣列或物件變數,然而,你可以使用使用者定義的變數(這種變
量將在本章後面介紹)。
下面的例子是一個用逗號分隔資料的順序檔:
Smith,John,15
Malloney,Joanne,28
Ikatama,Robert,15
要讀取該格式的文本的話,你必須給每個資料明確一個變數:姓,名和年齡。
1. 打開一個新工作簿,並且輸入下面的資料:
2. 將檔保存為 CSV 格式在 C:\Winners。Excel 將顯示資訊告訴你,該格式檔不支援含有多個工
作表的工作簿。點擊確定,只保存當前工作表。
3. 輸入下面的過程 Winners:
Sub Winners()
Dim lname As String, fname As String, age As Integer

Open "C:\Winners.csv" For Input As #1

Do While Not EOF(1)


Input #1, lname, fname, age
MsgBox lname & ", " & fname & ", " & age

Loop
Close #1
End Sub
4. 在運行過程 Winners 之前,你要確保該檔在指定的路徑下,或者在程式裡指定檔 Winners.csv
的正確位置。
上面的程式打開檔 Winners.csv 讀取資料,並且建立了一個 Do…While 迴圈,在整個檔裡運行直
到文件的結尾。Input #1 語句用來將每行的內容讀取到三個變數:lname, fname 和 age,然後,一
個資訊框將這三個變數的內容顯示出來。程式最後關閉檔 Winners.csv。
18.往順序檔裡寫資料
當你要往一個順序檔裡寫入資料時,你應該以 Append 或者 Output 模式打開該檔。這些模式的區
別解釋如下:
• Append 允許在一個現存檔的結尾處添加資料。例如,如果你以 Append 模式打開 Readme.txt 文
件,並且將文本“謝謝你閱讀本檔”加到該檔,VB 不會刪除或者以任何方式改變該檔中
已經存在的文本,但是,會在檔的結尾處加上新的文本。
• Output 當你以 Output 模式打開一個檔時,VB 將會將檔裡的現存的資料刪除,而且,如果
該檔並不存在的話,就會創建一個全新的檔。例如,如果你以 Output 模式打開檔
Readme.txt,並且試圖往裡面寫資料的話,那麼以前儲存在該檔裡的文本就會被刪除掉。如
果你在寫入資料之前沒有備份該檔的話,那該失誤的代價將會是非常大的。如果你想要用新
資料取代整個內容的話,就應該以 Output 模式打開該已存在的檔。
這裡有些例子,什麼時候應該用 Append,什麼時候用 Output:
• 要在文件 C:\Readme.txt 後面添加新文本,按下面以 Append 模式打開該文本:
Open "C:\Readme.txt" For Append As #1
• 要在一個叫 C:\Result.txt 的全新檔裡輸入一些文本,那麼以 Output 模式打開該檔:
Open “C:\Result.txt” For Output As #1
• 要取代現存檔 C:\Winners.csv 的內容,首先將原始檔案備份一份,然後將原始檔以 Output
146
模式打開:
FileCopy "C:\Winners.csv","C:\Winners.old"

Open "C:\Winners.csv" For Output As #1

技巧 8-6 不可同時讀寫
順序檔必須分別打開來執行讀和寫的操作,你不可以同時執行這些操作。例如,在一個檔已經
打開並且寫入資料後,該檔必須先關閉,之後才能再打開來讀取資料。
技巧 8-7 順序檔的優勢和劣勢
儘管順序容易創建和使用,並且不回浪費空間,但是它們也有很多不好的地方。例如,要是不讀一
大部分檔內容的話,你是很難找到某個特定專案的。同時,檔的個別專案不容易改變或刪除—
—你必須重新寫入整個檔。在有,在技巧 8-6 例說的,順序檔必須分開進行讀和寫的操作。
19.使用 Write # 和 Print # 語句
既然打開一文字檔來寫入資料的兩種方法(Append 或 Output)你都已經知道了,那麼是時候學習
Write #和 Print #語句了,它們讓你將資料發送到檔。當你使用 Input #語句從一個順序檔讀
取資料的時候,通常可以使用 Write #語句往該檔寫資料,如下所示:
Write #filenumber, [outputlist]
Filenumber 明確你正使用的檔的號碼,它是 Write #語句的唯一必須的參數。Outputlist 是你要
寫入的文本。Outputlist 可以是你要寫入的單個文本字元,也可以是包含資料的變數清單。如果你
只明確了檔號碼,VB 就會在打開的檔裡寫入一個空行。
我們了準備一個文字檔,裡面是三個人的名,姓,生日和兄弟姐妹的數目,演示資料是如何寫入
文件的:
1. 在當前模組裡輸入過程 DataEntry:
Sub DataEntry()
Dim lname As String
Dim fname As String
Dim birthdate As Date
Dim s As Integer
Open "C:\My Documents\Friends.txt" For Output As #1

lname = "Smith"
fname = "Gregory"
birthdate = #1/2/63#
s=3
Write #1, lname, fname, birthdate, s

lname = "Conlin"
fname = "Janice"
birthdate = #5/12/48# s = 1
Write #1, lname, fname, birthdate, s

lname = "Kaufman"
fname = "Steven"
birthdate = #4/7/57#
s=0
Write #1, lname, fname, birthdate, s

Close #1
End Sub
上面的過程打開檔 C:\My Documents\Friends.txt 來寫入資料。因為該檔還不存在於你的硬碟
上,所以 VB 就創建了一個全新的檔並寫入三個記錄。寫入檔的資料儲存在變數上。注意,這些
字串由雙引號分隔,而生日則有井號包圍起來了。
當你使用 Windows 記事本打開檔 Friends.txt 是,你將看到下述輸入:
"Smith","Gregory",#1963-01-02#,3
"Conlin","Janice",#1948-05-12#,1
147
"Kaufman","Steven",#1957-04-07#,0
注意,Write #語句自動在每個資料之間插入逗號並且將行結束字元(Chr(13) & Chr(10))放在每行
文本的後面,以至於每行新紀錄都從新的行開始。在上面的例子裡面,每行文本顯示一條記錄——
每條記錄以姓開始,以同胞數目結束。
如果你想要將資料顯示在一列中,而不是用逗號分隔資料,那麼就使用 Print #語句。例如,如果
將上面的程式 DataEntry 裡的 Write #語句用 Print #語句代替的話,那麼 VB 就會按下面的方式寫入
數據:
Smith Gregory 1/2/63 3
Conlin Janice 5/12/48 1
Kaufman Steven 4/7/57 0
儘管 Print #語句和 Write #語句的語法一樣,但是,Print #以一個準備列印的格式將資料寫入順
序文件。清單裡的變數可能用分號或者空格分隔。要列印多個空格的話,你就應該使用 Spc(n)指令,
這裡 n 是空格數。類似地,要將資料登錄到第五列的話,你就應該使用指令 Tab(5)。
我們來看一些格式例子:
• 使用帶逗號的 Write #語句,往檔裡輸入空行
Write #1,
• 在第五列輸入文本“fruits”
Write #1, Tab(5); “fruits”
• 用五個空格分隔開單詞“fruits”和“vegetables”
Write #1, “fruits”; Spc(5); “vegetables”
20.操作隨機檔
當某檔包含結構資料時,就以隨機模式打開它。以隨機模式打開檔讓你能夠:
• 同時讀寫
• 快速訪問某特別記錄
在隨機檔裡,所有記錄都是等長度的,並且每條記錄都有相同數目的固定大小區域。記錄或者區
域的長度必須在檔寫入資料之前就確定。如果寫入某區域的字串長度小於該區域的大小,那麼
VB 就會自動在該字串後面加空格來填充區域。如果寫入的文本比區域長度長的話,超出的字元就
不會被寫入。
要知道如果操作隨機檔,你現在就需要創建一個小資料庫用作外語學習。該資料庫將包含由兩個
區域組成的記錄,儲存英語片語和其外語等同語。
技巧 8-8 隨機檔是什麼?
隨機檔是儲存的記錄可以隨機訪問的檔,這意味著隨機檔裡的任何記錄都可以讀取,而不必
讀取它之前的每條記錄。
21.創建用戶定義的資料類型
除了第三章裡介紹的內置資料類型之外,VB 允許你在模組的上面使用 Type…End Type 語句定義一個
非標準的資料類型。該非標準資料類型也經常成為用戶自訂的資料類型。用戶自訂資料類型可
以包括各種資料類型(字串,整型,日期等等)的內容。當你使用隨機訪問的檔時,你經常要
創建一使用者定義的變數,因為,該變數使你可以輕易地訪問個別記錄。
1. 在當前工程裡插入新模組並且重命名為 RandomFiles
2. 在模組上面,緊接著 Option Explicit 語句下面,輸入下述類型定義:
Option Explicit
' define a user-defined type
Type Dictionary
en As String * 16 ' English word up to 16 characters

sp As String * 20 ' Spanish word up to 20 characters

End Type
用戶定義的名為 Dictionary 的類型包括兩個聲明為 String(字串)的專案,並且有特定的大小。
成員 en 可以接受最多 16 個字元,第二個專案 sp 的大小不能超過 20 個字元。如果你將這兩個成員的長
度加起來,那麼記錄長度將為 36(16+20)。如果模組了已經有了 Option Explicit 語句的話,你就
148
不必再輸入它了。
3. 輸入下面的過程 EnglishToSpanish
Sub EnglishToSpanish()
Dim d As Dictionary
Dim RecNr As Long
Dim choice As String
Dim totalRec As Long
RecNr = 1
'open the file for random access 打開檔作隨機訪問
Open "Translate.txt" For Random As #1 Len = Len(d)

Do
' get the English word 活動英文詞語
choice = InputBox("Enter an English word", "ENGLISH")

d.en = choice
' exit the loop if cancelled 如取消則退出迴圈
If choice = "" Then Exit Do
choice = InputBox("Enter the Spanish equivalent for " _

& d.en, "SPANISH EQUIVALENT " & d.en)

If choice = "" Then Exit Do


d.sp = choice
' write to the record 寫入記錄
Put #1, RecNr, d
' increase record counter 增加記錄計數器
recNr = recNr + 1
Loop Until choice = "" 'ask for words until Cancel 詢問詞語直到取消
totalRec = LOF(1) / Len(d)
MsgBox "This file contains " & totalRec & " record(s)."

' close the file


Close #1
End Sub
過程 EnglishToSpanish 開始時,聲明四個變數,變數 d 聲明為使用者定義的類型 Dictionary。該類型
在前面就用 Type 語句聲明了( 見第二步)。在給變數 RecNr 賦予了初始值之後, VB
打開文件
Translate.txt,並且將其作為檔編碼 1 隨機訪問。指令 Len(d)告訴 VB 每條記錄的大小為 36 字元。
接下來 VB 執行 Do…Until 迴圈裡面的語句,直到你取消。迴圈裡的第一條語句提示你輸入一個英語
單詞,並且將其賦予變數 choice,然後該值被傳遞給用戶定義 d 的第一個成員(d.en)。
一旦你停止輸入資料,VB 就會退出 Do 迴圈,並且執行程式裡的最後的語句計算和顯示檔裡的記錄
總數。最後一條語句將檔關閉。如果你輸入了英文詞語並點擊確定,那麼下個對話方塊就會提示你
輸入外語等同語。當然,如果你決定現在就退出的話,VB 就會跳出迴圈並且繼續剩下的語句。如果
一切正常,你輸入了外語翻譯,那麼 VB 就會將它賦予變數 choice 並且傳遞給使用者自訂變數 d 的第
二個成員(d.sp),接下來,VB 使用下述語句將整條記錄寫入到檔裡:
Put #1, recNr, d
寫入第一條記錄後,VB 會給記錄計數器增加 1,然後重複迴圈裡的語句。過程 EnglishToSpanish 允
許你在你的字典裡輸入任意多條記錄。當你退出提出詞語時,過程使用 LOF 和 Len 函數來計算檔裡
的總記錄數。VB 在顯示資訊後關閉該文字檔(Translate.txt)。
創建隨機檔僅僅是個開始,接下來,過程 VocabulartDrill 示範如何使用一個開啟的隨機檔的
記錄。這裡,你將學習讓你快速找到你檔中適合的資料的語句。
技巧 8-9 理解 Type 語句
Type 命令允許你創建一個自訂群組,包括混合的變數類型,稱為“使用者自訂資料類型”。Type 語
句通常用於隨機檔,將資訊作為區域儲存為固定大小的記錄。我們可以將隨機檔用的區域使用
Type 語句集中成為一個用戶自訂,而不必為每個區域都聲明一個變數。例如,如下所示定義一個
149
包含三個區域的記錄:
Type MyRecord
country As String * 20
city As String * 14
rank As Integer
End Type
一旦定義了類型,你必須給使用這種類型的變數名稱:
Dim myInfo As MyRecord
使用變數名稱後面加上句號和內部變數可以訪問內部變數(country, city, rank),例如,要明確
城市,輸入:
MyInfo.city = "Warsaw"
4. 在過程 EnglishToSpanish 下面輸入下面顯示的過程 VocabularDrill,代碼的介紹在其後面。
Sub VocabularyDrill()
Dim d As Dictionary
Dim totalRec As Long
Dim recNr As Long
Dim randomNr As Long
Dim question As String
Dim answer As String
' open a random access file 打開隨機訪問檔
Open "Translate.txt" For Random As #1 Len = Len(d)

' print the total number of bytes in this file 列印本檔的總位元組數


Debug.Print "There are " & LOF(1) & " bytes in this file."

' find out and print out the total number of records 找到並且列印總記錄數
recNr = LOF(1) / Len(d)
Debug.Print "Total number of records: " & recNr

Do
' get a random record number 獲取隨機記錄數
randomNr = Int(recNr * Rnd) + 1
Debug.Print randomNr
' find the random record 找到該隨機記錄
Seek #1, randomNr
' read the record 讀取記錄
Get #1, randomNr, d
Debug.Print Trim(d.en); " "; Trim(d.sp)

' assign answer to a variable 將答案賦予變數


answer = InputBox("What's the Spanish equivalent?", d.en)

' finish if cancelled 如取消則介紹


If answer = "" Then Close #1: Exit Sub

Debug.Print answer
' check if the answer is correct 檢查答案是否正確
If answer = Trim(d.sp) Then
MsgBox "Congratulations!"
Else
MsgBox "Invalid Answer!!!"
End If
' keep on asking questions, until Cancel is pressed 不斷提問,直到按下取消
Loop While answer <> ""
' close file 關閉文件
Close #1
150
End Sub
聲明變數之後,過程 VocabularyDrill 打開一個隨機訪問檔,並且告訴 VB 每個記錄的長度 Len =
Len(d),接下來,在立即視窗裡列印檔案的總位元組數和總記錄數。位元組數是由語句 LOF(1)返回的。
記錄數是總位元組數(LOF)除以一個記錄的長度——Len(d)。接下來,VB 執行迴圈裡的語句直到按
下 Esc 鍵或者取消按鈕。迴圈裡的第一條語句將函數 Rnd 的結果賦予變數 randomNr。接下來的語句將
這個數位寫入立即視窗,指令 Seek #1, randomNr 在開啟的檔中移動游標到變數 randomNr 明確的
記錄處,在下來的指令讀取找到的記錄內容。要在打開的隨機訪問檔中讀取資料,你必須使用 Get
語句。指令:
Get #1, randomNr, d
告訴 VB 要讀取的記錄號碼(randomNr)以及要讀取資料的變數(d)。隨機檔中的第一個記錄在位
置 1,第二個記錄在位置 2,依次類推。忽略記錄號碼會導致 VB 讀取下一個記錄。然後,用戶定義的
類型字典的兩個成員都被寫入了立即窗口。函數 Trim(d.en)和 Trim(d.sp)將讀取的記錄前後可能含
有的空格。接下來,VB 顯示資訊,提示使用者提供顯示單詞的外語等同語。該單詞賦予變數 answer,
如果你按下 Esc 而不是點擊確定的話,VB 就會關閉檔並且接受程式,否則,VB 將列印你的答案到
立即窗口,並且通知你,你的答案是否正確。當你要退出單詞訓練程式,隨時可以按下 Esc 或者點
擊對話方塊的取消按鈕。
如果你決定繼續並且點擊了確定按鈕,程式就會產生一個新隨機號碼,並且會找回一個英語單詞並
且問你相對應的西班牙譯語。
你可以修改該過程 VocabularyDrill,因此你可以將每個不正確翻譯的單詞寫到工作表。同樣,你
也許想要將文件 Translate.txt 裡的所有記錄到寫到工作表裡,這樣你就總可以知道你的字典內容。
你可以在本書帶的 CD 裡找到這兩個程式。
圖 8-3 在記事本裡打開隨機檔的內容
圖 8-4 試圖用 Microsoft Excel 打開隨機檔的內容。注意,Excel 正確的認識了原始的資料類型—
—隨機檔裡的資料是固定寬度的。
技巧 8-10 隨機文件的優勢與劣勢
不想順序檔,隨機檔裡的資料可以被很快地訪問,而且,這些檔在往裡面寫資訊和讀資訊期
間不需要關閉檔。隨機訪問檔不必按順序讀寫。因為它們的記錄和區域都有固定的長度,不管
儲存的字元有多少,使用的位元組數總是一樣的,因此,如果有些區域是空的或者比聲明的區域短時,
151
就會浪費許多空間。
22.操作二進位檔案
不象隨機檔那樣以固定長度儲存資料,二進位檔案是一些長度變化的記錄的集合。例如,檔包
含的第一個記錄可以使 10 個位元組,第二個記錄可以只有 5 個位元組,而第三個卻可以使 15 個位元組。這
種儲存資料的方法節省很多的硬碟空間,因為 VB 不需要在要儲存的字串後面加上多餘的空格來確
保它們有相同的長度(象往隨機檔裡寫資料那樣),在二進位檔案裡沒有空間浪費。這就不奇怪
二進位檔案比前面所講的兩種檔佔用的硬碟空間要少。正如隨機檔,二進位檔案也可以打開同
時進行讀和寫的操作。然而,因為二進位檔案裡的記錄是不同長度的,所以,這些檔的操作是更
苦難一些的。要找回正確的資料的話,你就必須將每個區域和記錄的大小資訊儲存起來。
你將使用下述四種語句來操作二進位檔案:
• 使用 Get 語句來讀取資料
• Put 語句允許你往二進位檔案輸入新資料
• Loc 語句返回所讀的最後位元組數(在隨機檔裡,Loc 語句返回最後所讀記錄的數位)
• Seek 語句將游標移動到檔中合時的位置。
為了快速掌握上面語句的使用,我們來打開立即視窗,並且將下面表格裡左邊的指令輸入到立即窗
口。本練習的目的是在一個叫做 MyData.txt 檔裡輸入你的姓和名,然後再找回你輸入的資料。
立即視窗輸入代碼
Open "MyData.txt" For Binary As #1
MsgBox "Total bytes: " & LOF(1)
fname = "Julitta"
ln = len(fname)
Put #1, , ln
MsgBox "The last byte: " & LOC(1)
Put #1, , fname
lname = "Korol"
ln = len(lname)
Put #1 , ,ln
Put #1,,lname
MsgBox "The last byte: " & LOC(1)
Get #1,1, entry1
MsgBox entry1
Get #1, , entry2
MsgBox entry2
Get #1, , entry3
MsgBox entry3
Get #1, , entry4
MsgBox entry4
Debug.Print
entry1;entry2;entry3;entry4
7 Julitta 5 Korol
Close #1
152
解釋
打開文件“MyData.txt”作為檔編號 1 來作二
進制訪問
顯示打開檔的位元組數(該檔現在為空)
給變數 fname 賦值
將儲存於變數 fname 的字串長度賦予變數 In
將變數 In 的內容放置在二進位檔案的下一個字

顯示最後一個位元組的位置
在下一個位置放置變數 fname 的內容
給變數 lname 賦值
將儲存於變數 lname 的字串長度賦予變數 In
將變數 In 的數值輸入到二進位檔案的下一個字

在下一個位置放置變數 lname 的內容
顯示最後一個位元組的位置
讀取第一個位元組位置的數值並將其賦予變數
entry1.
顯示變數 entry1 的內容
讀取下一個位置的數值並將其賦予變數
entry2.
顯示變數 entry2 的內容
讀取下一個位置的數值並將其賦予變數
entry3.
顯示變數 entry3 的內容
讀取下一個位置的數值並將其賦予變數
entry4.
顯示變數 entry4 的內容
將所有資料列印在立即視窗
立即視窗裡顯示的上條指令的結果
關閉文件
注意,上面的品質可以在 CD 裡的過程 EnterAndDisplay 裡找到。
當往二進位檔案輸入資料時,請遵循下述指導:
• 在往二進位檔案寫入字串之前,將該字串的長度賦予一個整型變數,通常可以使用下述代
碼塊:
字串長度= Len(變數名稱)
Put #1, , 字串長度
Put #1, , 變數名稱
• 當你從二進位檔案讀取資料時,先得讀取該字串長度,然後才是字串內容。可以使用 Get
語句和 String$函數來實現:
Get #1, , 字串長度
變數名稱=String(字串長度, " ")
Get #1, , 變數名稱
技巧 8-11 二進位檔案的優勢與劣勢
與順序檔和隨機檔相比,二進位檔案是最小的,因為它使用變化長度的記錄,可以保存硬碟空
間。和隨機訪問檔一樣,你可以同時讀寫二進位檔案。二進位檔案的一個最大的不好之處就是你
必須要準確地知道你要找回或者要操作的資料是如何儲存在檔裡的。
23.操作檔和資料夾的時髦方法
在你的電腦上有一個被隱藏的寶貝叫做 Windows Scripting Host(WSH 視窗腳本主機),它允許你創
建一些程式,可以控制視窗作業系統和它的應用程式,以及從作業系統找回資訊。WSH 是一種 ActiveX
控制項,可以在檔 Wshom.ocx 裡找到。如果你正在使用 Windows 95,98,NT5.0,2000,XP 或者 IE4,
5 或者 6 的話,該檔就會自動安裝在 Windows System32 資料夾裡面。
WSH 是一種指令碼語言。指令碼語言是指可以自動運行的一套命令。可以使用 Command Scripting Host
(Cscript.exe)從命令提示,或者使用 Windows Scripting Host (Wscript.exe)從視窗直接創建或
者運行腳本。在本章接下來的本分,你將學習 WSH 如何和 VBA 結合工作的。
WSH 有它自己的物件層次。使用 CreatObject 函數,你可以在 VBA 過程裡引用 WSH 物件。在開始編寫使
用 WSH 的 VBA 過程之前,我們來看看你將能控制的一些物件。
153
圖 8-5 WSH 是一個 ActiveX 控制項,用來創建一些進行簡單或複雜操作的腳本,而這種工作在早前的
MS-DOS 作業系統裡是有 Writing batch 檔(.bat)來完成的
1. 在 VB 編輯器視窗,選擇“工具”-“引用”
2. 在引用對話方塊,找到並選擇 Microsoft Scripting Runtime
圖 8-6 對 Microsoft Scripting Runtime 創建引用
3. 現在,按下 F2 打開物件流覽器
154
4. 在“所有庫”的下拉清單裡選擇“Scripting”。你將看到 WSH 庫裡面的部分物件清單。WSH 讓你
輕易的獲得一些問題的答案,例如“在哪個硬碟上我可以找到某個檔?”(GetDrive 方法),
“某檔的副檔名是什麼?”(GetExtensionName 方法),“該檔最後一次修改在什麼時候?”
(DateLastModified 屬性)以及“在給定的硬碟上存在某個資料夾或者檔嗎?”(FolderExists
和 FileExists 方法)。
圖 8-7 創建對 Microsoft Scripting Runtime 的引用之後(見圖 8-6),物件流覽器顯示了很多物件,
讓你使用硬碟,資料夾,檔和其它內容。
24.使用 WSH 獲取檔資訊
WSH 有個物件叫做 FileSystemObject,該物件有好幾種方法來操縱檔案系統。我們來看看你如何獲
取某個特定檔的資訊:
1. 在當前工程裡插入一新模組,並重命名為 WSH
2. 在模組 WSH 裡,輸入下述過程 FileInfo:
Sub FileInfo()
Dim fs As Object
Dim objFile As Object
Dim strMsg As String
Set fs = CreateObject("Scripting.FileSystemObject")

Set objFile = fs.GetFile("C:\WINNT\System.ini") ‘ 譯者: 有的可能是


C:\Windows\System.ini
strMsg = "File name: " & _
objFile.Name & vbCrLf
strMsg = strMsg & "Disk: " & _
objFile.Drive & vbCrLf
strMsg = strMsg & "Date Created:" & _

155
objFile.DateCreated & vbCrLf
strMsg = strMsg & "Date Modified:" & _

objFile.DateLastModified & vbCrLf


MsgBox strMsg, , "File Information"

End Sub
上面顯示的過程 FileInfo 使用 VBA 函數 CreateObject 來創建一個 ActiveX 物件(FileSystemObject),
該物件提供訪問電腦檔案系統的路徑
Dim fs As Object
Set fs = CreateObject("Scripting.FileSystemObject")

上面的代碼聲明了一個物件變數,名為 fs。然後使用函數 CreateObject 創建一個 ActiveX 物件並且


將該物件賦予物件變數。上面程式的第二行代碼
Set objFile = fs.GetFile("C:\WINNT\System.ini"),

創建和返回對 C:、WINNT 的 System.ini 檔物件的引用,並且將其賦值給物件變數 objFile。你可以


讀取 File 物件的很多屬性,例如,objFile.Name 語句返回該檔的完整檔案名,語句 objFile.Drive
返回該檔所處的硬碟名稱,語句 objFile.DateCreated 和 objFile.DateLastModified 分別返回該
檔的創建日期和最後一次修改日期。這個程式可以容易地修改成返回檔案類型,屬性和它的父文
件夾的名稱。請試圖使用下述指令你自己修改該過程: objFile.Type, objFile.Attributes,
objFile.ParentFolder 以及 objFile.Size。在物件流覽器裡麵點擊 File 物件,看看關於檔,你
還可以學習到別的什麼。
25.FileSystemObjec 的方法和屬性
FileSystemObjedt 是個 ActiveX 控制項,提供了到電腦檔案系統的訪問。該物件提供了很多種方法,
表 8-3 裡面列出了其中的一些。
方法
FileExists
描述
如果檔存在就返回 True
Sub FileExists()
Dim fs As Object
Dim strFile As String
Set fs =
GetFile
GetFileName
GetFileVersion
CopyFile
CreateObject("Scripting.FileSystemObject")

strFile = InputBox("Enter the full name of the

file:")
If fs.FileExists(strFile) Then
MsgBox strFile & " was found."
Else
MsgBox "File does not exist."
End If
End Sub
返回一對象 File
返回帶路徑檔案名
返回檔版本
複製檔
Sub CopyFile()
Dim fs As Object
Dim strFile As String
Dim strNewFile As String
strFile = "C:\Hello.doc"
strNewFile = "C:\Program Files\Hello.doc"

Set
156
fs =
MoveFile
DeleteFile
DriveExists
CreateObject("Scripting.FileSystemObject")

fs.CopyFile strFile, strNewFile


MsgBox "A copy of the specified file was
created."
Set fs = Nothing
End Sub
移動文件
刪除檔
Sub DeleteFile()
Dim fs As FileSystemObject
Set fs = New FileSystemObject
fs.DeleteFile "C:\Program Files\Hello.doc"

MsgBox "The requested file was deleted."

End Sub
如給定硬碟存在則返回 True
Function DriveExists(disk)
Dim fs As Object
Dim strMsg As String
Set fs =
GetDrive
CreateObject("Scripting.FileSystemObject")

If fs.DriveExists(disk) Then
strMsg = "Drive " & UCase(disk) & " exists."

Else
strMsg = UCase(disk) & " was not found."

End If
DriveExists = strMsg
' run this function from the worksheet

' by entering in any cell the following:

=DriveExists("E:\")
End Function
返回對象 Drive
Sub DriveInfo()
Dim fs, disk, infoStr, strDiskName
strDiskName = InputBox("Enter the drive
letter:", _
"Drive Name", "C:\")
Set fs =
CreateObject("Scripting.FileSystemObject")

Set disk =
fs.GetDrive(fs.GetDriveName(strDiskName))

infoStr = "Drive: " & UCase(strDiskName) &


vbCrLf
infoStr = infoStr & "Drive letter: " & _

UCase(disk.DriveLetter) & vbCrLf


infoStr = infoStr & "Drive Type: " &
disk.DriveType & vbCrLf
infoStr = infoStr & "Drive File System: " & _

disk.FileSystem & vbCrLf


infoStr = infoStr & "Drive SerialNumber: " & _

disk.SerialNumber & vbCrLf


157
GetDriveName
infoStr = infoStr & "Total Size in Bytes: " &

_
FormatNumber(disk.TotalSize / 1024, 0) & " Kb"

& vbCrLf
infoStr = infoStr & "Free Space on Drive: " &

_
FormatNumber(disk.FreeSpace / 1024, 0) & " Kb"

& vbCrLf
MsgBox infoStr, vbInformation, "Drive
Information"
End Sub
返回一個含有硬碟名稱或者網路共用名稱稱的字串
Function DriveName(disk)
Dim fs As Object
Dim strDiskName As String
Set fs =
FolderExists
CreateObject("Scripting.FileSystemObject")

strDiskName = fs.GetDriveName(disk)

DriveName = strDiskName
' run this function from the Immediate window

' by entering ?DriveName("D:\")


End Function
如資料夾存在則返回 True
Sub DoesFolderExist()
Dim fs As Object
Set fs =
GetFolder
CreateObject("Scripting.FileSystemObject")
MsgBox fs.FolderExists("C:\Program Files")

End Sub
返回對象 Folder
Sub FilesInFolder()
Dim fs As Object
Dim objFolder As Object
Dim objFile As Object
Set fs =
CreateObject("Scripting.FileSystemObject")

Set objFolder =
Workbooks.Add
fs.GetFolder("C:\")
For Each objFile In objFolder.Files

ActiveCell.Select
Selection.Formula = objFile.Name
ActiveCell.Offset(0, 1) _
.Range("A1").Select
Selection.Formula =
ActiveCell.Offset(1, -1) _
.Range("A1").Select
Next
Columns("A:B").Select
Selection.Columns.AutoFit
End Sub
158
objFile.Type
GetSpecialFolder 返回作業系統資料夾路徑:
0 — 視窗資料夾
1 — 系統資料夾
2 — 暫存檔案夾
Sub SpecialFolders()
Dim fs As Object
Dim strWindowsFolder As String
Dim strSystemFolder As String
Dim strTempFolder As String
Set fs =
CreateFolder
CreateObject("Scripting.FileSystemObject")
strWindowsFolder = fs.GetSpecialFolder(0)

strSystemFolder = fs.GetSpecialFolder(1)

strTempFolder = fs.GetSpecialFolder(2)

MsgBox strWindowsFolder & vbCrLf _


& strSystemFolder & vbCrLf _
& strTempFolder, vbInformation + vbOKOnly, _

"Special Folders"
End Sub
創建資料夾
Sub MakeNewFolder()
Dim fs, objFolder
Set fs =
CreateObject("Scripting.FileSystemObject")

Set objFolder =
CopyFolder
MoveFolder
DeleteFolder
CreateTextFile
fs.CreateFolder("C:\TestFolder")
MsgBox "A new folder named " & _
objFolder.Name & " was created."
End Sub
複製資料夾
Sub MakeFolderCopy()
Dim fs As FileSystemObject
Set fs = New FileSystemObject
If fs.FolderExists("C:\TestFolder") Then

fs.CopyFolder "C:\TestFolder",
"C:\FinalFolder"
MsgBox "The folder was copied."
End If
End Sub
移動資料夾
刪除資料夾
Sub RemoveFolder()
Dim fs As FileSystemObject
Set fs = New FileSystemObject
If fs.FolderExists("C:\TestFolder") Then

fs.DeleteFolder "C:\TestFolder"
MsgBox "The folder was deleted."
End If
End Sub
創建文字檔
159
OpenTextFile 打開文字檔
Sub ReadTextFile()
Dim fs As Object
Dim objFile As Object
Dim strContent As String
Dim strFileName As String
strFileName = "C:\WINNT\System.ini"

Set fs =
CreateObject("Scripting.FileSystemObject")
Set objFile = fs.OpenTextFile(strFileName)

Do While Not objFile.AtEndOfStream


strContent = strContent & objFile.ReadLine &

vbCrLf
Loop
objFile.Close
Set objFile = Nothing
ActiveWorkbook.Sheets(3).Select
Range("A1").Select
Selection.Formula = strContent
End Sub
物件 FileSystemObject 只有一個屬性,叫做 Drives,它返回對硬碟驅動集合的引用。使用該屬性,
你可以查看一個電腦硬碟的清單,如下所示:
Sub DrivesList()
Dim fs As Object
Dim colDrives As Object
Dim strDrive As String
Set fs = CreateObject("Scripting.FileSystemObject")

Set colDrives = fs.Drives


For Each Drive In colDrives
strDrive = "Drive " & Drive.DriveLetter & ": "

Debug.Print strDrive
Next
End Sub
26.物件 File 的屬性
物件 File 允許你訪問某特定檔的所有屬性,下面的代碼創建了對物件 File 的引用:
Set fs = CreateObject("Scripting.FileSystemObject")

Set objFile = fs.GetFile(“C:\My Documents\myFile.doc”)


在本章前面的過程 FileInfo 裡你可以發現使用物件 File 的例子。
屬性
Attributes
DateCreated
DateLastAccessed
DateLastModified
Drive
Name
ParentFolder
描述
返回檔案屬性(和本章前面介紹的 VBA 函數 GetAttr 比較一下)
檔創建日期
最後訪問日期
最後修改日期
驅動器名稱,後面帶冒號
檔案名
文件的父資料夾
160
Path
Size
Type
27.資料夾物件屬性
檔完整路徑
以位元組表示的檔大小(與本章前面介紹的 VBA 函數 FileLen 比較一
下)
檔案類型。這就是顯示在 Windows 檔流覽器類型一列中的文本(例
如,參數設置,應用程式,快捷方式)
物件 Folder 提供了對所有資料夾屬性的訪問。下面的代碼行創建了對物件 Folder 的引用:
Set fs = CreateObject("Scripting.FileSystemObject")

Set objFolder = fs.GetFolder(“C:\My Documents”)


屬性
Attributes
DateCreated
Drive
Files
IsRootFolder
Name
ParentFolder
Path
Size
SubFolders
Type
28.驅動器物件屬性
描述
資料夾屬性
資料夾創建日期
包含該資料夾的驅動器(譯者:原文為 Name of Folder)
資料夾中的檔集合
Sub CountFilesInFolder()
Dim fs, strFolder, objFolder, colFiles

strFolder = InputBox("Enter the folder name:")

If Not IsFolderEmpty(strFolder) Then

Set fs = CreateObject("Scripting.FileSystemObject")

Set objFolder = fs.GetFolder(strFolder)

Set colFiles = objFolder.Files


MsgBox "The number of files in the folder " & _

strFolder & "=" & colFiles.Count


End If
End Sub
上面的程序呼叫函數 IsFolderEmpty,該函數將在本表中的大小屬性
裡討論。
如果本資料夾為根資料夾則返回 True
資料夾名稱
該資料夾的父資料夾
資料夾的完整路徑
資料夾大小,以位元組表示
Function IsFolderEmpty(myFolder)
Dim fs, objFolder
Set fs = CreateObject("Scripting.FileSystemObject")

Set objFolder = fs.GetFolder(myFolder)

IsFolderEmpty = (objFolder.Size = 0)

End Function
該資料夾中的子資料夾集合
資料夾類型(例如資料夾或回收站)
物件 Drive 提供了訪問到電腦或伺服器上某驅動的屬性。下述代碼創建了對 Drive 物件的引用:
Set fs = CreateObject("Scripting.FileSystemObject")

Set objDrive = fs.GetDrive(“C:\”)


在下面的表格裡,你將看到好幾個使用 Drive 物件的程式示例。
161
屬性
AvailableSpace
FreeSpace
DriveLetter
DriveType
FileSystem
IsReady
Path
SerialNumber
TotalSize
29.使用 WSH 創建文字檔
描述
可用空間,位元組表示
同 AvailableSpace
驅動器字母(沒有冒號)
驅動器類型:
0 — 未知
1 — 可移動
2 — 固定
3 — 網路
4 — CD 驅動
5 — RAM
Sub CDROM_DriveLetter()
Const CDROM = 4
Dim fs, colDrives
Set fs = CreateObject("Scripting.FileSystemObject")

Set colDrives = fs.Drives


For Each Drive In colDrives
If Drive.DriveType = CDROM Then
MsgBox "The CD-ROM Drive: " & _
Drive.DriveLetter
End If
Next
End Sub
檔案系統,例如,FAT, NTFS 或 CDFS
如果合適的媒體(例如 CD)插入了並且可以訪問則返回 True
Function IsCDROMReady(strDriveLetter)

Dim fs, objDrive


Set fs = CreateObject("Scripting.FileSystemObject")

Set objDrive = fs.GetDrive(strDriveLetter)

IsCDROMReady = (objDrive.DriveType = 4) And _

objDrive.IsReady = True
' run this function from the Immediate window

' by entering: ?IsCDROMReady("D:")


End Function
根資料夾路徑
驅動器系列號
驅動器總容量,以位元組表示
WSH 提供了三種創建文字檔的方法:CreateTextFile,OpenTextFile 和 OpenAsTextStream。下麵
的表格裡列出了每種方法和示例。
方法/語法
CreateTextFile
示例
object.CreateTextFile(filename[, overwrite[, unicode]])

Object 是對象 FileSystemObject 或 Folder 的名稱。


filename 是明確要創建檔的字串運算式。
Overwrite (可選的) 是個布林值,表明你是否要覆蓋已經存在
的檔,如果可以覆蓋那麼該值為 True,如果不能覆蓋則為
162
OpenTextFile
OpenAsTextStream
False,如果忽略,那麼現存的檔將不會被覆蓋。
Unicode (可選的) 是個布林值,表明該檔創建為 Unicode 或者
ASCII 類型的文件。如果檔創建為 Unicode 類型那麼該值為真,
如果檔為 ASCII 的話那麼該值為假。如果忽略掉,那麼就會創
建 ASCII 類型的文件。
Sub CreateFile_Method1()
Dim fs, objFile
Set fs = CreateObject("Scripting.FileSystemObject")
Set objFile = fs.CreateTextFile("C:\Phones.txt", True)

objFile.WriteLine ("Margaret Kubiak: 212-338-8778")

objFile.WriteBlankLines (2)
objFile.WriteLine ("Robert Prochot: 202-988-2331")

objFile.Close
End Sub
上面的過程創建了一個文字檔來儲存兩個人的姓名和電話號
碼。因為在覆蓋參數處是一個 True 布林值, 所以
, 如果
C:\Phones.txt 已經存在的話就會被覆蓋。
object.OpenTextFile(filename[, iomode[, create[,
format]]])
Object 是 FileSystemObject 對象的名稱。
Filename 是明確要打開的檔案名稱的字串運算式。
Iomode (可選的) 是個布林值,表示如果該檔不存在時是否創
建一個新檔,如果可以創建新檔則為真,否則為假。如果忽
略該參數,那麼不會創建新檔。(譯者,紅色段落為原文翻譯,
應該是錯誤的)
參數 iomode 可以是下述常數之一:
ForReading (1)
ForWriting (2)
ForAppending (8)
Create (可選的) 是個布林值,表示如果該檔不存在時是否創
建一個新檔,如果可以創建新檔則為真,否則為假。如果忽
略該參數,那麼不會創建新檔。
Format (可選的) 是用來表明打開檔案類型的三種類型之一。如
果忽略,文件就會打開為 ASCII。
TristateTrue = 打開文件為 ASCII.
TristateFalse =打開文件為 Unicode.
TristateUseDefault = 使用系統預設方式打開檔
Sub CreateFile_Method2()
Dim fs, objFile
Set fs = CreateObject("Scripting.FileSystemObject")

Set objFile = fs.OpenTextFile("C:\Shopping.txt", _


ForWriting, True)
objFile.WriteLine ("Bread")
objFile.WriteLine ("Milk")
objFile.WriteLine ("Strawberries")
objFile.Close
End Sub
object.OpenAsTextStream([iomode, [format]])
Object 是 File 對象名稱。
Iomode (可選的) 表明讀寫模式,它可以是下述三個常數之一:
163
30.使用 WSH 進行其它操作
ForReading (1)
ForWriting (2)
ForAppending (8)
Format (可選的) 是用來表明打開檔案類型的三種類型之一。如
果忽略,文件就會打開為 ASCII。
TristateTrue = 打開文件為 ASCII.
TristateFalse =打開文件為 Unicode.
TristateUseDefault = 使用系統預設方式打開檔
Sub CreateFile_Method3()
Dim fs, objFile, objText
Set fs = CreateObject("Scripting.FileSystemObject")

fs.CreateTextFile "New.txt"
Set objFile = fs.GetFile("New.txt")

Set objText = objFile.OpenAsTextStream(ForWriting, _

TristateUseDefault)
objText.Write "Wedding Invitation"
objText.Close
Set objText = objFile.OpenAsTextStream(ForReading, _

TristateUseDefault)
MsgBox objText.ReadLine
objText.Close
End Sub
WSH 使任何安裝在你電腦上的自動化物件的操作成為可能。
除通過 FileSystemObject 訪問檔案系統之外,WSH 也允許你進行其它的一些操作,例如,處理 WSH
和 ActiveX 對象,設定和去除印表機和遠端驅動器,操縱註冊表,創建視窗和互聯網快捷方式以及
訪問 Windows NT 活動位址服務。WSH 物件模型由下述三種主要對象組成:WScript,WshShell 和
WshNetwork。本節示範如何利用 WshShell 物件來編寫程式啟動其它應用程式和創建快捷方式。
31.運行其它應用程式
在本書的下一章,你將學習多種從 Excel 裡啟動外部應用程式的方法。你可以加上即將在本節找到
的三種方法。
假設你想要從 VBA 過程裡啟動 Windows 記事本,接下來的過程,你將看到使用 WSH 的物件 WshShell 來
運行一個應用程式是多麼容易。如果你要運行內置的計算器的話,只要將記事本應用程式名稱改為
Calc 就可以了。
Sub RunNotepad()
Dim WshShell As Object
Set WshShell = CreateObject("WScript.Shell")

WshShell.Run "Notepad"
Set WshShell = Nothing
End Sub
上面的過程以聲明和創建一個 Wshshell 物件開始:
Dim WshShell As Object
Set WshShell = CreateObject("WScript.Shell")

下一語句則使用 Run 方法來運行要求的應用程式:


WshShell.Run "Notepad"
使用相同的概念,很容易運行視窗的公用應用程式,例如計算器或流覽器:
164
WshShell.Run “Calc”
WshShell.Run “Explorer”
過程的最後一行消滅了物件 WshShell,因為,不再需要它了。
Set WshShell = Nothing
你可以啟動你應用程式和某個特定的檔,而不是打開一個空的應用程式視窗,如下所示:
Sub OpenTxtFileInNotepad()
Dim WshShell As Object
Set WshShell = CreateObject("WScript.Shell")

WshShell.Run "Notepad C:\Phones.txt"

Set WshShell = Nothing


End Sub
試驗下面的過程來打開 MS-DOS 視窗,並且將目前的目錄下的檔清單列印出來:
Sub RunDOSCommand()
Dim WshShell As Object
Set WshShell = CreateObject("WScript.Shell")

WshShell.Run ("Command /c Dir >lpt1:")

End Sub
32.創建快捷方式
當你開始傳播你的 VBA 應用程式的時候,使用者可能會要求你自動在他們的桌面上放置一個你的軟體
的快捷方式。VBA 自己沒有提供創建快捷方式的方法。很幸運的是,你現在知道如何使用 WSH 了,你
可以使用它的物件 Shell 創建應用程式或者網頁的快捷方式,不必要使用者的干涉。對象 WshShell 使
用了方法 CreateShortcut,你可以按照下述方法:
Set myShortcut = WshShell.CreateShortcut(Pathname)

Pathname 是明確快捷檔完整路徑的字串。所有的快捷方式檔都有副檔名.lnk,並且該副檔名
必須包括在檔路徑名裡面。CreateShortcut 方法返回快捷方式物件,下面的表格裡列出了很多屬
性和一個方法。
方法/語法
TargetPath
WindowStyle
HotKey
IconLocation
Description
WorkingDirectory
Save
示例
TargetPath 屬性是可執行檔的路徑
WshShell.TargetPath = ActiveWorkbook.FullName

WindowStyle 屬性明確快捷方式使用的視窗類型
1 – 普通窗口
3 – 最大化視窗
7 – 最小化窗口
WshShell.WindowStyle = 1
HotKey 屬性是鍵盤快速鍵( 例如, Alt+f, Shift+g,
Ctrl+Shift+z, 等等)
WshShell.Hotkey = "Ctrl+Alt+w"
IconLocation 屬性是快捷方式圖示的位置。因為圖示檔裡通常
不止一個圖示,所以你應該提供圖示檔的路徑,並且後面標明
圖示在檔裡的索引號。如果不明確的話,Windows 會使用缺省
的圖示。
WshShell.IconLocation = "notepad.exe, 0"

Description 屬性包含一個描述快捷方式的字串
WshShell.Description = "Wordware Web Site"

WorkingDirectory 屬性明確快捷方式的工作目錄
strWorkDir = WshShell.SpecialFolders("Desktop")

WshShell.WorkingDirectory = strWorkDir

這是物件 Shortcut 的唯一方法。在使用方法 CreateShortcut 創建


165
創建快捷方式是個三步的過程:
1. 創建一個 WshShortcut 物件
2. 初始化它的屬性
3. 用方法 Save 將它保存到硬碟
一個快捷方式物件並且設置該快捷方式的屬性後,必須使用 Save
方法將快捷方式物件保存到硬碟上。
下面的例子創建一個 WshShell 物件和使用 CreateShortcut 方法創建兩個快捷方式:一個到當前
Excel 工作簿的 Windows 快捷方式,和一個到 Wordware Publishing 網頁的互聯網快捷方式。兩個快
捷方式都放在使用者的桌面上。該過程使用物件 WshShell 的 SpecialFolders 屬性來返回到視窗桌面的
路徑。
Sub CreateShortcut()
' this script creates two desktop shortcuts

Dim WshShell As Object


Dim objShortcut As Object
Set WshShell = CreateObject("WScript.Shell")

' create an internet shortcut


Set objShortcut = WshShell.CreateShortcut(WshShell. _

SpecialFolders("Desktop") & "\Wordware.url")

objShortcut.TargetPath = "http://www.wordware.com"

objShortcut.Save
' create a file shortcut
Set objShortcut = WshShell.CreateShortcut(WshShell. _
SpecialFolders("Desktop") & "\" & ActiveWorkbook.Name & ".lnk")

With objShortcut
.TargetPath = ActiveWorkbook.FullName

.WindowStyle = 7
.Save
End With
Set objShortcut = Nothing
Set WshShell = Nothing
End Sub
技巧 8-12 使用 SpecialFolders 屬性
你可以使用 SpecialFolders 屬性在你的機器上找到特殊資料夾的位置。下述特殊資料夾是可用的:
AllUsersDesktop(所有用戶桌面), AllUsersStartMenu(所有使用者開始功能表),AllUsersPrograms
(所有使用者程式),AllUsersStartup(所有使用者啟動),Desktop(桌面),Favorites(收藏),Fonts
(字體),MyDocuments(我的文件),NetHood(網路連接),PrintHood(印表機),Programs(程
序),Recent(最近),SendTo(發送到),StartMenu(開始功能表),Startup(啟動)和 Templates
(模版)。如果請求的特殊資料夾不可用,那麼 SpecialFolders 屬性就會返回一個空字串。
33.接下來……
在本章的課程裡,你學習並且測試了讓你操作檔案系統的 VBA 函數和語句。你知道了如何讀取和修
改與檔和資料夾有關的資訊,而且,知道了如何執行對順序,隨機和二進位檔案的讀和寫的操作。
你也學習了如何使用 WSH 來訪問 FileSystemObject 和進行其它操作,例如啟動應用程式和使用物件
WshShell 創建 Windows 快捷方式。如果你對討論的函數和語句更詳細的東西感興趣的話,那麼就花
些時間來流覽一下 VB 線上說明吧。
接下來的一章將給你介紹更多的自動化任務。例如,你將學習如何使用 VBA 來控制其它應用程式。
你將學習啟動應用程式的多種方法,並且研究如何直接從 Microsoft Excel 裡操縱其它應用程式。
166
第九章利用 VBA 控制其它應用程式
你每天在辦公室裡或者家裡在你的電腦上工作時,都要用到很多種應用程式。要從你的硬碟或者軟
盤上查找某個檔的話,你就要打開視窗流覽器。當你要設置系統時間或者更改螢幕外觀的話,可
以點擊控制台上的相應的圖示。如果你的電腦上安裝了微軟辦公軟體套餐的話,就可以使用 Word
創建各種各樣的檔,並且依靠 Excel 進行所有的計算。微軟 Access 對於保存重要的資料表非常有
用,而 PowerPoint 則有助於你使用聲音和圖片。最後,微軟 Outlook 使你易於保存你的聯繫、時間
和約會並且分享給他人。使用這些應用軟體的時候,你經常要在他們之間切換,你可以使用鍵盤直
接輸入資料或者複製或移動資料。這些操作——打開應用程式以及在它們之間傳輸資料時不需要手
動操作的。它們可以通過一些很有趣的 VBA 函數和指令來自動完成。在本章,你將學習多種從 VBA
過程裡打開應用程式的方法,並且找到如何使用稱為自動化的技術直接從微軟 Excel 直接控制其它
應用程式。
1.啟動應用程式
啟動一個應用程式的方法不止一個,實際上,你至少可以使用五種方法手動打開某個程式:通過“開
始”|“程式”功能表,快速鍵,“運行”命令,MS-DOS 視窗,或者在視窗流覽器裡按兩下可執行檔。
本節假設你對手動啟動應用程式很熟悉,並且很想從 Excel 內部的 VB 編輯視窗試驗其它啟動應用程
序的方法。
我們從最簡單的開始吧——Shell 函數。該函數使你可以從 VBA 過程裡直接打開任意程式。假設你的
過程必須打開視窗記事本,要打開記事本,你所有要做的就是在關鍵字 Sub 和 End Sub 之間加上一條
語句,或者更好的方法是在立即視窗裡輸入下述語句,並且按下回車鍵:
Shell "notepad.exe", vbMaximizedFocus

你將立即看到結果。
在上面的語句裡,“notepad.exe”是你要打開的程式的名稱。如果你擔心程式找不到的話,那麼該
名稱就應該包含完整的路徑(啟動器名稱和資料夾名稱)。注意,程式名稱用雙引號括起來了。Shell
函數的第二個參數可以忽略。該參數明確視窗形式(也就是當程式啟動的時候,它如何顯示在螢幕
上的)。在上面的例子裡,記事本將顯示為最大化的視窗。如果沒有明確視窗形式,那麼程式就會
被最小化(參見表 9-1)。
窗口形式常數
vbHide
vbNormalFocus
vbMinimizedFocus ( 默認

置)
vbMaximizedFocus
vbNormalNoFocus
vbMinimizedNoFocus

0
1
2
3
4
6
視窗顯示情況
視窗被隱藏
普通大小,並帶焦點
最小化,並帶焦點(這是缺省設置)
最大化,並帶焦點
普通大小,並失去焦點
最小化,並失去焦點
如果 Shell 函數能夠啟動某個可執行檔,那麼它就會返回一個叫做任務 ID 的號碼。該號碼是指示
應用程式啟動的唯一號碼。如果 Shell 函數不成功的話(也就是說某應用程式不能打開),VB 就會產
生一錯誤。如果你要使用 Shell 函數啟動的應用程式的話,就不要在 Shell 函數後面輸入任何語句。
Shell 函數啟動程式是不同時的,意思是說 VB 啟動 Shell 函數指定的應用程式,並且,VB 在啟動程式
後,立即就回到過程裡面去繼續剩餘的指令(因此,你沒有機會立即使用該應用程式)。你如果使
用 Shell 函數來啟動控制台呢?
1. 打開一新工作簿,保存為 Chap09.xls
2. 在 VB 編輯器視窗,插入新模組
3. 重新命名工程為 WorkWApplets,模組名為 ShellFunction
4. 輸入下面顯示的過程 StartPanel:
Sub StartPanel()
Shell "Control.exe", vbNormalFocus
End Sub
控制台裡面有很多圖示,每個圖示執行一個或者多個任務。眾所周知,在每個圖示後面都有一個
167
程式的,當使用者按兩下圖示或者用箭頭選擇該圖示然後按下 Enter 鍵,該程式都會被啟動。作為一個
規律,你總是可以通過查看某個圖示的屬相來檢查什麼檔案名驅動某個圖示。不幸的是,控制台
裡面的圖示的屬性選擇都被禁止了。然後,你可以通過創建一個到該圖示的快速鍵來查找控制台
裡圖示檔。例如,在你創建一個更改電腦原始設置的過程之前,我們來找出啟動該圖示的檔案名
稱。
1. 從“開始”功能表裡選擇“設置”,然後選擇“控制台”(在 Windows XP 開始功能表裡可以直接看
到“控制台”)
2. 在控制台視窗裡,按右鍵“初始選項”圖示,並且從快顯功能表中選擇創建快速鍵
3. 點擊確定,將快速鍵放在桌面上
4. 關閉控制台窗口
5. 返回桌面,在初始選項的快速鍵上按一下右鍵,然後選擇屬性
6. 在屬性視窗,點擊快速鍵頁,然後點擊更改圖示按鈕
圖 9-1 每個控制台裡的圖示都有一個尾碼名為.cpl 的文件
7. 寫下.cpl 檔案名稱(Control Panel Library)或者動態連結程式庫檔(.dll)並關閉該練習中開
啟的所有視窗
表 9-2 啟動控制台圖示的一些檔示例
控制台圖示
電話和數據機選項
添加/刪除程式
網路和撥號連線
32-Bit ODBC
系統
郵件
使用者和密碼
日期/時間
區域選項
Internet 選項
聲音和多媒體屬性
顯示
滑鼠
.cpl 或者.dll 文件
TELEPHON.CPL 或 MODEM.CPL
APPWIZ.CPL
NETCPL.CPL 或 NETSHELL.DLL
ODBCCP32.CPL
SYSDM.CPL
MLCFG32.CPL
PASSWORD.CPL 或 NETPLWIZ.DLL
TIMEDATE.CPL
INTL.CPL
INETCPL.CPL
MMSYS.CPL
DESK.CPL
MAIN.CPL
下面的國產 ChangeSettings 示範如何使用 Shell 函數來啟動控制台的初始設置圖示。注意 Shell
函數的參數必須寫在括弧裡,如果你後面需要在你的程式裡使用它返回值的話。
1. 在當前模組裡輸入過程 ChangeSettings,如下所示:
Sub ChangeSettings()
Dim nrTask
nrTask = Shell("Control.exe intl.cpl", vbMinimizedFocus)

Debug.Print nrTask
End Sub
2. 運行幾次過程 ChangeSettings,每次從表 9-2 裡列出的清單裡提供一個不同的.cpl 文件。你可能
168
需要將程式改為:
Sub ChangeSettings2()
Dim nrTask
Dim iconFile As String
iconFile = InputBox("Enter the name of the CPL or DLL file:")

nrTask = Shell("Control.exe " & iconFile, vbMinimizedFocus)

Debug.Print nrTask
End Sub
如果你要啟動的程式是微軟應用程式,那麼除了使用 Shell 函數外,你還可以很方便地使用 VB 的方
法 ActivateMicrosoftApp 來實現。該方法在微軟 Excel 應用程式的物件裡是可用的,例如,要從立
即視窗啟動 PowerPoint 的話,你所有要做的事情就是輸入下面的指令並且按下 Enter:
Application.ActivateMicrosoftApp xlMicrosoftPowerPoint

注意 ActivateMicrosoftApp 方法要求一個常量來指定要啟動的程式。如果 PowerPoint 沒有打開的


話,上面的過程就會打開 PowerPoint,但是如果該程式已經打開的話,該指令不會再打開一個新的
PowerPoint 介面,只是簡單的啟動已經在運行的應用程式。你可以結合 ActivateMicrosoftApp 方法
使用下列常量,常量的名稱指名應用程式名稱。
應用程式名稱
Access
FoxPro
Mail
PowerPoint
Project
Schedule
Word
2.在應用程式之間切換
常量
xlMicrosoftAccess
xlMicrosoftFoxPro
xlMicrosoftMail
xlMicrosoftPowerPoint
xlMicrosoftProject
xlMicrosoftSchedulePlus
xlMicrosoftWord
因為用戶可以同時在 Windows 環境下使用多個應用程式,所以你的 VBA 過程必須要知道如何在打開的
程式之間切換。假設除了 Excel 之外,你還打開了另外兩種應用程式:Word 和 Explorer。你可以按
照下面的語法使用 AppActivate 語句來啟動已經打開的程式:
AppActivate title [, wait]
只有標題參數是必須的,這是應用程式的名稱,正如它顯示在應用程式視窗的標題列那樣,或者它
也可以是 Shell 函數返回的任務 ID 號碼。注意,參數 title 要跟每個正運行的應用程式的標題字串
進行對比,如果沒有精確的匹配,那麼任何標題字串裡前面的字元和參數 title 一致的應用程式
就會被啟動。(譯者:例如,你要啟動 Excel,那麼 title 參數應該是“Microsoft Excel”,如果你
寫的是“Microsoft”,那麼啟動的就也可能是 Word,PowerPoint……)。第二個參數 wait 是可選的,
它是個布林值(True 或 False),明確 VB 什麼時候啟動應用程式。如果在這裡是 False 的話,該應用
程式就立即會被啟動,甚至被調應用程式並沒有焦點。如果在 wait 參數處放置 True 的話,那麼被調
的應用程式就會等到它有了焦點,然後才會啟動該應用程式。例如,要啟動 Word,你就得輸入下列
語句:
AppActivate “Microsoft Word”
注意,應用程式名稱用雙引號引用起來。你也可以使用 Shell 函數返回的數值作為語句 AppActivate
的參數:
‘ run Microsoft Word 運行 Word 應用程式
ReturnValue = Shell("C:\Microsoft Office\Office\Word.exe",1)

‘ activate Microsoft Word 啟動 Word


AppActivate ReturnValue
語句 AppActivate 用來在應用程式之間切換,所以要求這些程式已經在運行。該語句僅僅改變焦點,
指定的應用程式變為當前活動的視窗。AppActivate 語句不會啟動任何應用程式,參見下一章節的
過程 FindCPLFiles,這也是使用該語句的一個例子。我們來練習一下最近介紹的幾個 VBA 語句:
169
1. 通過在立即視窗裡輸入下列 VBA 語句來打開資源管理器:
Shell "Explorer"
按下回車鍵後,被請求的應用程式就被打開了,帶有“我的文件”資料夾的圖示就會出現在工作列
上。
2. 在立即視窗裡輸入下列代碼:
AppActivate "My Documents"
按下回車鍵後,焦點就會移至我的文件窗口。
3.控制其它應用程式
既然你已經知道了如何使用 VBA 語句來啟動一個程式,以及在應用程式之間切換,那麼我們來看看
一個應用程式是如何與另外一個應用程式交流的。對於一個應用程式來說,要控制另一個應用程式
的最簡單的方式就是使用 SendKeys 語句。該語句允許你將許多的按鍵發送到活動應用程式視窗,你
可以發送一個或複合鍵並且得到直接使用鍵盤的同樣效果。SendKeys 語句如下所示:
SendKeys string [, wait]
這個必須的參數 string 是你要發送到活動應用程式視窗的鍵或複合鍵,例如,使用下列指令來發送
字母“f”鍵:
SendKeys "f"
要發送複合鍵 Alt+f,使用:
SendKeys "%f"
百分符號(%)是表示 Alt 鍵的字串。要發送例如 Shift+Tab 的複合鍵的話,那麼就要使用下面的
語句:
SendKeys "+{TAB}"

加號(+)表示 Shift 鍵。要發送其它鍵或者其它複合鍵的話,請參見表 9-3 列出的相應字串。


技巧 9-1 SendKeys 和其它應用程式
你只能發送按鍵到那些為微軟視窗作業系統設置的應用程式。
技巧 9-2 SendKeys 和被保護的字元
有些字元在和 SendKeys 語句一起使用時具有特殊的意義,它們是:加號(+),脫字元號(^),符合
(~)和括弧()。要發送這些字元到另一個應用程式的話,就必須將它們用打括弧{}括起來。要發
送打括弧時,則需要輸入{{}和{}}
SendKeys 語句的第二個參數是可選的,wait 是個邏輯值 True 或者 False。如果是 False(缺省),那
麼 VB 在發送按鍵後立即返回過程,如果為 True,那麼 VB 只有在發送的按鍵執行後才能返回到過程。
如果要發送一個表格 9-3 裡面沒有列出的字元的話,那麼記住這些代碼必須用引號括起來,例如:
SendKeys “{BACKSPACE}”
表 9-3 SendKeys 語句裡使用的按鍵代碼

空白鍵
Break 鍵
大寫鎖定鍵
刪除鍵
向下箭頭
End 鍵
回車鍵
Esc 鍵
幫助鍵
Home 鍵
插入鍵
代碼
{BACKSPACE}
{BS}

{BKSP}

{BREAK}

{CAPSLOCK}

{DELETE}

{DEL}

{DOWN}

{END}

{ENTER}

~
{ESC}
{HELP}

{HOME}

{INSERT}


滾動鎖定
Tab
向上箭頭
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
170
代碼
{SCROLLLOCK}
{TAB}

{UP}

{F2}

{F3}

{F4}

{F5}

{F6}

{F7}

{F8}

{F9}

{F10}

{F11}

{F12}

向左箭頭
數字鎖定鍵
向下翻頁鍵
向上翻頁鍵
螢幕列印鍵
向右箭頭
{INS}

{LEFT}
{NUMLOCK}

{PGDN}

{PGUP}

{PRTSC}

{RIGHT}

F13
F14
F15
F16
Shift
Ctrl
Alt
{F13}

{F14}

{F15}

{F16}

+
^
%
技巧 9-3 SendKeys 語句對格敏感
當你使用 SendKeys 語句發送按鍵時,你一定要牢記區分字元的大小格。因此,要發送複合鍵 Ctrl+d
的話,你必須使用^d,而發送 Ctrl+Shift+D 的話,則必須使用字串:^+d
在本章前期,你學習了.cpl 檔啟動多種控制台的圖示。你現在要創建的 VBA 過程目的是要定位
你硬碟上所有副檔名為.cpl 的文件。
1. 使用立即視窗來啟動資源管理器:
Shell “Explorer.”
“我的文件”圖示將出現在螢幕下方的工作列上。
2. 在當前工程裡插入新模組並且重命名為 SendKeysStatement
3. 輸入過程 FindCPLFiles,如下所示:
Sub FindCPLFiles()
' The keystrokes are for Windows 2000

AppActivate "My Documents"


' activate the Search window 啟動搜索窗口
SendKeys "{F3}", True

' move the pointer to the Search for files 將游標移到搜索文件


' and folders named text box 和資料夾(名稱在文字方塊裡)
SendKeys "%m", True
' type in the search string 輸入要搜索的字串
SendKeys "*.cpl", True
' move to the Look in drop down box 焦點移到下拉清單
SendKeys "{Tab}{Tab}", True

' change to the root directory 更改根目錄


SendKeys "C:\", True
' execute the Search 執行搜索
SendKeys "%s", True
End Sub
4. 切換到 Excel 應用程式視窗並且運行過程 FindCPLFiles(使用 Alt+F8 打開宏對話方塊,選擇過程名
稱,再點擊運行)。
上面過程的第一條語句使用 AppActivate 語句(參見前面章節)來啟動已經打開的應用程式,還記
得你在立即視窗裡使用 Shell 語句啟動了資源管理器嗎?剩餘的語句發送一些必要的按鍵到活動應
用程式。本過程的結果是副檔名為.cpl 的控制台檔的搜索結果列表。你也可以使用一個
SendKeys 語句來發送所有必須的按鍵(參見下面的例子),然而,一步一步發送按鍵更容易理解程
序。
Sub FindCPLFiles2()
AppActivate "My Documents"
SendKeys "{F3}% m*.cpl{Tab}{Tab}C:\%s", True

End Sub
4.控制應用程式的其它方法
儘管你可以使用 SendKeys 語句來傳遞命令給其它應用程式,但是你還是必須要求助於其它方法來獲
得對該應用程式的充分控制。有兩種標準方法可以供應用程式和另外一種應用程式交流。最新的方
171
法,被稱為自動控制,它允許你訪問和操縱另一種應用程式的物件。你可以通過自動控制編寫 VBA
過程,通過引用其它應用程式的物件、屬性和方法來控制其它應用程式。在本章接下來的章節裡,
你將學習如何通過自動控制來控制其它應用程式。稱為 DDE(動態資料交換)的老資料交換技術是
允許你在兩個應用程式之間動態發送資料的協定,它通過創建一個特殊的通道來發送和結束資訊。
DDE 非常慢,使用困難,只有當你需要與一個不支援自動控制的老應用程式交流時,才需要使用 DDE。
5.瞭解自動控制
當和另外一個應用程式交流時,你可能需要更多的功能,而不只是啟動它來發送按鍵。例如,你可
能需要在該應用程式裡創建和操縱物件,你可以在 Excel 試算表力插入整個 Word 文檔。因為 Excel
和 Word 都支持自動控制,所以,你可以在 Excel 裡編寫一個 VBA 過程在操作 Word 物件,比如文檔或者
段落。支援自動控制的應用程式稱為自動控制伺服器(Automation servers)或者自動控制物件
(Automation objects)。
能夠操作伺服器物件的應用程式稱為自動控制控制項。有些應用程式只能是伺服器或者控制項,而其它
的則既可以是伺服器也可以是控制項。Microsoft Office 2000 和 2002 都可以作為自動控制伺服器和
控制項。自動控制控制項可以是安裝在你電腦上的各種 ActiveX 控制項,你將在下一章裡學習這些物件。
6.瞭解連結和嵌入
在你學習如何使用自動控制從 VBA 程序控制其它應用程式之前,我們來看一看如何手動連結和插入
對象。人們熟知的 OLE,物件連結和嵌入,允許你創建組合文檔。組合文檔包含其它應用程式創建
的對象。例如,如果你要在 Excel 裡嵌入一個 Word 文檔的話,Excel 只要知道創建該物件需要用到的
應用程式名稱,以及該物件在螢幕上顯示的方法。組合文檔有連結或者物件嵌入產生。當你使用手
動方法來內嵌物件時,你首先要在一個應用程式裡複製它,再粘貼到另一個應用程式裡。連結化物件
和內嵌物件的主要區別是物件儲存和更新的方式。我們來試驗一下:
1. 啟動 Word 並打開任意一個文檔
2. 選擇和複製任意一段文本
3. 在 Excel 工作表裡,使用下述四種方法之一將複製的文本進行粘貼:



粘貼為文本(選擇編輯|粘貼)。複製的文本就會出現在作用儲存格(見圖 9-2,儲存格
A2)
粘貼為內嵌物件(選擇編輯|選擇性粘貼,點擊“粘貼選項”按鈕,並且在清單裡選擇
“Microsoft Word Document 對象”。)粘貼的文本將作為一個嵌入的物件(見圖 9-2,單
元格 A5)。該嵌入的物件成為了目的檔的一部分。因為該嵌入的物件沒有和原始資料鏈
接,所以該資訊是靜態的。當檔源中的資料改變時,該嵌入的物件不會被更新。如果要
更改嵌入的資料,你就必須按兩下它,這樣就會打開該物件在來源程式裡編輯它。當然,該源
程式必須已經安裝在你的電腦上了。當你內嵌物件時,所有的資料都會存儲在目的檔裡,
這會導致文件大小顯著增大。注意,當你嵌入一個物件後,Excel 的編輯欄裡將顯示:
=EMBED("Word.Document.8","")
粘貼為連結化物件(選擇編輯|選擇性粘貼,點擊“粘貼連結”選項,然後在清單裡選擇
“Microsoft Word Document 對象”)。雖然目的檔顯示了所有的資料,但是它僅僅儲存
了該資料的位址。當你按兩下該連結的物件時(見圖 9-2,儲存格 A9),原應用程式就會被啟
動。連結化物件是一種動態的操作,這意味著當原始檔案裡的資料改變時,連結的資料就會自
動更新。因為目的檔只包含物件如何與原始檔案連結的資訊,所以,物件連結並不會增加
目的檔的大小。下面的公式是 Excel 用來連結化物件的:
=Word.Document.8|'C:\Documents and Settings\tj8147\My

Documents\Tiger\VB\Excel2002_Programming\Chinese\Excel2002VBA_Ch9.doc'!'!OLE_

LINK2'(譯者:由於檔存儲位置不同,本節的翻譯可能和你的情況不一樣,請注意分辨)
粘貼為超連結(選擇粘貼|超連結譯者:應該為“編輯”|“粘貼為超連結”)粘貼的數
據在工作表裡顯示為帶底線、有顏色的文本(見圖 9-2,儲存格 A11)。點擊該超連結,
你可以快速地啟動該原始檔案。
172
圖 9-2 示範連結和嵌入
7.使用 VBA 進行連結和嵌入
過程 InsertLetter 示範了如何使用程式在 Excel 嵌入一個 Word 文檔。用你自己的檔案名稱代替引用
“C:\Hello.doc”。過程 InsertLetter 使用 AddOLEObject 方法,該方法創建一個 OLE 物件,並且返回
一個對表該新 OLE 物件的 Shape 物件。在 VB 線上說明裡面,你可以找到 AddOLEObject 方法可用的其它
參數。
1. 在當前工程裡面插入一新模組,並重命名為 OLE
2. 輸入過程 InsertLetter,如下所示:
Sub InsertLetter()
Workbooks.Add
ActiveSheet.Shapes.AddOLEObject FileName:="C:\Hello.doc"

End Sub
上面的過程打開一個新工作簿,然後嵌入該指定的 Word 文檔。要連結一個文檔的話,你就必須明確
另外一個參數 Link,如下所示:
ActiveSheet.Shapes.AddOLEObject _
FileName:="C:\Hello.doc", Link:=True

技巧 9-4 物件連結和嵌入
當你不得不做出決定是否使用嵌入還是連結化物件時,只要有下列之一的條件,那麼就使用嵌入:



據。
你不在乎文檔大小,或者你有足夠的硬碟空間和記憶體來處理大檔
你再也不會在其它複合文檔裡使用原始檔案或者源文本
你想要將該文檔通過電子郵件或者磁片發送給別人,並且確保他們能夠順利地讀取數
(譯者:本人也傾向於使用嵌入,因為連結經常會問你是否要更新連結,而且,很多人經常會忘記
發送原始檔案給別人。)
173
8.COM 和自動控制
在自動控制後面的驅動力量是元件物件模型(COM),它決定了伺服器應用程式創建物件的規則,也
明確伺服器和控制應用程式在使用這些物件時必須遵循的方法。COM 標準包含作為自動控制介面
(Automation interfaces)可用的函數集合。
當伺服器應用程式創建一個物件是,它會自動地製作一個和它一起可用的介面。該介面包括該物件
可識別的屬性、方法和事件。控制應用程式不需要為了控制該物件去瞭解它的內部結構,只需要知
道如何操作伺服器應用程式製作的物件介面。
9.瞭解綁定
對於控制應用程式與自動控制物件(伺服器)來說,你必須將你的 VBA 過程中可用的物件和伺服器
實際的自動控制物件聯繫起來,這個過程就叫做綁定。這裡有兩種類型的綁定:後期綁定和早期綁
定。你對綁定的選擇對你的應用程式表現影響很大。
10.後期綁定
當你聲明一個變數 As Object 或者 As Vaiant 時,VB 使用的是後期綁定。後期綁定也叫運行綁定。
簡單地說,後期綁定意味著 VB 在設計時不會將你的物件變數和自動控制物件聯繫起來,而是要等到
你實際運行該過程時才聯繫起來。因為 As Object 或者 As Variant 的聲明在本質上是非常普通的,
所以,VB 在彙編時不能決定你變數指向的物件真正具有你的 VBA 過程使用的屬性和方法。
下麵的聲明導致對指定對象的後期綁定:
Dim mydoc As Object
後期綁定的優勢是所有的自動控制物件都知道如何使用。
後期綁定的劣勢是對內置常量不支持。因為在設計時,VB 並不知道你的物件指向的型別程式庫,所以,
你必須通過在應用程式文檔裡查詢數值在你的代碼裡定義常量。同樣,在運行時詢問應用程式將放
慢你程式的執行。
注意:後期綁定使得在另外一個應用程式的型別程式庫裡訪問物件稱為可能,而不需要首先建立對該對
象庫的引用。如果你不肯定你的用戶是否在他們的機子上安裝了要指向的型別程式庫,那麼就使用後期
綁定。
下面的過程示範如何使用後期綁定來列印 Word 文檔。
Sub PrintWordDoc()
Dim objWord As Object
Set objWord = CreateObject("Word.Application")

With objWord
.Visible = True
.Documents.Open "C:\Hello.doc"
.Options.PrintBackground = False
.ActiveDocument.PrintOut
End With
objWord.Documents.Close
objWord.Quit
Set objWord = Nothing
End Sub
技巧 9-5 這是什麼類型的綁定?
無論何時你使用常用的 Object 或 Variant 資料類型聲明物件變數,請考慮後期綁定。後期綁定和早
期綁定的主要區別是你如何聲明你物件變數。
11.早期繫結
當你聲明物件變數為明確的物件類型時,VB 使用的是早期繫結。早期繫結也熟知為彙編綁定。這意
味著 VB 在原始程式碼轉變為可執行代碼時期,就將你的物件變數和自動控制物件聯繫起來了。常見的語
法如下所示:
Dim objectVariable As Application.ObjectType

174
在上面的語法中,Application 是應用程式的名稱,正如它出現在物件流覽器裡的工程庫下拉清單
裡的樣子(例如 Word 和 Excel)。ObjectType 是物件類型(例如應用程式,文檔,工作簿,工作表)。
下麵的聲明導致早期繫結:
Dim mydoc As Word.Document
Dim mydoc As Excel.Worksheet
早期繫結讓你能夠充分利用 VB 編輯器上可用的許多調試工具。例如你可以使用物件流覽器查找外部
物件,屬性和方法。VB 的自動語法檢測,自動列出成員以及自動顯示快速資訊(這些都在第二章裡
有討論)可以讓你在編寫代碼時更快,更少出錯。另外,早期繫結允許你使用內置常量作為方法和
屬性設定的參數。因為這些常量在設計的時候在型別程式庫裡面就是可用的,所以你不需要定義它們。
這些非常方便的內置語法檢測,智慧特點和對內置常量的支援,在後期綁定裡是不可用的。雖然使
用早期繫結的 VBA 過程執行得更快一些,但是一些非常老的視窗應用程式只能使用後期綁定。
注意:為了使用早期繫結,你必須首先建立對物件程式庫的引用(參見接下來的章節)。當你確定你的
用戶安裝了引用的型別程式庫時,就使用早期繫結。
12.建立到物件程式庫的引用
如果你決定通過自動控制使用早期繫結連接到另外的應用程式的話,你首先就應該建立對包括你要
操作物件的物件程式庫的引用。依照下面的步驟創建對 Microsoft Word 物件程式庫的引用:
1. 啟動 VB 編輯器視窗
2. 在工程流覽器裡選擇當前工程,並且選擇“工具”|“引用”
3. 在引用對話方塊裡,選擇“可使用的引用”清單方塊裡面的應用程式名稱。例如,點擊 Microsoft Word
9.0 Object Library 或者 Microsoft Word 10.0 Object Library 旁邊的核取方塊(見圖 9-3)(譯
者:這裡引用的是 Microsoft Word 11.0 Object Library)。拉下可用引用清單方塊的捲軸定位
該物件程式庫,如果你已經安裝了某個應用程式但是它的型別程式庫在可用引用清單方塊裡面沒有出現的
話,那麼你可以點擊“流覽”按鈕。
4. 點擊確定按鈕關閉引用對話方塊。
引用對話方塊列出了 VBA 工程可用的引用名稱。沒有使用的引用按字母順序列出,勾選上了的引用按
優先順序列出。例如,在 Excel 裡,Microsoft Excel 10.0 Object Library 比 Microsoft Word 10.0
或者 9.0 Object Library 具有更高的優先順序。當一個過程引用一個物件時,VB 從引用對話方塊裡列
出的庫裡按循序搜尋所有被引用的物件程式庫。
圖 9-3 為了要操作其它應用程式的物件,你應該建立對該需要的物件程式庫的引用
在建立了對所要求物件程式庫的引用後,你就可以在物件流覽器裡流覽該物件的屬性和方法。
175
圖 9-4 建立了對 Microsoft Word 11.0 Object Library 的引用後(見圖 9-3),Microsoft Word 的所
有物件,屬性和方法都可以從 Excel VBA 工程裡訪問了。
13.創建自動控制對象
依照下列步驟,在你的 VBA 過程裡創建一個自動控制物件:
• 使用 Dim…As Object 或者 Dim…As Application.ObjectType 子句聲明物件變數(參見前面章節
裡的後期和早期繫結使用主題)
• 如果你在使用早期繫結,那麼使用引用對話方塊來建立對應用程式物件型別程式庫的引用
• 如果自動控制物件不存在,那麼使用 CreateObject 函數;如果自動控制物件已經存在的話,那
麼就使用 GetObject 函數來建立對該物件的引用
• 使用關鍵字 Set 將 CreateObject 或者 GetObject 函數返回的物件賦值到物件變數
14.使用 CreateObject 函數
按照下面的語法,使用 CreateObject 函數從 VBA 過程裡創建對自動控制物件的引用:
CreateObject(class)
參數 class 是你想要引用的應用程式的名稱。該名稱包含早先討論的物件類類型(參見早期繫結部
分)。必須使用關鍵字 Set 將自動控制物件賦予物件變數,如下所示:
Set variable_name = CreateObject(class)

例如,使用自動控制物件來啟動 Word,你的 VBA 過程裡需要包括下面的聲明語句:


'early binding 早期繫結
Dim wordAppl As Word.Document
Set wordAppl = CreateObject("Word.Application")

或者
'late binding 後期綁定
Dim wordAppl As Object
Set wordAppl = CreateObject("Word.Application")

通常,CreateObject 函數創建該特定自動控制物件的新示例。然而,一些應用程式註冊為一種叫做
“單一示例”的應用程式了,這就是說你不能同時運行一個以上的示例。Microsoft Word 和
PowerPoint 就是這種單一示例的應用程式,因此,如果 Word 或者 PowerPoint 已經在運行,那麼
176
CreateObject 函數就會直接引用到在運行的示例去,而不會再創建一個新的示例。
15.使用自動控制創建一個新的 Word 文檔
我們來看看你如何將在前面章節學習到的關於綁定的知識應用到現實生活中的例子裡。你也許有時
需要從 Excel 直接通過程式打開一個 Word 文檔,並且往裡面寫入資料,下面的例子使用了早期繫結。
1. 在當前工程裡插入新模組,並重命名為 Automation
2. 在工程流覽器裡,選擇當前工程,並且選擇“工具”|“引用”
3. 如果可用引用列表裡的 Microsoft Word 9.0 Object Library 或者 Microsoft Word 10.0 Object
Library 沒有被勾選,那麼找到它們並勾選上,點擊確定退出。
4. 輸入下面過程 WriteLetter:
Sub WriteLetter()
Dim wordAppl As Word.Application
Application.StatusBar = "Creating Word Application Object..."

Set wordAppl = CreateObject("Word.Application")

With wordAppl
.Visible = True
Application.StatusBar = "Creating a new document..."

.Documents.Add
.ActiveDocument.Paragraphs(1).Range.InsertBefore "Invitation"

Application.StatusBar = "Saving document..."

.ActiveDocument.SaveAs "C:\Invite.doc"

Application.StatusBar = "Exiting Word..."

.Quit
End With
Set wordAppl = Nothing
Application.StatusBar = False
End Sub
5. 切換到 Excel 應用程式視窗,並選擇“工具”|“巨集”|“巨集”,找到過程 WriteLetter 並點擊運行
過程 WriteLetter 開始時聲明物件變數為特定的物件類型(Word.Application)。回想這種生命(早
期報導)要求你建立對 Word 物件程式庫的引用(本章的前面講過)。CreateObject 函數返回的自動控制
物件賦值到一個叫做 wordAppl 的物件變數,因為由子弟控制啟動的應用程式不會出現在螢幕上,所
以使用語句:
wordAppl.Visible = True
使啟動的 Word 應用程式可見,這樣你就可以觀察 VBA 的工作情況。該過程裡後面的語句打開一個新
文檔(Add 方法),並且在第一段輸入文本(InsertBefore 方法),將文檔保存到硬碟(SaveAs 方法),
以及關閉 Word 應用程式(Quit 方法)。每條語句之前都有一條指令,將資訊顯示在 Excel 應用程式窗
口下麵的狀態列上。當 Word 應用程式關閉後,指令:
Set wordAppl = Nothing
清除物件變數,收回該物件佔用的記憶體。語句:
Application.StatusBar = False
將狀態列上的資訊恢復為預設的“就緒”。
前面提到過,Word 是單一示例(single-instance)應用程式,這意味著你不能同時運行一個以上
的 Word 示例,簡單說,如果你沒有啟動 Word,WriteLetter 過程裡面的 CreateObject 函數將會啟動
Word,否則,它將會使用當前活動的 Word 示例。
16.使用 GetObject 函數
如果你確定自動控制物件以及存在並且已經打開,那麼就考慮使用 GetObject 函數,如下所示:
GetObject([pathname][, class])
GetObject 函數有兩個參數,它們都是可選的。使用第一個參數來明確你要打開的檔案名稱,應該
提供完整的檔路徑。如果你忽略該參數,那麼不必須明確參數 class,指明要使用的物件類型,
例如:
177
Excel.Application
Excel.Sheet
Excel.Chart
Excel.Range
Word.Application
Word.Document
PowerPoint.Application
在 Invite.xls 的基礎上創建一個 Excel 物件,並且強制設置為 Excel 5 工作表,你可以使用下列聲明:
‘ late binding 後期綁定
Dim excelObj As Object
Set excelObj = GetObject("C:\Invite.xls", Excel.Sheet.5")

要設定物件變數為某個特定的 Word 文檔的話,你可以使用:


‘early binding 早期繫結
Dim wordObj As Word.Application
Set wordObj = GetObject("C:\Invite.doc")

如果要訪問一個正在運行的 Office 應用程式物件,那麼可以將第一個參數空出:


Dim excelObj As Object
Set excelObj = GetObject(, "Excel.Application")

當你調用不帶第一個參數的 GetObject 函數時,它就會返回一個對該應用程式示例的引用,如果該


應用程式沒有啟動的話,就會產生錯誤。
17.打開存在的 Word 文檔
下面的過程 CenterText 示範了 GetObject 函數的使用,訪問 Invite.doc 文件。回想一下,該文件是
在本章前面的過程 WriteLetter 裡創建的。過程 CenterText 會將指定 Word 文檔裡的第一段居中。該
過程使用了一個叫做 DocExists 的自訂函數來檢查指定的檔是否存在。另外一個自訂函數
(IsRunning)檢查 Word 是否已經在運行。基於上述檢查結果,使用 CreateObject 或者 GetObject
函數。如果出現錯誤,那麼錯誤編號和錯誤描述將會顯示出來。
Sub CenterText()
Dim wordDoc As Word.Document
Dim wordAppl As Word.Application
Dim mydoc As String
Dim myAppl As String
On Error GoTo ErrorHandler
mydoc = "C:\Invite.doc"
myAppl = "Word.Application"
'first find out whether the specified document exists 首先查明該文檔是否存在
If Not DocExists(mydoc) Then
MsgBox mydoc & " does not exist." & Chr(13) & Chr(13) _

& "Run the WriteLetter procedure to create " & mydoc & "."

Exit Sub
End If
'now check if Word is running 現在檢查 Word 是否正在運行
If Not IsRunning(myAppl) Then
MsgBox "Word is not running - will create a new instance of _

Word. "
Set wordAppl = CreateObject("Word.Application")

Set wordDoc = wordAppl.Documents.Open(mydoc)

Else
MsgBox "Word is running - will get the specified document. "

'bind the wordDoc variable to a specific Word document 將變數 wordDoc 綁定到
178
特定的 Word 文檔
Set wordDoc = GetObject(mydoc)
End If
'center the 1st paragraph horizontally on page 將第一段水準居中
With wordDoc.Paragraphs(1).Range
.ParagraphFormat.Alignment = wdAlignParagraphCenter

End With
wordDoc.Application.Quit
SaveChanges:=True
Set wordDoc = Nothing
Set wordAppl = Nothing
MsgBox "The document " & mydoc & " was reformatted."

Exit Sub
ErrorHandler:
MsgBox Err.Description, vbCritical, "Error: " & Err.Number

End Sub
Function DocExists(ByVal mydoc As String) As Boolean

On Error Resume Next


If Dir(mydoc) < > "" Then
DocExists = True
Else
DocExists = False
End If
End Function
Function IsRunning(ByVal myAppl As String) As Boolean

Dim applRef As Object


On Error Resume Next
Set applRef = GetObject(, myAppl)
If Err.Number = 429 Then
IsRunning = False
Else
IsRunning = True
End If
'clear object variable 清除物件變數內容
Set applRef = Nothing
End Function
18.使用關鍵字 New
除了使用 CreateObject 函數來引用到其它的應用程式之外,你可以使用關鍵字 New。關鍵字 New 告訴
VB 創建一個物件的新示例,返回到該示例的引用,以及將引用賦予該物件變數。例如,你可以按下
面的方式使用關鍵字 New:
Dim objWord As Word.Application
Set objWord = New Word.Application
Dim objAccess As Access.Application

Set objAccess = New Access.Application

使用關鍵字 New 聲明的物件變數總是早期繫結的。使用關鍵字 New 比使用 CreateObject 函數更高效。


你每次使用關鍵字 New 的時候,VB 就會創建應用程式的一個新示例。如果該應用程式以及運行,你
就不需要打開另外一個示例,你應該使用 GetObject 函數。關鍵字 New 也可以用來在聲明物件變數的
時候,同時創建一個新的物件示例,例如:
Dim objWord As New Word.Application

179
注意,當你使用關鍵字 New 在 Dim 語句裡聲明物件變數的時候,你就不需要使用 Set 語句了。然而,
不建議使用這種創建物件變數的方法,因為當該物件變數真正被創建後,你就失去對它的控制了。
在聲明中使用關鍵字 New 會導致創建物件,即使它沒有被使用到。因此,如果你想要控制創建的對
象變數,那麼總是使用下述語法聲明你的物件變數吧:
Dim objWord As Word.Application
Set objWord = New Word.Application
Set 語句可以進一步在你需要使用該物件的地方使用,接下來的章節將示範如何使用關鍵字 New 來創
建 Microsoft Outlook 的新示例,並且編寫你的聯繫地址到 Excel 工作表中。
19.使用自動控制訪問 Microsoft Outlook
要從 Excel 直接訪問 Outlook 的物件模型的話,首先就要建立對 Microsoft Outlook 10.0 或者 9.0
Object Library 的引用。下面的程式例子將在 Excel 工作表裡插入你 Outlook 裡面的聯繫資訊。
Sub GetContacts()
Dim objOut As Outlook.Application
Dim objNspc As NameSpace
Dim objItem As ContactItem
Dim Headings As Variant
Dim i As Integer ' array element 陣列成員
Dim r As Integer ' row index 行號
r=2
Set objOut = New Outlook.Application

Set objNspc = objOut.GetNamespace("MAPI")

Headings = Array("Full Name", "Street", "City", _

"State", "Zip Code", "E-Mail")


Sheets(1).Activate
For Each cell In Range("A1:F1")
cell.FormulaR1C1 = Headings(i)
i=i+1
Next
For Each objItem In objNspc.GetDefaultFolder _

(olFolderContacts).Items
With ActiveSheet .Cells(r, 1).Value = objItem.FullName

.Cells(r, 2).Value = objItem.BusinessAddress

.Cells(r, 3).Value = objItem.BusinessAddressCity


.Cells(r, 4).Value = objItem.BusinessAddressState

.Cells(r, 5).Value = objItem.BusinessAddressPostalCode

.Cells(r, 6).Value = objItem.Email1Address

End With
r=r+1
Next objItem
Set objItem = Nothing
Set objNspc = Nothing
Set objOut = Nothing
End Sub
過程 GetContacts 開始聲明一個叫做 objOut 的物件變數來存儲到 Outlook 應用程式的引用,該變數定
義為明確的對象類型(Outlook.Application),因此 VBA 使用早期繫結。
注意在該過程裡,我們使用關鍵字 New(在前面部分由討論)來創建一個新的 Outlook 應用程式物件
示例,返回引用到該示例,並且將該引用賦予聲明的變數 objOut。
為了訪問 Outlook 裡的聯繫專案, 你也需要聲明物件變數來引用 Outlook 的
NameSpace 和
ContactItem。NameSpace 物件代表了儲存為 MAPI(資訊應用程式程式設計介面)的資訊。NameSpace 對
180
象包含了資料夾(聯繫位址,日誌,任務,等等),每個資料夾由一次有它們的專案。一個項目是
Outlook 的一個詳細資料,例如郵件資訊,或者聯繫位址。
使用 For…Each…Next 迴圈在工作表裡寫入列標題之後,過程使用另外一個 For…Each…Next 迴圈來
遍歷聯繫地址資料夾中的專案。GetDefaultFolde 方法返回一個聯繫位址資料夾的物件變數,該方
法有一個參數,該常量代表了你要訪問的資料夾。當所有的聯繫地址都被寫入 Excel 工作表後,該
過程釋放所有物件變數,將它們設定為 Nothing。
注意,當你運行過程 GetContacts 時,你可能會看到一個警告資訊,告訴你程式試圖訪問電子郵件
位址,點擊確定允許操作。
20.接下來……
在本章,你學習了如何從 VBA 程式裡啟動、啟動和控制其它應用程式(Word 和 Outlook)。你學習了
如何使用 SendKeys 方法發送按鍵到另一個應用程式。你也學習了如何手動和程式設計地添加連結和嵌入
對象。最後,你使用自動控制從 Excel 裡創建新的 Word 文檔,以及後來訪問該文檔並設置一些格式。
你也學習了如何從 Outlook 裡獲取聯繫地址並放置到 Excel 工作表中。你使
用兩個新函數
CreateObject 和 GetObject 擴展了你的 VBA 知識。你也學習了如何以及何時使用關鍵字 New。請在第
十五章裡學習如何從 Excel 裡控制 Microsoft Access。
在下一章,你將學習如何通過自訂表單從使用者處收集更多的資料。
第十章對話方塊和自訂表單
在第四章,你學習了如何使用 Excel 內置的 InputBox 函數在 VBA 過程執行期間從用戶處收集單一
數據。但是,萬一你的程式在運行時需要多個資料怎麼辦呢?使用者也許希望一次就提供所有資料,
或者從專案清單中作出所有合適的選擇。如果你定程式必須收集資料的話,那麼你可以:
• 使用內置對話方塊集合
• 創建一個自訂表單
本章將教你如何從 VBA 過程裡顯示內置的對話方塊,以及從零開始設計你自己的自訂表單。
Excel 對話方塊
在開始創建自己的表單之前,你應該花上一些時間學習如何利用 Excel 內置的對話方塊,這些內置對
話框本來就是為我們準備的。我講的不是手動選擇適合的選項,而是從你自己的 VBA 過程裡調用這
些對話方塊。
Excel 有一個特殊的內置對話方塊集合,它們用開頭為 xlDialog 的常量表示,例如 xlDialogClear,
xlDialogFont,xlDialogDefineName 和 xlDialogOptionsView。這些內置對話方塊是 Excel 物件,屬於
內置 Dialos 集合,每個 dialog 物件代表一個內置對話方塊。
表 10-1 常用的內置對話方塊
對話方塊名稱
新建
打開
另存為
頁面設置
列印
字體
常量
xlDialogNew
xlDialogOpen
xlDialogSaveAs
xlDialogPageSetup
xlDialogPrint
xlDialogFont
按照下述格式使用 Show 方法來顯示對話方塊:
Application.Dialogs(常量).Show
例如,下面的語句顯示字體對話方塊:
Application.Dialogs(xlDialogFont).Show

如果你在物件流覽器裡面選擇 Excel 庫後,再輸入 xlDialog 搜索的話,那些代表 Excel 內置對話方塊的


常量清單就會顯示在物件流覽器裡面了(參見圖 10-1)
1. 打開一個新工作簿並且保存為 Chap10.xls
2. 切換到 VB 編輯器視窗
3. 打開立即視窗
181
4. 輸入下述語句並查看結果:
Application.Dialogs(xlDialogClear).Show

Application.Dialogs(xlDialogFont).Show

Application.Dialogs(xlDialogFontProperties).Show

Application.Dialogs(xlDialogDefineName).Show

Application.Dialogs(xlDialogOptionsView).Show

最後一句指令顯示“選項”對話方塊的“視圖”。顯示內置對話方塊後,你可以選擇合適的選項,然後
Excel 就會將當前被選擇的儲存格,區域或者整個工作表設置相應的格式。
儘管你不能更改內置對話方塊的外觀和行為,但是當你從你的 VBA 過程顯示內置對話方塊的時候,你可
以決定它的初始設置。如果你不更改初始設置,那麼 VBA 將顯示對話方塊和其缺省設置。
假設你要顯示清除對話方塊,並且所有按鈕都被選擇上。通常 Excel 顯示對話方塊的時候,內容選項按
鈕是被選擇上的。在立即視窗裡輸入下列語句:
Application.DialogS(xlDialogClear).Show 1

你可以在 Show 方法後面加上一系列的參數,在清除對話方塊裡,“全部”選項按鈕出現在四個選項按


鈕組的最開頭。Excel 通常將可用的選項進行編號,因此,“全部”=1,“格式”=2,“內容”=3,以
及“批註”=4。線上說明可以搜索到內置對話方塊的參數列表(參見圖 10-3)
圖 10-1 首碼為“xlDialog”的常量識別 Excel 內置對話方塊
在立即視窗裡輸入下面的語句,可以顯示字體對話方塊,並且當前選擇為“Arial”字體和 14 字型大小:
Application.Dialogs(xlDialogFont).Show "Arial", 14

如果只要明確字型大小的話,那麼可以在第一個參數的位置放置一個逗號就行:
Application.Dialogs(xlDialogFont).Show , 8

下面的指令顯示“定義名稱”對話方塊,並且在工作簿中的“名稱”文字方塊中輸入“John”,“引用位
置”裡引用到儲存格 A1:
Application.Dialogs(xlDialogDefineName).Show "John", "=$A$1"

如果你點擊確定 Show 方法就返回 True,點擊取消則為 False。


182
圖 10-2 以常量 xlDialogOptionsView 代表的“選項”對話方塊“視圖”的可用設置
圖 10-3 Excel 內置對話方塊參數列表
1.文件打開和另存為對話方塊
OfficeXP 中一個新而功能強大的物件是 FileDialog。該物件允許你從你的 VBA 過程裡顯示檔打開
和文件另存為對話方塊。因為 FileDialog 物件是 Microsoft Office 10.0 Object Library 的一部分,
183
所以它在所有的 Office XP 應用程式裡都是可用的。在前期的 Excel 版本中,程式師使用了兩種特殊
的方法來顯示檔打開和檔另存對話方塊,這些方法(GetOpenFilename 和 GetSaveAsFilename)將
在本章後面解釋。要在你的 VBA 過程裡面使用新的 FileDialog 物件來顯示檔打開對話方塊的話,你
可以輸入下列語句:
Application.FileDialog(msoFileDialogOpen).Show
要顯示檔另存對話方塊的話,則使用下面的語句:
Application.FileDialog(msoFileDialogSaveAs).Show

現在,我們在立即視窗裡輸入上面的語句來看看檔打開和檔另存對話方塊。
除了檔打開和檔另存為對話方塊之外,FileDialog 物件也能夠顯示“流覽”對話方塊,列出檔和
資料夾(參見圖 10-4),或者資料夾(圖 10-5):
‘ browse the list of files and folders 流覽檔和資料夾清單
Application.FileDialog(msoFileDialogFilePicker).Show

圖 10-4 檔採集對話方塊允許用戶選擇一個或多個檔,該對話方塊顯示檔和資料夾清單,並且標
題顯示為“流覽”
‘ browse the list of folders 流覽資料夾清單
Application.FileDialog(msoFileDialogFolderPicker).Show

圖 10-5 資料夾採集對話方塊允許使用者選擇一個路徑,該對話方塊顯示目錄清單,並且標題顯示為“瀏
覽”
檔對話方塊使用的常量列在下面的表格裡,首碼“mso”表明這些常量都是 Microsoft Office 物件
184
模型裡一部分。
msoFileDialog 常量
msoFileDialogOpen
msoFileDialogSaveAs
msoFileDialogFilePicker

1
2
3
msoFileDialogFolderPicker 4
可以使用 FileDialog 的 Filters 屬性來控制顯示檔的類型。如果你打開檔打開對話方塊下面的“文
件類型”下拉式清單方塊時,你將看到許多可選擇的文件篩檢程式。那裡有 24 種預先設置好的檔篩檢程式,
你也可以在該清單裡添加你自己的篩檢程式。在立即視窗裡輸入下述語句,我們就可以得到缺省的文
件篩檢程式數目了:
set f = Application.FileDialog(msoFileDialogOpen).Filters

?f.count
FileDialog 物件的篩檢程式儲存在 FileDialogFilters 集合裡面。我們來創建一個簡單的過程,將缺
省的文件篩檢程式返回到 Excel 工作表:
1. 在當前 VBA 工程裡插入一個新模組,並且重命名為 DialogBoxes
2. 在 DialogBoxes 代碼視窗裡輸入下面顯示的 ListFilters 過程:
Sub ListFilters()
Dim fdfs As FileDialogFilters
Dim filt As FileDialogFilter
Dim c As Integer
Set fdfs = Application.FileDialog(msoFileDialogOpen).Filters

Sheets(3).Cells(1, 1).Select
Selection.Formula = "List of Default Filters"

With fdfs
c = .Count
For Each filt In fdfs
Selection.Offset(1, 0).Formula = filt.Description & _

": " & filt.Extensions


Selection.Offset(1, 0).Select
Next
MsgBox c & " filters were written to Sheet3."

End With
End Sub
該過程聲明了兩個物件變數,變數 fdfs 返回對 FileDialog 物件裡的 FileDialogFilters 集合的引用,
而物件變數 filt 則儲存對物件 FileDialogFilter 的引用。FileDialogFilters 集合的 Count 屬性返回
文件篩檢程式的總數。之後,過程遍歷篩檢程式集合,並且找到每個篩檢程式的描述和副檔名。
使用 FileDialogFilters 集合的 Add 方法,你可以輕易地將你自己的篩檢程式添加到缺省的篩檢程式中
去。下面修改後代工程 ListFilters2 示範了如何將暫存檔案(*.tmp)篩檢程式添加到篩檢程式清單中
去。該過程裡的最後語句將打開檔打開對話方塊,因此你自己可以檢查自訂的篩檢程式是否已經被
添加到了檔案類型下拉式清單方塊裡。
Sub ListFilters2()
Dim fdfs As FileDialogFilters
Dim filt As FileDialogFilter
Dim c As Integer
Set fdfs = Application.FileDialog(msoFileDialogOpen).Filters

Sheets(3).Cells(1, 1).Select
Selection.Formula = "List of Default Filters"

With fdfs
185
c = .Count
For Each filt In fdfs
Selection.Offset(1, 0).Formula = filt.Description & _

": " & filt.Extensions


Selection.Offset(1, 0).Select
Next
MsgBox c & " filters were written to Sheet3."

.Add "Temporary Files", "*.tmp", 1


c = .Count
MsgBox "There are now " & c & " filters." & vbCrLf _

& "Check for yourself."


Application.FileDialog(msoFileDialogOpen).Show

End With
End Sub
你可以使用 FileDialogFilters 集合的 Clear 方法清除所有預設的篩檢程式。修改一下上面的過程,在
添加自訂的暫存檔案(*.tmp)篩檢程式之前,清除內置的篩檢程式。
當你從檔打開對話方塊裡選擇一個檔時, 該被選擇的檔案名稱和路徑就會被
放置在
FileDialogSelectedItems 集合裡。使用 SelectedItems 屬性可以返回 FileDialogSelectedItems 集
合。通過設定 FileDialog 物件的 AllowMultiSelect 屬性為 True,使用者就可以同時按下 Shift 鍵或者
Ctrl 鍵和檔案名稱,選擇一個或多個檔。
接下來的過程示範了如何使用上面提及的屬性,該過程打開一個新的工作簿並且插入一個清單方塊控
件。允許用戶選擇一個以上的檔,然後被選擇的檔將加入到該清單方塊控制項裡,並且加亮第一個
檔案名。
Sub ListSelectedFiles()
Dim fd As FileDialog
Dim myFile As Variant
Dim lbox As Object
Set fd = Application.FileDialog(msoFileDialogOpen)

With fd
.AllowMultiSelect = True
If .Show Then
Workbooks.Add
Set lbox = Worksheets(1).Shapes. _
AddFormControl(xlListBox, _
Left:=20, Top:=60, Height:=40, Width:=300)
lbox.ControlFormat.MultiSelect = xlNone

For Each myFile In .SelectedItems


lbox.ControlFormat.AddItem myFile
Next
Range("B4").Formula = _
"You've selected the following " & _

lbox.ControlFormat.ListCount & " files:"

lbox.ControlFormat.ListIndex = 1
End If
End With
End Sub
186
圖 10-6 使用過程 ListSelectedFiles(見上面)將用戶選擇的文件添加到工作表中清單方塊控制項中去
注意,Show 方法不會將用戶所選的檔打開,它僅僅顯示檔打開對話方塊。當用戶點擊“打開”按
鈕時,檔案名稱通過 SelectedItems 屬性從 SelectedItems 集合裡獲得。如果你希望用戶點擊“打開”
按鈕時立即執行檔的打開操作的話,你就應該使用 FileDialog 物件的 Execute 方法。下面的過程
示範了如何立即打開用戶選擇的文件:
Sub OpenRightAway()
Dim fd As FileDialog
Dim myFile As Variant
Set fd = Application.FileDialog(msoFileDialogOpen)
With fd
.AllowMultiSelect = True
If .Show Then
For Each myFile In .SelectedItems
.Execute
Next
End If
End With
End Sub
2.GetOpenFilename 和 GetSaveAsFilename 方法
從多年以前開始,Excel 就給程式師們提供了兩種方便的 VBA 方法來顯示檔另存為和檔打開對話
框:GetOpenFilename 和 GetSaveAsFilename。這些方法只有在 Excel 裡可用,並且在 Excel2002 裡
面如果需要向後相容的話仍然可用。
GetOpenFilename 方法顯示“打開”對話方塊,在那裡你可以選擇要打開的檔案名稱,第二個方法
(GetSaveAsFilename)則顯示另存為對話方塊。
1. 在立即視窗輸入下面的指令:
Application.GetOpenFilename
Application.GetSaveAsFilename
Application.GetSaveAsFilename ("Plan2.xls")

GetOpenFilename 方法從用戶處獲得檔案名稱,而不必實際打開某特定的文件。該方法有四個可選
的參數,經常使用的是第一和第三個參數,顯示入下表:
187
GetOpenFilename 參數描述
fileFilter
title
該參數決定了對話方塊的檔案類型(譯者:原文為 Save as type,有誤)
下拉清單了的內容。例如, 要在檔案類型下拉清單裡顯示
“ Excel
Files(*.xls)”的話,你就應該輸入下列文本作為 fileFilter: “Excel
Files(*.xls), *.xls”(譯者:“Excel Files, *.xls”也一樣。)過
濾器的前面部分(逗號前)決定檔案類型下拉清單要顯示的文本,第二部
分(逗號後)明確你要顯示的那種類型的檔。確保你按照表格裡的例
子試驗一下。
這是對話方塊的標題,如果忽略,對話方塊將顯示標題為“打開”
在立即視窗裡輸入下列語句(確保在一行輸入),來看看這些參數是如何使用的:
Application.GetOpenFilename("Excel Files(*.xls), *.xls"),,"Highlight the File"

GetOpenFilename 方法返回所選的或者指定的檔案名稱,該名稱之後可以在你的 VBA 過程裡用來打開


該檔,例如:
yourFile = Application.GetOpenFilename

?yourFile
C:\EXCEL\Mark.xls
Workbooks.Open Filename:=yourFile
在上面的例子裡,檔案名稱被賦予變數 yourFile,接下來的兩條輸入為詢問檔案名稱(?yourFile)
和顯示該名稱(C:\EXCEL\Mark.xls)。第四條語句打開變數 yourFile 明確的檔。如果你通過點擊
Esc 鍵或者對話方塊上的取消按鈕來取消對話方塊的話,那麼 GetOpenFilename 方法就會返回 False。
GetSaveAsFilename 方法返回檔案名和路徑,然而,它不會自動地保存該特定的檔。輸入下述指
令提供檔案名稱:
Application.GetSaveAsFilename ("Plan2.xls")

如果你忽略檔案名稱的話,Excel 就會顯示當前使用中的檔案的名稱。當你使用 GetSaveAsFilename 方法


時,你可以明確檔篩檢程式和對話方塊自訂標題:
yourFile = Application.GetSaveAsFilename(“Plan2.xls”, "Excel Files(*.xls),
*.xls",,"Name your file")
要顯示另存為對話方塊的話,就要將 GetSaveAsFilename 方法的結果賦予一個變數,如上所示。
3.創建表單
儘管內置的對話方塊很方便使用,但是它並不能滿足你所有的 VBA 應用程式的要求。除了將對話方塊顯
示在螢幕上和初始化它的設置之外,你不能控制對話方塊的外觀,你不能決定增加哪個按鈕,刪除哪
個按鈕,而哪個又移動,同樣,你不能改變內置對話方塊的大小。如果你想用提供一個自訂的介面
的話,那麼你的唯一辦法就是創建一個使用者表單。
使用者表單看上去就像一個自訂對話方塊,你可以在上面添加各種各樣的控制項,給這些控制項設置屬性
以及編寫對表單反應的 VBA 過程和控制事件。表單是單獨的物件,你可以在 VB 編輯器功能表裡選擇“插
入”|“使用者表單”來添加表單。
表單可以在不同的應用程式之間分享使用,例如,你可以在 Word 或者任何其它使用 VB 編輯器的應用
程式裡面,重新使用 Excel 裡設計的表單。
按照下述步驟創建自訂表單:
• 切換到 VB 編輯器視窗
• 選擇“插入”|“使用者表單”
一個叫做表單的資料夾顯示在工程流覽器視窗,該資料夾包含一個空白使用者表單。工作區域自動顯
示表單和帶有添加控制項的工具箱。
188
圖 10-7 通過選擇插入功能表裡面的使用者表單可以添加一個新的表單到活動 VBA 工程
屬性視窗(見圖 10-8)顯示很多種屬性,你可以根據你的需要進行設置。該表單內容安排成七個類
別:外觀,行為,字體,雜項,圖片,位置和滾動。要按類別顯示表單內容的話,那麼可以點擊屬
性視窗上的“按分類序”。要查找某特定屬性的資訊的話,可以點擊該屬性名稱並且按下 F1 鍵,在
線説明將啟動該屬性描述主題。在你的 VBA 工程裡添加新表單後,你應該通過設置“名稱”(Name)
屬性給該表單設置一個獨特的名稱,除了名稱之外,每個表單還應該包含一個標題,你可以使用
Caption 屬性給你的表單設置標題。
技巧 10-1 在 VBA 應用程式之間共用表單
所有使用 VB 編輯器的 VBA 應用程式在創建自訂表單時共用一些功能特點,你可以通過從檔中導
出和導入,或者通過拖曳表單物件到另一個工程來共用你的表單。要導入或者匯出表單檔,可以
選擇“檔”|“導入”或者“檔”|“匯出”。在你匯出某個表單檔之前,確保你在工程流覽
器視窗裡選中了該表單。在將表單拖曳到一個不同的 VBA 應用程式之前,你得安排好 VBE 視窗,確保
你能同時看到兩個應用程式的工程流覽器視窗,拖住工程流覽器裡的表單名稱到另外一個工程流覽
器。
189
圖 10-8 使用屬性視窗,你可以很容易地更改你自訂表單的外觀,行為和其它特點
4.創建使用者表單的工具
當你設計一個表單時,你可能要插入一些合適的控制項來使它有用。工具箱包含了標準 VB 所有你可以
添加到表單上的控制項按鈕,它也可以包含已經安裝在你電腦上的額外的控制項。工具箱可用的控制項被
稱為 ActiveX 控制項,這些控制項能夠對特定的使用者行為例如點擊該控制項或改變它的製作出反應。你將
在本章剩餘的部分學習如何使用工具箱控制項。
圖 10-9 工具箱顯示了可以添加到你的自訂表單的控制項(譯者:在每台電腦上的工具箱上顯示控
件可能不一樣,可以通過“附加控制項”在工具箱上添加額外的控制項按鈕)
Microsoft Office 套裝提供了額外可以放置在工具箱上以快速訪問的 ActiveX 控制項,如果你的電腦
上還安裝了其它包含 ActiveX 應用程式的話,那麼你也可以將它們放置在工具箱上。依照下述步驟
在工具箱上添加其它的 ActiveX 控制項:
1. 在工具箱上(非控制項區域)按一下右鍵並且點擊“新建頁”,並且選擇重命名
2. 在“題注”框裡面輸入新名稱“額外控制項”,在“控制項提示文字”框裡面輸入“額外的 ActiveX
控制項”,點擊確定返回到工具箱
3. 在新頁的任意地方按一下右鍵,並從快顯功能表中選擇“附加控制項”。如果該選項不可用的話,那麼
確定你在該頁區域按一下右鍵,而不是點擊“額外控制項”本身
4. 當附加控制項對話方塊出現時,點擊你要添加的每個控制項前面的選項按鈕。圖 10-10 顯示了加亮的日
曆控制項。當你點擊確定時,這個控制項就會出現在工具箱的當前頁上
190
圖 10-10 你在工具箱上附加額外安裝在你電腦上的 ActiveX 控制項
在接下來的幾段中將描述標準的 VB 控制項。
5.標籤
標籤允許你在表單上添加文本,標籤控制項經常用來添加字幕,標題,抬頭和解釋。你可以使用標籤
給那些不帶 Caption 屬性的控制項(例如文字框,清單方塊,捲軸和旋轉按鈕)加上標題。你可以給
標籤定義快速鍵,例如,在添加完標籤控制項和設置它 Accelerator 屬性後,通過按下 Alt 鍵和某個特
定字母鍵,你就可以立即啟動該控制項(譯者:啟動標籤控制項後面的控制項,例如文字框,清單方塊等。
這是一個非常有用的工具!)。要給某個已經存在的控制項添加一個標題或者一個鍵盤快速鍵的話,你
就應該添加一個標籤控制項,並且在它屬性視窗的 Accelerator 屬性裡輸入一個字母。下一步選擇“視
圖”|“Tab 鍵順序”,並且確保該標籤名稱出現在你要使用快速鍵啟動的控制項名稱之前。你將在本
章後面學習如何使用 Tab 鍵順序對話方塊(參見圖 10-14)
6.文字框
文字框是最流行的表單控制項,因為它們可以用來顯示或者從使用者處獲取資料。你可以在文字框裡輸
入文本、數位、儲存格引用或者公式。通過更改 MultiLine 屬性設置,你可以在文字框裡輸入一行
以上的文本。當你設置了 WordWrap 屬性時,文本行可以自動換行。此外,如果你將 EnterKeyBehavior
屬性設置為 True,同時 MultiLine 屬性也為真的話,那麼你將能夠在文字框裡面通過按下回車鍵開
始新的一行。另外一個屬性 EnterFieldBehavior,決定當使用者選擇文本區域時是否選擇文本(譯者:
應該說啟動該控制項, 例如說使用 Tab 鍵來啟動文字框。), 設置該書寫為
0
( fmEnterFieldBehaviorSelectAll ) 的話, 將會選擇該區域的所有文本, 設置該
屬性為 1
(fmEnterFieldBehaviorRecallSelection)的話,將僅僅選擇使用者上次啟動該區域時選擇的文本。
如果你想要限制使用者在文字框輸入字元數的話,那麼你可以設置 MaxLength 屬性的字元確切數目。
7.框架
框架允許你可視地組織和邏輯地聚合表單上面的各種控制項。當你使用框架控制項包圍選項按鈕時,VB
將這些按鈕視為相互排斥的,你同時只能選擇其中的一個按鈕,因此,如果使用者選擇了可用的選項
按鈕之一 i,那麼其它的選項按鈕就不能再被選擇。在本章的後面,你將找到使用兩個框架的“信
息調查”表單示例。其中一個將“硬體”和“軟體”選項按鈕群組織成一個邏輯化的集合,而第二個
框架則將和電腦類型相關的核取方塊集中到一起(參見圖 10-11)。
8.選項按鈕
選項按鈕讓你在許多相互排斥的選項中選擇一個,選項按鈕通常以兩個或者多個按鈕組織在一起並
且包圍在一個框架之內。在任何時刻,只能選擇一個選項按鈕。當你選擇一個新選項按鈕,之前被
選擇的選擇按鈕就會自動取消選定。要啟動或者失活一個選項按鈕的話,只要將它的 Value 屬性設
置為 True 或者 False。True 意味著該選項被啟動,False 則意味著該選項失活。
191
9.核取方塊
核取方塊用來將具體的選項打開或者關閉,不同于選項按鈕只讓你同時選擇一個選項,使用者可以同時
選上一個或者多個核取方塊。如果核取方塊被選擇,那麼它的 Value 屬性就設置為 True,如果核取方塊沒
有被選擇,那麼它的 Value 屬性則設置為 False。
10.切換按鈕
切換按鈕看上去像命令按鈕,而作用與選項按鈕類似。當你點擊切換按鈕時,該按鈕會保持凹下去,
如果再點擊一次的話,按鈕則恢復正常。按下的切換按鈕的值屬性為 True。
11.清單方塊
除了可以使用文字框提示使用者輸入明確的資料之外,有時候提供一個可供選擇的清單可能會更好,
清單方塊排除了輸入錯誤資料的可能性。清單方塊的內容可以在工作表中敲入,也可以直接從 VBA 過程
裡使用 AddItem 方法添加。RowSource 屬性指定顯示在清單方塊裡面的資料來源,例如引用$A$1:$B$8,
清單方塊裡將顯示這些區域裡的內容。
當你設置 ColumnCount 屬性時,清單方塊可以顯示一列或者多列資料。另外一個屬性,ColumnHeads,
可以設置為 True,顯示清單方塊的列標題。使用者也沒有限制只選擇一個選項,如果過程需要選擇兩個
或者多個列表項目的話,你也可以設置 MultiSlect 屬性為 True。
12.複合框
複合框是一個結合文字框和清單方塊在一起的控制項,該控制項經常用來節省表單上的空間。當用戶點擊
複合框右邊的下拉箭頭時,它會打開顯示一系列可供選擇的項目。如果裡面沒有一個適用的選項的
話,你可以將 MatchRequired 屬性設置為 False,這樣就允許使用者直接輸入一個新的資料。ListRows
屬性決定了下拉清單出現時, 顯示的專案數。Style 屬性決定了複合框的
類型。使用 0
(fmStyleDropDownCombo)可以允許使用者從清單裡選擇一項或者在文字框裡輸入一新項。如果要限
制用戶的選擇只能在該複合框的可用清單裡的話, 那麼就將 Style 屬性設
置為 2
(fmStyleDropDownList)。
13.捲軸
你可以在表單上放置水準的或者豎直的捲軸。儘管捲軸通常使用於錨定視窗,但是它也可以用
在你表單上來輸入一些預設範圍的數值。捲軸的當前值由 Value 屬性設置或者返回。捲軸的 Max
屬性讓你設置它的最大值,而 Min 屬性則決定了它的最小值。LargeChange 屬性決定了當使用者點擊滾
動條內部時 Value 屬性的改變值。同樣,當使用捲軸程式設計時,不要忘記設置 SmallChange 屬性,它
決定當你點擊捲軸的箭頭時 Value 屬性如何改變。
14.旋轉按鈕
旋轉按鈕作用類似於捲軸。你可以點擊旋轉按鈕來增加或者減小某個數值。旋轉按鈕經常和文字
框一起使用,因為這樣使用者就可以使用旋轉按鈕在文字框裡敲入精確的值或者選擇數值。和文字框
一起使用旋轉按鈕的技術將會在本章後面討論到。
15.圖像
圖像控制項讓你可以在表單上顯示圖像,該控制項支援下列檔案格式:*.bmp,*.cur,*gif,*.ico,
*.jpg 和*.wmf。就像工具箱裡的其它控制項一樣,圖像控制項也有許多屬性可以設定。例如,你可以使
用 PictureSizeMode 屬性控制圖片的外觀,該屬性有三種設置:0(fmPictureSizeModeClip 將不在
圖片框裡面的部分截除),1(fmPictureSizeModeStretch 水準或豎直拉伸圖片,使之正好適合圖片
框)和 3(fmPictureSizeModeZoom 按比例放大圖片)。
16.多頁控制項
多頁控制項可以在表單頂部顯示一系列的頁面(參見圖 10-17)。每小頁作為單獨的頁面使用。使用多
頁控制項,你可以設置包含兩頁或多頁的表單,你可以在每頁上放置不同的控制項。當一個表單包含很
多的資料時,它的可讀性便降低了。點擊表單頁要比在一個使用捲軸的長表單上移動要輕鬆的多。
預設上,每個多頁控制項在表單上顯示兩頁,通過快顯功能表可以添加新頁,也可以在 VBA 過程裡使用
192
Add 方法添加新頁。本章中的第二個實踐練習時犯了如何使用該控制項來追蹤學生的考試分數。
17.TabStrip 控制項
雖然 TabStrip 和多頁控制項看上去非常相似,但是它們有各自不同的作用。TabStrip 控制項(參見圖
10-17)允許你使用相同的控制項來顯示多套相同的資料。假設表單顯示學生的考試,每個學生必須
通過相同科目的考試。每個科目可以放在一個單獨的頁上(tab),每頁包含相同的控制項來收集得分
和考試日期。當你啟動任何頁的時候,你將看到相同的控制項,只有這些控制項裡的資料變化。參見本
章的第二個實踐練習,看看如何使用 TablStrip 控制項。
18.RefEdit 控制項
RefEdit 控制項是專門在 Excel 裡面創建的表單控制項,它允許你在工作表裡選擇一個儲存格或者儲存格
區域並且傳遞到你的 VBA 過程。看一下一些 Excel 內置對話方塊,你就可以看到這個控制項是如何工作的,
例如,“資料”功能表裡的“合併計算”對話方塊就有一個標為“引用位置”的 RefEdit 控制項,讓你選定
想要進行合併計算的資料區域。點擊 RefEdit 右邊的按鈕,可以在選擇儲存格區域的時候暫時隱藏
對話方塊。本章的第二個實踐練習使用 RefEdit 控制項給清單方塊添加學生姓名。
19.在表單上放置控制項
當你創建自訂表單的時候,你將工具箱裡可用的很多控制項(參見圖 10-9)放置在一個空表單上。
選擇什麼樣的控制項取決於控制項需要儲存的資料類型,以及你表單的功能。當你使用表單時,工具箱
總是可見的,你可以在螢幕上移動它,改變它的大小,或者當你將所有需要的控制項放在表單上了並
且你要做的只是設置它們的屬性,你也可以關閉它。臨時被移除的工具箱也可以通過選擇“視圖”
|“工具箱”重新顯示。
工具箱的使用是很容易的,要在表單上添加新控制項的話,可以先點擊工具箱上面的控制項圖示,然後
在表單上點擊一下或者畫一個框。在表單上點擊一下(不畫框)將會在表單上放置一個缺省大小的
控制項。每個控制項的標準設定可以在它的屬性視窗裡查找到。例如,標準的文字框大小為 18X72 磅(參
見文字框的 Height 和 Width 屬性)。在表單上放置控制項後,“選定物件”按鈕(用箭頭代表)成為工
具箱上的活動控制項。如果你按兩下工具箱上的控制項時,你可以隨你需要畫上很多這個控制項。例如,要
快速在表單上放置三個文字框,可以按兩下文字框控制項,然後在表單上點擊三次。點擊工具箱上的選
定物件按鈕,可以失活所選的控制項。
技巧 10-2 設置網格選項
當你在表單上拖曳控制項時,VB 將調節控制項以使得它和表單的網格對齊。通過使用“選項”對話方塊你
可以按你的喜好設置表單的網格。要訪問網格選項的話,可以選擇“工具”|“選項”,然後點擊選
項對話方塊的“通用”頁,表單網格設置區域允許你關閉網格、調整網格大小,以及決定是否需要控
件和網格對齊。
20.應用程式示例 1:資訊調查
既然你已經通讀了創建使用者表單的理論知識,並且瞭解了工具箱上不同控制項之間的區別,你已經可
以來做一些實踐練習了。你可能也知道,理解一個複雜特徵的最好方式就是將它應用到一個實際生
活的項目中。在這部分,你將給合作者創建一個自訂表單,他要求你將給工作表輸入調查資料的
單調過程簡單化。使用該表單時(參見圖 10-11),你將有機會體驗許多控制項和它們的屬性,另外,
你也將學習如何將資料從你的表單轉移到工作表(參見圖 10-12)
在本章結束的時候,你將擁有創建自訂表單的必要技能,適應你 VBA 應用程式獨特的要求。
1. 在工程流覽視窗, 選中工程 VBAProject(Chap10.xls) 並且在屬性視窗將工程
名稱改為
CustomForms
2. 選擇“插入”|“使用者視窗”,添加一個空白表單
3. 在屬性視窗,按兩下 Name 屬性並輸入 InfoSurvey,將表單的缺省名稱(UserForm1)更改掉。你將
在 VBA 過程裡使用給名稱引用到該使用者表單
4. 按兩下 Caption 屬性,並輸入表單新標題:Info Survey。該名稱將出現在表單的標題列上
5. 按兩下 BackColor 屬性,點擊“調色板”並且給表單底色選上一種顏色
193
圖 10-11Info Survey 表單允許使用者通過作一些適當的選擇就可以快速地資料資料
圖 10-12 每次使用表單 Survey 後,使用者的選擇就會寫入到工作表裡面
21.在表單上添加按鈕、選項框和其它控制項
給自訂表單設置完初始屬性(Name 和 Caption)後,我們繼續來給表單放置需要的控制項吧。這裡
是一步一步的指導如何準備如圖 10-11 顯示的表單。
1. 更改表單大小
當你在工程裡插入的缺省表單太小,不夠放置你 VBA 程式要求的控制項時,你可以按照下述方法
之一來更改它的大小:
a. 使用滑鼠調整大小
• 點擊表單的空白部分,表單周圍便會出現好幾個選用符
• 將滑鼠放在表單右邊中間的選用符上,並且將其向右拖曳至你想要的位置,釋放滑鼠
194
• 將滑鼠放在表單下邊中間的選用符上,並且將其向下拖曳至你想要的位置,釋放滑鼠
b. 通過屬性視窗調整表單大小:
每個新建的表單缺省大小為 180 X 240。表單尺寸單位是磅。一磅等於 1/72 英寸。輸入表單兩
個屬性:Height 和 Width 的新數值,可以改變表單的大小。
• 點擊表單的標題列(顯示“Info Survey”的地方)
• 在屬性視窗,按兩下屬性 Height 並且輸入值 252.75,更改 Width 屬性為 405.75
為了避免重複工作,總是在添加需要的控制項之前調整表單的大小。
2. 添加框架
• 點擊工具箱上的框架控制項,這時滑鼠游標變成了一個十字架並且跟隨著被選擇控制項的標

• 指向表單的左上角,然後點擊並拖曳滑鼠畫出一個小矩形。當你釋放滑鼠後,你將看到
一個標題為“Frame1”的小矩形。當該框架被選擇上後,它旁邊就會出現一些選用符,
並且屬性視窗的標題列便會顯示“屬性-Frame1”
• 在屬性視窗,按兩下 Caption 屬性並將預設的標題 Frame1 改為“Main Interest”
3. 添加選項按鈕
• 點擊工具箱上的選項按鈕,將滑鼠移動到你剛才在表單上添加的框架“Main Interest”
內部,點擊並且向右拖曳滑鼠,直到看到一個帶有標籤“OptionButton1”的矩形
• 在屬性視窗將該選項按鈕的 Caption 屬性改為“Hardware”
• 使用相同的技術,在“Main Interest”框架裡添加另外一個選項按鈕並且將 Caption
屬性改為 Software
無論何時當用戶必須從一組相互排斥的選項中選擇一個時就要使用選項按鈕,如果使用者必須
選擇多餘一個的選項的話,就要使用核取方塊。
4. 添加清單方塊
• 點擊工具箱上的清單方塊控制項,這時滑鼠游標變成了一個十字架並且跟隨著被選擇控制項的
標誌。在 Main Interest 框架下麵點擊並且向下向右拖曳滑鼠畫出一個清單方塊,當你釋
放滑鼠後,將看到一個白的矩形。
圖 10-11 顯示了添加了各種硬體的清單方塊。在本實踐工程的後面,你將學習如何在該清單方塊裡
面顯示合適的專案。
5. 添加帶選項按鈕的框架
• 按照第二步在清單方塊下面插入一個框架並且按照第四步將框架的 Caption 屬性改為
Gender。在該框架裡面添加兩個選項按鈕,並且將第一個按鈕的 Caption 屬性改為 Male,
第二個為 Female。參見圖 10-11
技巧 10-3 操作和移動控制項
如果你想要複製控制項的話,那麼就選擇該控制項(被選擇的控制項將會有選用符在其周圍),按住 Ctrl
鍵,將游標置於控制項中央然後按下滑鼠左鍵,拖曳游標到你需要的位置,然後釋放滑鼠,更改按鈕
的 Caption 屬性。
在選擇和移動一組控制項的話,點擊工具箱上面的“選定物件”工具,並且在你需要移動的一組控制項
周圍畫一個矩形框,當你釋放滑鼠後,所有控制項都將被選擇上。(你也可以通過按住 Shift 鍵,並點
擊每個要選擇的控制項來選擇一個以上的控制項——不要只看,動手試試)要移動被選擇的控制群組到窗
體上的另外位置的話,可以點擊選擇區域並拖曳滑鼠到預期位置。
6. 添加帶核取方塊的框架
• 點擊工具箱上的框架控制項,並且在 Main Interest 框架右邊畫一個矩形
• 將其 Caption 屬性改為 Computer Type
• 點擊工具箱上的核取方塊按鈕,並且在剛添加的框架內部點擊一下,框架裡應該出現
CheckBox1 控制項
• 更改 CheckBox1 的 Caption 屬性為 IBM/Compatible
• 在 Computer Type 框架裡面再放置兩個核取方塊,使用 Caption 屬性將這兩個核取方塊標題設
置為:Notebook/Laptop 和 Macintosh。最後的結果應該和圖 10-11 一致。
不像選項按鈕那樣相互排斥,核取方塊允許用戶同時啟動一個或者多個選項。核取方塊在特定時
候可以是選定的,未選定的或者不可用的。不可用的核取方塊會變灰並且不能被選定。選定的
195
核取方塊的標題前面有一個 x 號,具有焦點的核取方塊的標題周圍有虛線包圍。
技巧 10-4 核取方塊還是選項按鈕
同時只能選擇一個選項的時候使用選項按鈕,而核取方塊讓使用者選擇任意多個適合的選項。
7. 添加標籤和複合框





點擊工具箱上的標籤控制項
點擊 Computer Type 框架下面的空白地方,Label1 控制項應該就會出現在那裡
將 Label1 的 Caption 屬性改為 Where Used
點擊工具箱上的複合框控制項
點擊標籤 Where Used 下面的空白地方並且拖曳滑鼠畫出一個長方形,釋放滑鼠。
只有當你點擊控制項右邊的向下箭頭時,複合框才會顯示可用的選項清單。複合框有時也被稱
為下來清單並且用來螢幕上的寶貴空間。儘管使用者一時只能看見清單中的一項,但是通常點
擊向下箭頭可以很快地改變當前選擇。
8. 添加標籤、文字框和旋轉按鈕


點擊工具箱上的標籤控制項
在 Where Used 複合框下面的空白地方點擊一下,將會出現一個標籤控制項,更改其 Caption
屬性為 Percent (%) Used
• 點擊工具箱上的文字框控制項
• 在 Percent (%) Used 標籤右邊點擊一下放置一個預設大小的文字框
• 點擊工具箱上的旋轉控制項,然後點擊文字框控制項的右側,出現一個預設大小的旋轉按鈕。
最後的樣子如圖 10-11 所示
旋轉按鈕有兩個箭頭,用來在一個給定範圍內增加或者減少數值。最大值由 Max 屬性的設置決
定,而最小值則由 Min 屬性設定。旋轉按鈕和捲軸有相同的屬性,但是也有兩個不同之處:
旋轉按鈕沒有捲軸,而且它少一個 LargeChange 屬性。文字框通常放置在旋轉按鈕的旁邊,
這樣允許使用者直接在文字框裡面輸入資料,或者使用旋轉按鈕控制數值。如果旋轉按鈕必須
和文字框一起使用的話,那麼你定 VBA 過程必須確保文字框裡輸入的資料和旋轉按鈕保持同
步。在本練習裡,你將使用旋轉按鈕來顯示所選硬體或者軟體的興趣百分比。
9. 添加命令按鈕
• 按兩下工具箱上的命令按鈕控制項。回想一下按兩下工具箱上的控制項時,表明你想要添加一個
以上的所選控制項
• 點擊表單的右上角,這將導致出現 CommandButton1
• 點擊 CommandButton1 的下面,出現 CommandButton2
• 將 CommandButton1 的 Caption 屬性為 OK,CommandButton2 為 Cancel。
多數自訂表單都有兩個命令按鈕,確定和取消,使使用者能夠接受表單上輸入的資料,或者
離開表單。在本練習裡,OK 按鈕將輸入在表單上的資料轉移到工作表裡。無論何時當他完成
資料登錄的時候,使用者也可以點擊取消按鈕。你將在本章後面編寫合適的 VBA 過程,使按鈕對
用戶的操作有反應。
10. 添加圖像控制項
• 點擊工具箱上的圖像按鈕
• 在 Cancel 按鈕下面用滑鼠點擊,並且拖曳滑鼠,畫一個長方形,釋放滑鼠,最後的樣子
如圖 10-11 所示。表單將根據選擇的是 Hardware 或者 Software 選項按鈕,顯示不同的圖
片。圖像將用 VBA 程式上載。
11. 檢查表單外觀
• 點擊表單標題列,或者點擊表單上的任意空白區域,選定表單
• 按下 F5 鍵或者選擇“運行”|“運行子過程/使用者表單”來顯示使用者將看到的表單
• VB 將切換到 Excel 視窗的當前活動工作表,並且顯示你設計的自訂表單。如果你忘了
選擇表單,就會出現巨集對話方塊(譯者:按下 F5 的時候),關閉宏對話方塊,然後重複前面
兩步
• 點擊表單右上角的關閉按鈕(x)來關閉該表單,返回到 VB 編輯器視窗。回想一下,我
們放置在表單上的 OK 和 Cancel 按鈕都還沒有任何功能,它們需要 VBA 過程讓它們工作。
在表單上添加完控制項後,你可以使用滑鼠或者格式功能表命令來調節控制項的對齊和空間。
196
技巧 10-5 使用使用者表單工具列
使用者表單工具列包含很多有用的使用表單的快速鍵,例如使控制項大小一樣,水準或者數值居中,控
件邊緣對齊,以及組合或取消群組控制項。選擇“視圖”|“工具列”|“使用者表單”。
22.更改控制項名稱
在開始編寫程式控制表單之前,你應該給每個放置在表單上的控制項分配自己的名稱。儘管 VB 自動給
每個控制項分配一個缺省名稱,但是要在一個過程裡引用多個幾乎具有一樣的名稱的同類物件的話,
這些名稱是很難區分的,例如:OptionButton1, OptionButton2,等等。給表單上的控制項分配有
意義的名稱可以讓你引用這些控制項的 VBA 過程可讀性增加。
給控制項命名:
• 點擊表單上合適的控制項
• 按兩下屬性視窗的 Name 屬性
注意:在更改 Name 屬性之前,你得確保屬性視窗的標題列顯示正確的控制項類型。例如,要給一個框
架控制項重命名的話,就要先點擊表單上的框架控制項。當屬性視窗顯示“屬性-Frame1”時,按兩下 Name
屬性,並且在預設名稱加亮區域輸入新的名稱。
不要將控制項的名稱和標題(Caption)混淆。例如,在 Info Survey 表單上,框架的缺省名稱是 Frame1,
但是該控制項的標題是 Main Interest。控制項的標題可以通過設置其 Caption 屬性來更改。控制項的標題
允許使用者明確控制項的目的,以及建議預期的資料類型,然而控制項的名稱是用在 VBA 代碼裡使事情發
生的。
1. 給表單 Info Survey 上的控制項分配名稱,如下表所示:(譯者:你可以按照自己的方式命名控制項,
但是一個好的方法是按表中的樣式命名控制項。例如第一個選項按鈕,optHard,opt 表示該控制項
類型為 OptionButton,而 Hard 則表明該控制項的作用是 Hardware 選項。)
對象類型
第一個選項按鈕
第二個選項按鈕
清單方塊
第三個選項按鈕
第四個選項按鈕
第一個核取方塊
第二個核取方塊
第三個核取方塊
複合框
文字框
旋轉按鈕
第一個命令按鈕
第二個命令按鈕
圖像
23.設置其它控制項屬性
Name 屬性
optHard
optSoft
lboxSystems
optMale
optFemale
chkIBM
chkNote
chkMac
cboxWhereUsed
txtPercent
spPercent
butOK
butCancel
picImage
放置在 Info Survey 表單上的控制項也是物件,這些物件中的每一個都有它們自己的屬性和方法。在
前面的部分,你更改了所有物件的 Name 屬性,這些後面將在 VBA 過程裡引用。控制項的屬性可以在自
定義表單的設計時候設定,也可以在運行的時候設置(也就是說,在 VBA 過程執行的時候)。
我們現在來給所選控制項設置一些屬性。點擊表單上的控制項,定位屬性視窗上希望設置的屬性,並且
在屬性名稱右邊輸入新值。例如,設置控制項 lboxSystems 的 ControlTipText 屬性,點擊表單 Info
Survey 上的清單方塊並且在屬性視窗裡找到 ControlTipText 屬性,在屬性視窗的右邊一列裡輸入你要
顯示的文本,當使用者將滑鼠移動到該清單方塊上面時就會顯示該文本:Select only one item
1. 更改物件屬性,如下所示:
197
對象名稱
lboxSystems
spPercent
spPercent
OK button
Cancel button
picImage
屬性
ControlTipText
Max
Min
Accelerator
Accelerator
PictureSizeMode
更改為
Select only one item
100
0
O
C
0 - fmPictureSizeModeClip
Accelerator 屬性指明物件名稱中的哪一個字母可以用在鍵盤快捷組合家裡來啟動該控制項。該特定
的字母在該物件的標題裡將顯示為底線。例如,顯示該表單後,你將可以使用複合鍵 Alt+O 快速
選擇 OK 按鈕。Info Survey 表單物件的其它屬性將將 VBA 過程裡直接設置。
24.準備工作表以儲存表單資料
使用者在表單上選擇了合適的選項並點擊 OK 按鈕之後,這些被選擇的資料將會轉移到一個工作表中。
然而,在此之前,你得準備一個工作表來接收這些資料,並且給使用者一個易於操作的介面來啟動你
的表單。按照下述步驟來準備你的工作表:
1. 啟動 Excel 窗口
2. 按兩下工作簿 Chap10.xls 的 Sheet1 頁,並且輸入新名稱:Info Survey
3. 輸入列標,如圖 10-13 所示
4. 選擇 K 列和第一行,並且將它們的底色改為你喜歡的顏色(使用“格式”工具列上的“填充底色”
按鈕)。
從工作表裡啟動自訂表單的最簡單方法是點擊一個按鈕,接下來的步驟帶領你在工作表 Info
Survey 裡添加按鈕 Survey
5. 選擇“視圖”|“工具列”,並選擇“表單”
6. 點擊表單工具列上的按鈕控制項,在儲存格 K2 裡放置一個按鈕。當出現指定巨集對話方塊時,在“宏
名”框裡面輸入 DoSurvey,並且點擊確定。稍後你將編寫該過程。
7. 當你返回工作表時,該被指定巨集 DoSurvey 的按鈕(按鈕 1)應該仍然被選中了,給它輸入一個新
名稱:Survey。如果該按鈕沒有被選定的話,那麼就在其上按一下右鍵來選中它,選擇快顯功能表
上的“編輯文字”並輸入 Survey 作為其新名稱。要退出編輯模式,可以點擊按鈕之外的任何地
方。
8. 保存你對 Chap10.xls 做的改變。
198
圖 10-13 Survey 按鈕將會啟動 Info Survey 表單。當使用者點擊表單上的 OK 按鈕時,表單資料將會放
置到該工作表裡面
25.顯示自訂表單
每個使用者表單都有 Show 方法,讓你可以給使用者顯示該表單。在下面的例子裡,你將準備 DoSurvey
過程。回想前面部分,你已經將該過程指定給了 Info Survey 工作表的 Survey 按鈕。
1. 在 VB 編輯器視窗,選擇工程 CustomForms(Chap10.xls),並且選擇“插入”|“模組”
2. 在屬性視窗,將新模組的名稱改為 ShowSurvey
3. 輸入下述顯示自訂表單的過程
Sub DoSurvey()
InfoSurvey.Show
End Sub
注意,Show 方法前面是表單物件的名稱,正如出現在表單資料夾裡面的那樣(InfoSurvey)
4. 保存 Chap10.xls 的變化
5. 切換到 Excel 視窗,並點擊 Survey 按鈕,表單 Info Survey 將出現。
注意,如果你點擊 Survey 按鈕後出現錯誤資訊的話,那麼你可以沒有按前面步驟 6 那樣給該按鈕指
定需要的巨集。要更正該錯誤,可以點擊確定,按一下右鍵于 Survey 按鈕,並選擇快顯功能表上的“指定
巨集”,然後點擊指定巨集對話方塊裡的 DoSurvey 巨集,並點擊確定退出。現在,你可以點擊 Survey 按鈕顯
示表單了。
6. 通過點擊表單右上角的關閉按鈕(x),關閉 Info Survey 表單
26.設置 Tab 順序
使用者可以使用滑鼠或者 Tab 鍵在表單上移動,因為許多使用者傾向於使用鍵盤在表單上移動,所以決
定表單上控制項啟動的順序是很重要的。下列步驟示範設置 Info Survey 表單上控制項的 Tab 順序:
1. 在工程流覽器視窗裡的表單資料夾裡,按兩下 InfoSurvey 表單
2. 選擇“視圖”|“Tab 鍵順序”。Tab 鍵順序對話方塊出現了,該對話方塊按控制項添加的順序顯示表單
InfoSurvey 上的所有控制項名稱。對話方塊的右邊有一些按鈕,允許你向上或者向下移動所選的控
件。要移動某個控制項的話,可以點擊其名稱並且點擊上移或者下移按鈕,直到你想要的位置。
3. 按照圖 10-14 顯示的那樣重新安排 Info Survey 表單上的控制項
4. 點擊確定,退出 Tab 鍵順序對話方塊
199
5. 返回 Excel 介面,並且點擊按鈕 Survey
6. 按 Tab 鍵向前移動,按 Shift+Tab 向後移動
7. 關閉 InfoSurvey 表單。如果你想要更改控制項啟動的順序的話,那麼重新打開 Tab 鍵順序對話方塊,
並作適當的更改。
圖 10-14 Tab 鍵順序對話方塊讓你覺得按 Tab 鍵時控制項啟動的順序
27.瞭解表單和控制項事件
除了屬性和方法之外,每個表單和控制項都有一套預先設計好的事件。事件是指一類操作,例如點擊
滑鼠、按鍵、從清單裡選擇一項或者改變清單方塊裡可用的清單或專案。事件可以由使用者或者系統引
發。編寫事件程序,可以明確表單或控制項如何對該事件作出反應。
當你設計一個自訂表單的時候,你應該預想和規劃運行(當表單使用的時候)時能夠發生的一些
事件。最常見的事件時點擊事件。每當點擊一個命令按鈕的時候,就會引發某個事件程序對該按鈕
的點擊事件作出反應。表單本身可以對 20 多種事件作出反應,包括 Click(點擊)、DblClick(按兩下)、
Activate(啟動),Initialize(初始化)和 Resize(重置大小)。
表 10-2 列出了各種表單控制項可以識別的事件。如果某個控制項不能識別某個事件,那麼表格相應單元
格便顯示“N”,否則為空白。花幾分鐘熟悉一下事件名稱吧,例如,看看表格裡的 AddControl 事件,
一眼就可以看出,該事件只對三個物件可用:框架、多頁和表單本身。
Event

name

Activate

NNNNNNNNNNNNNNN

AddControl

AfterUpdate

BeforeDragOver

BeforeDropOrPaste

BeforeUpdate

Change

Click

DblClick

NNNNNNN

NN

NN

NN

NN

NNNN

NNNN

NN

NNNN

NN

NNN

NN

Deactivate

DropButtonClick

Enter

Error

NN

NN

NNNNNNNNNNNNN

NNNNNNNNNNN
N

Initialize

NNNNNNNNNNNNNNN

Exit

KeyDown

KeyPress

KeyUp

N NN

NNNN

Layout

MouseDown

MouseMove

MouseUp

NNNNNNNNNNNNN

NN

NN

NN

QueryClose

NNNNNNNNNNNNNNN

RemoveControl

NNNNNNNNNNNNN

Resize

NNNNNNNNNNNNNNN

Scoll

NNNNNNNNNNNN

SpinDown

SpinUp

NNNNNNNNNNNNN

NNNNNNNNNNNNN

NN

NN

Terminate

表 10-2 表單和控制項事件
NNNNNNNNNNNNNNN

你創建的每個表單都有一個表單模組用來儲存 VBA 事件程序。你可以通過以下幾種方式來進入表單


模組,編寫事件程序或者找到某個控制項可識別的事件:
• 按兩下某個控制項
• 在控制項上按一下右鍵,並選擇快顯功能表上的查看代碼
200
U

s
e

rF

rm

tB

is

tB

ti

u
tt

le

tt

ra

tto

tr

ip

lt

iP

c
ro

llB

in

tto

Im

fE

it

• 點擊工程流覽視窗的查看代碼按鈕
• 按兩下使用者表單上任何未用的區域
執行以上任意操作都會導致打開表單的代碼視窗。圖 10-15 顯示了通過按兩下表單上的命令按鈕而激
活的代碼視窗。注意,Microsoft Visual Basic 標題列顯示的標題:Chap10.xls – [UserForm1(代
碼)]。表單模組包含一個“通用”部分,也包含放置於表單上面的每個控制項的部分。通用部分用來
聲明表單變數或者常量。
你可以通過點擊右上角複合框右邊的向下箭頭來進入預期的部分。該複合框被稱為過程框,它顯示
左手邊複合框顯示控制項可以識別的所有事件程序。已經編寫了過程的事件顯示為粗體。
圖 10-15 過程框列出了命令按鈕控制項可用的事件程序
28.編寫 VBA 過程對表單和控制項事件反應
在使用者能夠使用自訂表單完成特殊任務之前,你通常必須編寫一些 VBA 過程。正如前面提及的,
VB 編輯器創建的每個表單都有一個模組以儲存該表單使用的過程。
在顯示自訂表單之前,你可以需要設置控制項的初始值。編寫一個 Initialize 事件程序,可以給控
件設置初始值,或者說預設值,每次顯示表單的時候控制項都會擁有這些值。
Initialize 事件發生在表單啟動時,但是在它顯示在螢幕之前。假設你想要 Info Survey 表單顯示
以下初始設置:
1. 在 Main Interest 框架裡,選擇了 Hardware 按鈕
2. 下面的清單方塊包含了對應 Hardware 選項按鈕的內容
3. Computer Type 核取方塊裡沒有一個核取方塊是被選中的
4. 標籤 Where Used 下面複合框顯示第一條可用的專案,並且用戶不能給該複合框添加項目
5. 在旋轉按鈕旁邊的文字框顯示初始值(0)
6. 圖像控制項顯示與 Hardware 或 Software 選項按鈕相關的圖片
29.編寫過程來初始化表單
1. 在工程流覽器視窗,按兩下 InfoSurvey 表單
2. 按兩下表單背景,打開活動表單的代碼視窗。
當你按兩下表單或控制項的時候,代碼視窗會自動打開並且該被點擊的表單或者控制項的 Click 事件就
會出現以編輯。
在程序定義(圖 10-15)時,VB 自動在關鍵字 Sub 之前添加 Private。私有過程只能從當前表單模
塊裡調用,換句話說,在當前工程的其它模組裡的過程不能調用該私有過程。在代碼視窗的頂
端,有兩個複合框,左邊的複合框顯示所有的表單物件;右邊的複合框則顯示所選控制項能夠識
別的所有事件。
3. 點擊過程框的向下箭頭,並且選擇 Initialize 事件,VB 將顯示 UserForm_Initialize 過程在代碼
201
窗口:
Private Sub UserForm_Initialize()
End Sub
4. 在關鍵字 Private Sub 和 End Sub 之間輸入表單的初始設置,完整的 UserForm_Initialize 過程如
下所示:
Private Sub UserForm_Initialize()
'select the Hardware option 選擇 Hardware 選項
optHard.Value = True
'turn off the Software option and all the check boxes 關閉 Software 選項和所有
核取方塊
optSoft.Value = False
chkIBM.Value = False
chkNote.Value = False
chkMac.Value = False
'display a zero in the text box 文字框顯示 0
txtPercent.Value = 0
'call the procedure to populate the list box with

'hardware options 調用過程用硬體選項來填充清單方塊


Call ListHardware
'populate the combo box 添加複合框專案
With Me.cboxWhereUsed
.AddItem "home"
.AddItem "work"
.AddItem "school"
.AddItem "work/home"
.AddItem "home/school"
.AddItem "work/home/school”
End With
'select the first element in the combo box 選擇複合框第一個項目
Me.cboxWhereUsed.ListIndex = 0
'select the first element in the list box 選擇清單方塊的第一個項目
Me.lboxSystems.ListIndex = 0
'load a picture file for the Hardware option 上載 Hardware 選項的圖片檔
Me.picImage.Picture = LoadPicture("C:\cd.bmp")

End Sub
你可以使用關鍵字 Me 代替表單的實際名稱來簡化事件程序代碼,例如,除了使用語句:
InfoSurvey.cboxWhereUsed.ListIndex = 0

之外,你也可以使用下面的語句,節約打字時間:
Me.cboxWhereUsed.ListIndex = 0
特別是當表單名稱很長時,這種技術很有用。注意,清單方塊的第一個成員的索引號為 0,因此,
如果想要選擇列表裡的第二個項目的話,你就必須設置其 ListIndex 屬性為 1。
該過程結束時給圖像控制項上載圖片,請確保該指定的圖片檔可以在指定的資料夾裡找到。如
果你沒有該檔,那麼輸入你想顯示的圖片檔的完整路徑。
過程 UserForm_Initialize 調用外部過程(ListHardware)用硬體成員來填充它的清單方塊控制項。
5. 啟動 ShowSurvey 模組並且輸入過程 ListHardware,如下所示:
Sub ListHardware()
With InfoSurvey.lboxSystems
.AddItem "CD-ROM Drive"
.AddItem "Printer"
.AddItem "Fax"
202
.AddItem "Network"
.AddItem "Joystick"
.AddItem "Sound Card"
.AddItem "Graphics Card"
.AddItem "Modem"
.AddItem "Monitor"
.AddItem "Mouse"
.AddItem "Zip Drive"
.AddItem "Scanner"
End With
End Sub
既然你已經準備好了 UserForm_Initialize 過程和 ListHardware 過程,那麼你可以運行表單查看
結果了。
6. 在工作表 Info Survey,使用 Survey 按鈕來啟動表單,表單顯示後,使用者選擇合適的選項或者點
擊 Cancel 按鈕。當使用者點擊 Software 選項按鈕時,下面的清單方塊應該顯示不同的專案,同時,
圖像控制項應該上載一個不同的圖片。下一節將解釋如何編寫這些事件。
30.編寫過程填充清單方塊控制項
在前面的部分,你準備了 ListHardware 過程用 Hardware 成員來填充 lboxSystems 清單方塊,你可以使
用同樣的方法將 Software 專案上載到該清單方塊。
1. 啟動 ShowSurvey 模組,並輸入過程 ListSoftware 代碼,如下所示:
Sub ListSoftware()
With InfoSurvey.lboxSystems
.AddItem "Spreadsheets"
.AddItem "Databases"
.AddItem "CAD Systems"
.AddItem "Word Processing"
.AddItem "Finance Programs"
.AddItem "Games"
.AddItem "Accounting Programs"
.AddItem "Desktop Publishing"
.AddItem "Imaging Software"
.AddItem "Personal Information Managers"

End With
End Sub
31.編寫程序控制選項按鈕
1. 啟動表單 InfoSurvey,並按兩下位於 Main Interest 框架裡的 Software 選項按鈕
2. 當代碼視窗出現 optSoft_Click 過程構架的時候,選中該代碼並按下 Delete 鍵
3. 點擊右上角的複合框向下箭頭,並且選擇 Change 事件程序,VB 將會自動為你輸入 optSoft_Change
過程的開頭和結尾
4. 輸入 optSoft_Change 過程代碼,顯示如下:
Private Sub optSoft_Change()
Me.lboxSystems.Clear
Call ListSoftware
Me.lboxSystems.ListIndex = 0
Me.picImage.Picture = LoadPicture("C:\Books.bmp")

End Sub
過程 optSoft_Change 開始時,使用 Clear 方法將清單方塊 lboxSystems 的當前成員列表清除,下一
條語句調用 ListSoftware 過程用軟體成員來填充清單方塊,換句話說,當使用者點擊 Software 按鈕
時,程式將硬體成員清除然後添加軟體成員。如果你在添加新成員之前,不清除清單方塊的話,
203
這些新成員就會附加在當前清單後面。語句 Me.lboxSystems.ListIndex = 0 選擇列表裡的第一
條成員。該過程裡的最後一條語句為圖像控制項上載圖片檔,請確保換上你電腦裡有效圖片的
完整路徑。
因為使用者在選擇 Software 按鈕之後,還可能要重新選擇 Hardware 按鈕,所以,你必須給 optHard
選項按鈕創建一個類似的 Change 事件程序。
5. 在 optSoft_Change 過程下面,輸入下述過程 optHard_Change:
Private Sub optHard_Change()
Me.lboxSystems.Clear
Call ListHardware
Me.lboxSystems.ListIndex = 0
Me.picImage.Picture = LoadPicture("C:\cd.bmp")

End Sub
6. 點擊工作表 Info Survey 裡的按鈕 Survey 啟動表單來查看結果。當你點擊 Software 選項按鈕時,
你應該看到清單方塊裡的軟體成員,同時,圖像控制項也應該顯示了相應的圖片。點擊 Hardware 選
項按鈕後,清單方塊應該顯示適當的硬體成員,同時,圖像控制項應該顯示一張不同的圖片。
7. 點擊表單右上角的關閉按鈕,關閉該表單
32.編寫過程同步文字框和旋轉按鈕
Info Survey 表單在旋轉按鈕前面有個文字框,使用者可以直接在文字框裡輸入或者使用旋轉按鈕,
在文字框裡表明 Hardware 或者 Software 成員使用的百分率。文字框的初始值為 0,假設用戶輸入了
10,現在要用旋轉按鈕將它增加到 15。要啟動這個動作的話,那麼該文字框和旋轉按鈕必須得同步。
每個物件需要有一個單獨的 Change 事件程序。
1. 在旋轉按鈕上按一下右鍵,並選擇快顯功能表上的查看代碼
2. 輸入過程 spPercent_Change 過程,如下所示:
Private Sub spPercent_Change()
txtPercent.Value = spPercent.Value
End Sub
使用旋轉按鈕將會導致文字框數值增加或減少。
3. 輸入下述過程 txtPercent_Change:
Private Sub txtPercent_Change()
Dim entry As String
On Error Resume Next
entry = Me.txtPercent.Value
If entry > 100 Then
entry = 0
Me.txtPercent.Value = entry
End If
spPercent.Value = txtPercent.Value
End Sub
過程 txtPercent_Change 確保只有從 0 到 100 之間的數值可以輸入在文字框裡,該過程使用了 On
Error Resume Next 語句來忽略錯誤資料的輸入。如果使用者輸入了一個非數位資料(或者大於 100
的數位),VB 將會將文字框的值重新設置為 0。每次點擊旋轉按鈕,文字框裡的數位將會增加或
者減少 1。
33.編寫過程關閉使用者表單
顯示表單之後,使用者可能需要通過按 Esc 鍵或者點擊 Cancel 按鈕來取消表單,可以準備一個使用 Hide
方法的簡單過程,讓表單從螢幕上消失。
1. 按兩下 Cancel 按鈕,並且輸入下述過程 cmdCancel_Click:
Private Sub butCancel_Click()
Me.Hide
End Sub
204
方法 Hide 將物件隱藏,但是不從記憶體裡清除它。這樣,當使用者看不到表單的時候,你的 VBA 過程
仍然可以在螢幕後面使用該表單的物件和屬性。使用 Unload 方法可以將表單從螢幕上卸退並且
從記憶體裡清除:
Unload Me
當表單卸退後,所有相關的記憶體都會被收回,使用者不能再和表單相互交流了,表單的物件也不能為
你的 VBA 過程訪問了,除非使用 Load 語句再將表單放置於記憶體裡。
34.轉移表單資料到工作表
當使用者點擊 OK 按鈕,表單的選擇應該寫入工作表中,用戶就可以通過點擊 Cancel 按鈕隨時退出表單。
1. 按兩下 OK 按鈕,並輸入過程 cmdOK_Click,如下所示:
Private Sub butOK_Click()
Me.Hide
r = Application.CountA(Range("A:A"))

Range("A1").Offset(r + 1, 0) = Me.lboxSystems.Value

If Me.optHard.Value = True Then


Range("A1").Offset(r + 1, 1) = "*"
End If
If Me.optSoft.Value = True Then
Range("A1").Offset(r + 1, 2) = "*"
End If
If Me.chkIBM.Value = True Then
Range("A1").Offset(r + 1, 3) = "*"
End If
If Me.chkNote.Value = True Then
Range("A1").Offset(r + 1, 4) = "*"
End If
If Me.chkMac.Value = True Then
Range("A1").Offset(r + 1, 5) = "*"
End If
Range("A1").Offset(r + 1, 6) = Me.cboxWhereUsed.Value

Range("A1").Offset(r + 1, 7) = Me.txtPercent.Value

If Me.optMale.Value = True Then


Range("A1").Offset(r + 1, 8) = "*"
End If
If Me.optFemale.Value = True Then
Range("A1").Offset(r + 1, 9) = "*"
End If
Unload Me
End Sub
過程 butOK_Click 以隱藏使用者表單開始。語句:
r = Application.CountA(Range("A:A"))

使用了 VB 函數 CountA 來計算 A 列裡含有資料的儲存格數目,函數的結果被賦予變數 r。下一句:


Range("A1").Offset(r + 1, 0) = Me.lboxSystems.Value

將清單方塊選擇的項目輸入到 A 列最後一個使用了的儲存格的下面一個儲存格(r+1)。接下來,是好
幾條條件陳述式。第一條告訴 VB,當 Hardware 選項按鈕被選中時,在 B 列適當的儲存格裡輸入一個星
號。B 列位於 A 列的右邊一列,因此,在 Offset 方法第二個參數的位置為 1。第二條 If 語句,當用戶
選擇 Software 選項按鈕的話,就在 C 列輸入星號。類似的指令記錄核取方塊的實際數值。在 G 列,將會
輸入 Where Used 複合框裡所選的專案。H 列顯示 Percent (%) Used 文字框裡的數位,而 I 列和 J 列則
分別顯示提高調查人員的性別。
205
35.使用 Info Survey 應用程式
現在,你的應用程式已經準備好做最後測試了。
1. 切換到 Excel 介面,Info Survey 工作表,並且點擊 Survey 按鈕
2. 當表單出現時,選擇適當的選項,並點擊 OK
3. 啟動表單幾次,每次選擇不同的選項
4. 保存 Chap10.xls 變化
36.應用程式示例 2:學生和考試
近年來,許多 Windows 應用軟體越來越依賴於那些將各種控制群組合在一起的對話方塊。“選項”對話方塊
就是很好的例子,使用 Tab 頁,你可以給一個對話方塊裡的很多控制項提高設置。用 Tab 頁組合的對話方塊
非常容易和方便使用。當給更高級的 VBA 應用程式設計自訂表單時,你可以利用工具箱裡可用的
兩種特殊的 Tab 控制項:多頁控制項和 TabStrip 控制項。應用程式示例 2,本章剩下的主題,使用這些和其
它的高級控制項(例如 RefEdit 和 Calendar)來追蹤學生和他們的考試分數。
37.使用多頁和 TabStrip 控制項
圖 10-16 所示的自訂表單中間的物件是一個多頁控制項,它由兩頁組成。第一頁含有文字框和複合
框來收集學生資訊,例如社會保險號碼(SSN:Social Security Number),名和姓,學習年限和專
業。表單上部有一組選項按鈕,讓你明確學生狀況:New 或者 Active。如果該學生的資料還沒有輸
入到工作表中,那麼該學生的狀況就是 New。當表單第一次啟動時,New 選項按鈕就會自動被選上。
當你想要回顧或者更新已有學生的資料時,點擊 Active 選項按鈕將會顯示 RefEdit 控制項,用來使用
學生姓名填充清單方塊控制項,如同工作表中的一樣。
1. 在當前工程裡插入一個新使用者表單,並重命名為 Students
2. 將表單的 Caption 屬性改為 Students and Exams
3. 點擊工具箱上的多頁控制項,然後點擊表單的左上方並拖曳滑鼠到表單右下角
4. 按右鍵 Page1 頁,並且選擇快顯功能表上的“重命名”,在“題注”文字框裡面輸入 Students,
並在“快速鍵”區域輸入 S。用同樣的方法,重命名第二頁 Exams,以及輸入快速鍵 X
5. 點擊 Students 頁,使用工具箱上的控制項,在 Students 頁上添加如圖 10-16 所示的所有控制項。按照
下述指南:
1. 框架 Status 包含兩個選項按鈕,設置它們的 Caption 屬性為 New 和 Active,再將它們的 Name 屬性
設置為 optNew 和 optActive
2. 使用標籤標示的文字框,標籤的 Caption 屬性分別為 SSN,Last Name 和 First Name。更改文字框
的 Name 屬性為 txtSSN,txtLast 和 txtFirst
3. 標示複合框的標籤 Caption 屬性設置為 Year 和 Major。複合框的 Name 屬性設置為 cboxYear 和
cboxMajor。要讓使用者只能選擇一個選項的話,可以給每個複合框的下述屬性:MatchRequired
為 True,MatchEntry 為 1-fmMatchEntryComplete
4. 在 RefEdit 控制項上面放置標籤 Name Range。設置 RefEdit 控制項的 Name 屬性為 refNames
5. 設置清單方塊的 Name 屬性為 lboxStudents,設置 ColumnCount 屬性為 2,來顯示兩列資料
6. 設置命令按鈕的 Caption 屬性為 OK 和 Cancel,並且設置它們的 Name 屬性為 cmdOK 和 cmdCancel
206
圖 10-16 多頁控制項可以包含兩頁或者多頁,每頁顯示不同的控制項
多頁控制項的第二頁(圖 10-17)用來記錄和顯示與考試相關的資訊。該頁包含兩個物件。標籤控制項
臨時標題為“Last,First”,在運行的時候,該標籤將顯示前一頁選定學生的姓名。該頁上的第二
個控制項是 TabStrip 控制項,它包含了四個 tab 頁,是四門功課考試。儘管在本練習中,頁顯示在控制項
的上部,但是,VB 同樣也允許你將 tab 的方位設置在底部,左邊或者右邊。當你從一頁翻到另一頁
時,你可以看到這些相同的控制項,只是每個控制項顯示的資料改變了。
1. 點擊多頁控制項上的 Exams 頁
2. 點擊工具箱上的標籤控制項,並點擊 Exams 頁的左上角,拖曳滑鼠畫一個足夠放下人姓名的長方形
3. 在屬性視窗,設置該標籤的 Name 屬性為 lblWho,Caption 屬性為“Last, First”,設置 Font 屬性
為 Arial,Bold 和 14 磅
4. 點擊工具箱上的 TabStrip 控制項,在緊接標籤控制項的下麵點擊,並拖曳滑鼠直到達到你想要的大
小和形狀(參見圖 10-17)。預設地,TabStrip 控制項將會顯示兩頁:Tab1 和 Tab2。要添加新的 tab
頁,首先要點擊 TabStrip 控制項內部以選擇該物件,再次點擊,直到 TabStrip 控制項周圍的虛框顯
示為粗體(或者看到頁頭有虛線包圍),按一下右鍵,並選擇快顯功能表上的新建頁, VB 就會添加 Tab3,
使用相同的方法添加 Tab4。在每頁上按一下右鍵,並且選擇快顯功能表上的重命名,參見圖 10-17,
給每頁設置名稱。注意,每頁的名稱有一個帶底線,它允許使用者從鍵盤訪問該頁,例如按下
Alt+E 將啟動 English 頁。
5. 點擊 English 頁,並開始畫下述控制項:
• 如果工具箱上只有題為控制項的一頁的話,那麼回到本章的前面標題為“創建使用者表單的工
具”的部分,找到如何添加 Calendar 控制項到工具箱上。然後將 Calendar 控制項放置到 TabStrip
裡,如圖 10-17 所示。設置 Calendar 控制項的 ShowDateSelectors 屬性為 True。
• 添加 Enter/Change Grade 標籤和一複合框,設置複合框的 Name 屬性為 cboxGrade
• 添加兩個標籤,Caption 屬性設置為 Grade 和 Date,(另外兩個標籤的)Name 屬性設置為
lblGrade 和 lblDate。更改這兩個標籤的 SpecialEffect 屬性為 3-fmSpecialEffectEtched
技巧 10-6 設置 TabStrip 控制項
設置 TabStrip 控制項的最好方法是先添加它本身,然後放置其它的控制項在裡面。然而,如果已經有了
控制項在那裡,那麼你可以在它們上面畫 TabStrip 控制項,然後使用“移至底層”命令,將 TabStrip
控制項放到 Z-順序的底層。
圖 10-17 外面的框架包含一個多頁控制項,而裡面的框架則包含 TabStrip 控制項
Students and Exams 自訂表單允許你輸入新資料,或者顯示資料,就像在工作表裡一樣。圖 10-18
顯示了本表單使用的工作表。
1. 準備如圖 10-18 所示的工作表
2. 在工作表裡添加按鈕 Display Form,並且給他指定宏 DoStudents
3. 在 VB 編輯器螢幕,在當前工程裡添加新模組,並設置模組的 Name 屬性為 InfoStudents
4. 在 InfoStudents 模組裡輸入下述過程 DoStudents:
Sub DoStudents()
Students.Show
End Sub
207
圖 10-18 Students and Exams 應用程式的協助工作表
5. 返回到工作表,並點擊按鈕 Display Form,測試 DoStudents 過程
6. 點擊表單右上角的關閉按鈕,關閉表單
38.給表單 Students and Exams 自訂表單編寫 VBA 過程
自訂表單 Students and Exams 包含很多 VBA 過程,顯示在下面。這些過程的代碼必須輸入在表單
模組裡面,按兩下表單背景,啟動表單模組。
1. 從代碼視窗左上角的複合框裡選擇“(通用)”。右邊的過程選擇複合框應該顯示“(聲明)”,輸
入下述變數聲明:
'Declarations
Dim r As Integer
Dim nr As Integer
Dim indexPlus As Integer
Dim YesNo As Integer
2. 輸入過程 UserForm_Initialize 代碼,初始化表單
Private Sub UserForm_Initialize()
'select first page of the MultiPage control 選擇多頁控制項的第一頁
'page numbering begins from zero (0) 頁碼編號從 0 開始
Me.MultiPage1.Value = 0
'choose the New option button 選擇 New 選項按鈕
optNew.Value = True
'hide three controls on startup 啟動時先隱藏三個控制項
lblNames.Visible = False ‘前面應該將標籤 Name Range 命名為 lblNames(原文為
lblLast)
refNames.Visible = False
lboxStudents.Visible = False
'populate the Year combo box 填充 Year 複合框
With Me.cboxYear
.AddItem "1"
.AddItem "2"
.AddItem "3"
.AddItem "4"
End With
' populate the Major combo box 填充 Major 複合框
With Me.cboxMajor
.AddItem "English"
.AddItem "Chemistry"
.AddItem "Mathematics"
.AddItem "Linguistics"
.AddItem "Computer Science"
End With
' populate a combo box with grades 填充得分複合框
With Me.cboxGrade
208
.AddItem "A"
.AddItem "B"
.AddItem "C"
.AddItem "D"
.AddItem "F"
End With
'display date in the lblDate label control 在 lblDate 標籤裡顯示日期
Me.lblDate.Caption = Me.Calendar1.Value

'activate the first tab in the TabStrip control 啟動 TabStrip 控制項的第一頁


Me.TabStrip1.Value = 0
'activate the SSN text box 啟動 SSN 文字框
Me.txtSSN.SetFocus
End Sub
3. 輸入兩個過程來控制選項按鈕(optNew_Click 和 optActive_Click)
Private Sub optNew_Click()
lblNames.Visible = False
refNames.Visible = False
lboxStudents.Visible = False
Me.MultiPage1(1).Enabled = False
If lboxStudents.RowSource < > "" Then

Me.txtSSN.Text = ""
Me.txtLast.Text = ""
Me.txtFirst.Text = ""
Me.cboxYear.Text = ""
Me.cboxMajor.Text = ""
Me.txtSSN.SetFocus
End If
Me.txtSSN.SetFocus
End Sub
Private Sub optActive_Click()
lblNames.Visible = True
refNames.Visible = True
refNames.SetFocus
If lboxStudents.RowSource < > "" Then

lboxStudents.Visible = True
Call lboxStudents_Change
End If
End Sub
4. 輸入過程 lboxStudents_Change 和 refNames_Change 的代碼,這些控制 Students 頁上面的 RefEdit
和清單方塊控制項:
Private Sub lboxStudents_Change()
indexPlus = lboxStudents.ListIndex + 3

With ActiveWorkbook.Worksheets("Sheet2")

Me.txtSSN.Text = Range("A" & indexPlus).Value

Me.txtLast.Text = Range("B" & indexPlus).Value

Me.txtFirst.Text = Range("C" & indexPlus).Value

Me.cboxYear.Text = Range("D" & indexPlus).Value

Me.cboxMajor.Text = Range("E" & indexPlus).Value

Call TabStrip1_Change
Me.MultiPage1(1).Enabled = True
209
End With
End Sub
Private Sub refNames_Change()
lboxStudents.RowSource = refNames.Value

lboxStudents.ListIndex = 0
lboxStudents.Visible = True
Call lboxStudents_Change
End Sub
5. 輸入代碼來控制命令按鈕 OK(cmdOK_Click)和 Cancel(cmdCancel_Click):
Private Sub cmdOK_Click()
If Me.optNew.Value = True Then
Me.Hide
ActiveWorkbook.Sheets("Sheet2").Select

r = ActiveSheet.UsedRange.Rows.Count

nr = r + 1
Range("A" & nr).Value = Me.txtSSN.Text

Range("B" & nr).Value = Me.txtLast.Text

Range("C" & nr).Value = Me.txtFirst.Text

Range("D" & nr).Value = Me.cboxYear.Text

Range("E" & nr).Value = Me.cboxMajor.Text

Me.txtSSN.Text = ""
Me.txtLast.Text = ""
Me.txtFirst.Text = ""
Me.cboxYear.Text = ""
Me.cboxMajor.Text = ""
Me.txtSSN.SetFocus
'redisplay the form
Me.Show
Else
MsgBox "This control is currently unavailable."

End If
End Sub
Private Sub cmdCancel_Click()
Unload Me
Set Students = Nothing
End Sub
6. 輸入過程 cboxGrade_Click 來控制位於 Exams 頁上的 Grade 複合框:
Private Sub cboxGrade_Click()
YesNo = MsgBox("Enter the grade in the worksheet?", _

vbYesNo, "Modify Grade")


If YesNo = 6 Then
Me.lblGrade.Caption = cboxGrade.Value

Select Case TabStrip1.Value


Case 0
Range("F" & indexPlus).Value = Me.lblGrade.Caption

Case 1
Range("H" & indexPlus).Value = Me.lblGrade.Caption

Case 2
Range("J" & indexPlus).Value = Me.lblGrade.Caption

Case 3
210
Range("L" & indexPlus).Value = Me.lblGrade.Caption

End Select
cboxGrade.Value = ""
End If
End Sub
7. 輸入過程 Calendar1_Click,如下所示:
Private Sub Calendar1_Click()
YesNo = MsgBox("Enter the date in the worksheet?", vbYesNo, _

"Modify Date")
If YesNo = 6 Then
Me.lblDate.Caption = Calendar1.Value

Select Case TabStrip1.Value


Case 0
Range("G" & indexPlus).Value = Me.lblDate.Caption

Case 1
Range("I" & indexPlus).Value = Me.lblDate.Caption

Case 2
Range("K" & indexPlus).Value = Me.lblDate.Caption

Case 3
Range("M" & indexPlus).Value = Me.lblDate.Caption

End Select
End If
End Sub
8. 輸入過程 TabStrip1_Change 和 MultiPage1_Change,如下所示:
Private Sub TabStrip1_Change()
indexPlus = lboxStudents.ListIndex + 3

With ActiveWorkbook.Worksheets("Sheet2")

Select Case TabStrip1.Value


Case 0
' English
Me.lblGrade.Caption = Range("F" & indexPlus).Value

Me.lblDate.Caption = Range("G" & indexPlus).Value

Case 1
'French
Me.lblGrade.Caption = Range("H" & indexPlus).Value

Me.lblDate.Caption = Range("I" & indexPlus).Value

Case 2
'Math
Me.lblGrade.Caption = Range("J" & indexPlus).Value

Me.lblDate.Caption = Range("K" & indexPlus).Value

Case 3
'Physics
Me.lblGrade.Caption = Range("L" & indexPlus).Value

Me.lblDate.Caption = Range("M" & indexPlus).Value

End Select
End With
End Sub
Private Sub MultiPage1_Change()
Me.lblWho.Caption = Me.txtLast.Value & ", " _

& Me.txtFirst.Value
211
Call TabStrip1_Change
End Sub
39.使用自訂表單 Students and Exams
既然你已經準備好多有必須的 VBA 過程了,我們來看看該表單是如何對使用者操作反應的。
1. 切換到 Excel 視窗,並啟動 Sheet1
2. 點擊按鈕 Display Form
點擊 Display Form 按鈕運行過程 DoStudents,該過程顯示自訂表單 Students and Exams。在
表單出現在螢幕上之前,VB 執行過程 UserForm_Initialize 裡面的每條語句。結果顯示為圖
10-19。
圖 10-19 工作表裡的按鈕 Display Form 讓你快速訪問自訂表單 Students and Exams 來查看或者輸
入數據。當表單上載時,只有符合選定的選項按鈕的控制項才會顯示。
表單顯示後,你就可以輸入新學生並點擊 OK 將學生的資料轉移到工作表。當你點擊 OK 按鈕時,就會
執行 cmdOK_Click 過程。注意,你不能輸入新學生參加的考試,因為多頁控制項的第二頁(Exams)這
時不可用。一旦新學生的資料被寫入工作表,該表單就會重新顯示,你可以繼續輸入資料,或者你
可以點擊 Cancel,將表單從螢幕上清除。當你點擊 Cancel 按鈕時,就會執行過程 cmdCancel_Click。
3. 使用自訂表單 Students and Exams 輸入兩位元新學生的資料。任何時候,你都可以點擊 Active
選項按鈕,從已有學生中上傳資料。當你點擊 Active 選項按鈕時,標籤 Name Range 和 RefEdit
控制項就變為可見的了。
4. 點擊 Active 選項按鈕,然後點擊 RefEdit 控制項的減號按鈕
5. 選擇工作表裡的姓名區域,如圖 10-20 所示。
圖 10-20 使用 RefEdit 控制項,你可以選擇包含你想要使用資料的儲存格區域
使用 RefEdit 控制項,你可以選擇工作表裡的儲存格區域,在本練習裡,選擇了包含學生姓和名的單
元格。選擇有效資料很重要,開始點擊 Last Name 列標下面的儲存格(B3)並且向下向右拖曳滑鼠
以至包括學生的名
212
注意,當你使用 RefEdit 控制項時,表單臨時被隱藏。你所選擇的儲存格出現在 RefEdit 控制項上。點擊
RefEdit 控制項的減號按鈕返回表單。當你返回表單時,過程 refNames_Change 正在運行,該過程使用
儲存格區域位址, 將學生姓名填充到清單方塊控制項裡。該過程的最後一條語句調

lboxStudents_Change 過程,確保表單的文字框和複合框與清單方塊裡所選學生姓名同步。清單方塊顯
示已有學生的姓名,被選學生的資料顯示在左邊的文字框和複合框裡面(參見圖 10-21)
圖 10-21 表單上的清單方塊是通過 RefEdit 控制項將儲存在工作表裡的資料填充的
6. 點擊清單方塊裡的任意姓名,並查看該學生的資料
7. 點擊清單方塊裡的任意姓名,然後點擊 Exams 頁。
Exams 也顯示了被選學生(參見圖 10-22)的姓名。TabStrip 控制項顯示考試科目,如果被選學生參加
過任何考試,當你點擊適當科目頁時,考試的日期和分數就會顯示出來。
圖 10-22 Exams 頁顯示被選學生和科目的考試日期和分數
你可以通過提供的複合框和日曆控制項,輸入或者更改學生的分數和考試日期。VB 將問你是否修改數
據(回顧 Calendar1_Click 和 cboxGrade_Click 過程的 VBA 代碼)在你以確定回應對話方塊後,所選的
資料或者分數就會寫入到工作表中相應的列(參見圖 10-23)
TabStrip1_Change 過程確保你點擊科目頁的時候,VB 會從適當的工作表儲存格顯示考試的分數和考
試日期。MultiPage1_Change 過程則確保當你點擊 Exams 頁時,lblWho 標籤顯示當前清單方塊裡選項學
生的姓和名。
213
圖 10-23 工作表 F 到 M 列裡的資料是通過使用者表單 Students and Exams 上 Exams 頁輸入的
40.接下來……
既然你到了這個相對比較長的章節的結尾,那麼你已經有了必要的技巧來設計有用的表單。我們來
簡單總結一下你在本章學習的內容。內置對話方塊可以從你自己的 VBA 過程裡顯示,對於需要使用者輸
入的自訂 VBA 應用程式,就需要創建一個自訂表單。通過設置 tab 鍵順序,確保使用者可以表單上
按邏輯順序移動。在表單模組裡面編寫 VBA 過程,讓表單對使用者操作作出反應。通過使用屬性視窗
或者編寫 UserForm_Initialize 過程,給控制項設置初始值。確保有過程將資料轉移到工作表。在下
章裡,當你開發集合和自訂物件主題的時候,將獲得更多於自訂表單實用的經驗。
第十一章自訂集合和類別模組
在第九章,你學習了如果通過使用自動控制(Automation)控制另一個應用程式的物件,回想一下,
在創建了對 Microsoft Word 10.0 Object Library 的引用之後,你就能夠控制 Word 應用程式,調用
其物件、屬性和方法。你也學習了如何使用自動控制從 Microsoft Outlook 獲取聯繫位址。有個好
消息,那就是,你不必局限於使用 Excel 的內置物件或者其它應用程式的物件,VBA 允許你創建你自
己的物件和物件集合,以及它們完整的方法和屬性。在本章,你將學習如何使用集合,包括如何聲
明自訂集合物件。你也將學習如何使用類別模組來創建使用者定義的物件。
在跳入這些理論和實用的實例之前,我們來過一下一些本章將用到的術語吧:
集合(Collection)—— 一個包含一組相關物件的物件
類(Class)—— 物件的定義,包含其名稱、屬性、方法和事件。類充作一種物件模版,在允許的
時候,由此創建對象示例。
示例(Instance)—— 術語類的一種特定物件,稱為類的示例。當你創建一個示例的時候,你也
就創建了一個新物件,它擁有類定義的屬性和方法。
類別模組(Class Module)—— 包含類定義的模組,包括它的屬性和方法定義
模組——模組含有 Sub(子過程)和 Function(函數)過程,可為其它 VBA 過程使用,並且和任何對
象沒有特別的關係。
表單模組——包含給由使用者表單或者其控制項引發的事件程序使用的 VBA 代碼。表單模組是一種類模
塊。
事件—— 一種可以為物件識別的物件,例如滑鼠點擊或者按鍵,你可以為其定義應對操作。事件
可以由使用者操作,或者 VBA 語句或者系統引發。
事件程序—— 一個可以自動執行的過程,是對使用者引發的事件或者系統引發的程式碼的反應。
1.使用集合
一組相類似的物件成為集合。例如,在 Excel 裡,所有打開了的工作簿屬於 Workbooks 集合,而某個
具體工作簿裡面的所有工作表都是 Worksheets 集合裡面的成員。在 Word 裡,所有打開的文檔都屬於
Documents 集合,一個文檔裡的每個段落都是 Paragraphs 集合的成員。集合是包含其它物件的物件。
無論你想要使用什麼集合,你都可以做下述事情:
• 使用索引值可以引用集合裡的特定物件,例如,要指向 Worksheets 集合裡的第二個物件的話,
那麼使用下述語句的任意一條:
Worksheets(2).Select
或者
214
Worksheets("Sheet2").Select
• 使用 Count 屬性可以知道集合裡的成員數目,例如,當你在立即視窗裡輸入語句:
?Worksheets.Count
VBA 將會返回當前工作簿裡的工作表總數
• 使用 Add 方法可以在集合裡插入新的專案,例如,當你在立即視窗裡輸入語句:
Worksheets.Add
VBA 將會在當前工作簿裡面插入一個新的工作表,這時,Worksheets 集合裡多了一個成員。
• 使用 For Each…Next 迴圈可以遍歷集合裡的每個物件。假設你打開了一個工作簿,包含五個工
作表,它們的名稱為:
“Daily wages”, “Weekly wages”, “Bonuses”, “Yearly salary”和“Monthly
wages”。
使用下述過程將名稱裡包含“wages”的工作表刪除:
Sub DeleteSheets()
Dim ws As Worksheet
Application.DisplayAlerts = False
For Each ws In Worksheets
If InStr(ws.Name, "wages") Then
ws.Delete
End If
Next
End Sub
當你編寫你自己的 VBA 過程時,你可能會碰到這樣一種情況,那就是,沒有方便的內置集合來處理
你的任務。解決辦法就是創建自訂集合。從第七章,你就已經知道如何通過動態或靜態陣列來實
用多個資料。因為,集合有允許你添加、移動和計算其成員的內置屬性和方法,所以使用集合比使
用陣列容易得多。
2.聲明自訂集合
要創建一個使用者定義的集合的話,你應該先聲明一個 Collection 類型的物件變數,該變數在 Dim 語
句裡和關鍵字 New 一起聲明,如下所示:
Dim 集合名稱 As New Collection
3.給自訂集合添加物件
在聲明 Collection 物件後,你就可以使用 Add 方法往集合裡插入新成員了。用來組成該集合的物件
不必需要是同樣的資料類型。Add 方法如下所示:
object.Add item, key, before, after

你只需要明確物件和成員,object 是集合名稱,它是使用在 Collection 物件聲明中的相同名稱;item


是你要添加到集合裡的物件。儘管其它的參數是可選的,但是它們也很有用。集合裡的成員自動從
1 開始分配號碼,瞭解這個很重要。然而,它們也可以給分配一個獨特的鍵。除了通過索引號(1,
2,3 等等)訪問某個特定的成員之外,你也可以在集合添加物件的時候給該物件分配一個鍵。例如,
如果你創建一個自訂工作表集合,那麼你應該使用工作表名稱作為鍵;要鑒別學生或者員工集合
裡的單個人員的話,你就可以使用社會保險號碼(SSN)作為他們的鍵。
如果你想要確定物件在集合裡面的位置時,那麼你就應該使用 before 或者 after 參數(不要同時使
用它們)。參數 before 在此之前添加新物件的物件,而參數 after 是一個物件,在它之後添加新的對
象。
接下來過程 GetComments 聲明了一個叫做 colNotes 的自訂集合,該過程提示輸入作者姓名,然後
在活動工作簿裡遍歷所有的工作表,以找到該作者的批註。只有某個特定作者輸入的批註會加入自
定義集合。過程給第一個批註分配一個鍵,然後將剩餘的批註添加到集合裡,每次都將它們置於最
後添加的批註之前(注意 before 參數的使用)。如果集合至少有一個批註,那麼過程會在一個資訊
框顯示由參數 key 確定的批註內容。注意,參數 key 如何用來引用集合裡的成員。然後,過程將集合
裡的所有批註列印到立即視窗。文本函數(Mid 和 Len)用來僅獲取批註內容,而排除作者姓名。接
著,返回工作簿裡的批註數目和自訂集合裡的批註數目到 Count 屬性。在試驗過程 GetComments
之前,我們先得按照以下步驟創建一個工作簿:
215
1. 打開一個新工作簿並保存為 Chap11.xls
2. 在工作表 Sheet1 的任意儲存格上按一下右鍵,並從快顯功能表上選擇插入批註,隨意輸入一些文本。
在批註框之外的任意地方點擊一下,退出批註編輯模式。使用相同的技巧在工作表 Sheet2 再插
入兩個批註,給每個批註輸入不同的文本。在當前工作簿裡插入新工作表(Sheet4),並插入一
個批註。現在你應該在三個工作表裡一共有四個批註。
3. 選擇“工具”|“選項”並且點擊“常規”頁,用戶名文字方塊裡面應該顯示的是你的名字,刪除
你自己的名字,並輸入 Joan Simth,並點擊確定。現在,在工作表 Sheet2 和 Sheet4 的任意地方
各輸入一個批註。這些批註應該會自動地印上 Joan Smith 的名字。當你輸入完批註後,回到選
項對話方塊,並將常規頁上的用戶名改回你自己的名字。
4. 切換到 VB 編輯器,並將 VBA 工程重命名為 ObjColClass
5. 在當前工程裡添加一新模組,並重命名為 MyCollection
6. 輸入過程 GetComments,顯示如下:
Sub GetComments()
Dim sht As Worksheet
Dim colNotes As New Collection
Dim myNote As Comment
Dim I As Integer
Dim t As Integer
Dim fullName As String
fullName = InputBox("Enter author's full name:")

For Each sht In ThisWorkbook.Worksheets

sht.Select
I = ActiveSheet.Comments.Count
For Each myNote In ActiveSheet.Comments

If myNote.Author = fullName Then


MsgBox myNote.Text
If colNotes.Count = 0 Then
colNotes.Add Item:=myNote, key:="first"

Else
colNotes.Add Item:=myNote, Before:=1

End If
End If
Next
t=t+I
Next
If colNotes.Count <> 0 Then MsgBox colNotes("first").Text

MsgBox "Total comments in workbook: " & t & Chr(13) & _

"Total comments in collection:" & colNotes.Count

Debug.Print "Comments by " & fullName

For Each myNote In colNotes


Debug.Print Mid(myNote.Text, Len(myNote.Author) + 2, _

Len(myNote.Text))
Next
End Sub
7. 運行過程 GetComments 並且查看結果
4.從自訂集合移出物件
從自訂集合裡移出成員和添加成員一樣簡單。使用 Remove 方法可以移出物件,如下所示:
object.Remove item
object 是自訂集合的名稱,含有你要移出的物件;item 是你要從集合移出的物件。
216
我們來修改你在前面部分準備的過程 GetComments,示範從集合裡移出成員。在該過程結尾,我們
將當前集合 colNotes 裡成員的內容一個一個地顯示出來,並且詢問使用者,使用應該將該成員從該集
合移出。
1. 將下面的聲明加入到過程 GetComments 的聲明部分:
Dim response
Dim myId As Integer
第一條語句聲明了一個叫做 response 的變數,你將使用該變數來儲存 Msgbox 函數的結果。第二
條浴巾聲明變數 myld 來儲存集合物件的索引號。
2. 定位過程 GetComments 的下述語句:
For Each myNote In colNotes
在上面的語句之後,添加下述語句:
myId = 1
3. 定位到過程 GetComments 的下述語句:
Debug.Print Mid(myNote.Text, Len(myNote.Author) + 2, _

Len(myNote.Text))
在該語句後,輸入下面的代碼塊:
response = MsgBox("Remove this comment?" & Chr(13) _

& Chr(13) & myNote.Text, vbYesNo + vbQuestion)

If response = 6 Then
colNotes.Remove Index:=myId
Else
myId = myId + 1
End If
4. 在該過程輸入下述語句:
Debug.Print "The following comments remain in the collection:"

For Each myNote in colNotes


Debug.Print Mid(myNote.Text, Len(myNote.Author) + 2, _

Len(myNote.Text))
Next
5. 運行過程 GetComments,並且將顯示在資訊框裡批註之一刪除。
修改後的過程 GetComments2 可以在附帶的 CD 裡找到。該過程將指定的批註從自訂集合裡清除,但
是不會從工作表裡刪除批註。
技巧 11-1 給集合重新編號
當移出物件後,集合回自動重新編號,因此,你可以使用 1 作為 Index 參數,來刪除自訂集合裡的
所有物件,如下所示:
Do While myCollection.Count >0
myCollection.Remove Index:=1
Loop
插入:模組還是類別模組?
在 VB 編輯器的插入功能表裡有兩個模組命令:模組和類別模組。這些在本章的開頭有定義。到目前為止,
你已經使用過標準模組來創建 Sub 和 Function 過程。你將第一次在本章使用類別模組來創建自訂對
象並且定義它的屬性和方法。
5.創建自訂對象
創建一個新的,非標準的 VBA 物件,需要在你的工程裡插入類別模組並且在模組裡添加代碼。然而,
在做此之前,你需要對類是什麼要有一個基本的瞭解。
如果你回頭看一下本章的開頭,那麼你就會知道類從某種意義上說是物件範本。一個經常使用的類
比就是將類比作一個曲奇剪切機。就像曲奇剪切機決定某個曲奇做成什麼樣一樣,類的定義決定某
個物件應該是什麼樣以及如何做。在實際使用一個物件類之前,你必須先創建一個類的新示例。對
象示例就是這些曲奇。每個示例都有其類定義的特點(屬性和方法),正如你可以使用相同的區旗
217
剪切機制作許多曲奇一樣,你可以創建多個類的示例。你可以獨立改變相同類中的任意示例中的類
的每個示例的屬性。
類別模組允許你定義你自己的自訂類,連同自訂屬性和方法。回憶一下,屬性是定義物件某個特
點的品質,例如外形、位置、顏色、標題等等。方法是物件可以執行的操作。通過在類別模組裡面編
寫屬性過程,你可以給你的自訂物件創建屬性。物件的方法也可以在類別模組裡面通過編寫子過程
或者函數過程來創建。在類別模組裡創建完你的物件後,你可以象使用其它的內置物件一樣使用它。
你也可以將物件類匯出 VBA 工程給其它能使用 VBA 的應用程式使用。
6.創建類
本章的剩餘部分將示範創建和使用一個叫做 CEmployee 的自訂物件的過程。該物件將代表一個員
工,該 CEmployee 物件將具有屬性例如:Id,FirstName,LastName 和 Salary。它也有一個方法,可
以修改當前薪水。
1. 在工程流覽器視窗選擇 ObjColClass (Chap11.xls),並選擇“插入”|“類別模組”
2. 選擇工程流覽器裡的類別模組,並使用屬性視窗將其重命名為 CEmployee。
技巧 11-2 給類別模組命名
每次創建新類別模組的時候,請給個它一個有意義的名稱。將類別模組命名為你想要在你的 VBA 過程使
用的名稱。你給你的類選擇的名稱應該易於理解並且明確物件類要代表的東西。作為一個規則,對
象類名稱一般前面有一個大寫字母“C”。
7.變數聲明
在添加和重命名類別模組後,下一步就是聲明變數,用來存儲你要存儲在該物件裡資料。你想要存儲
與物件的每個資料都得分配一個變數。類的變數被稱為資料成員,並且使用關鍵字 Private 聲明。
該關鍵字確保這些變數只在該類別模組裡面可用。使用關鍵字 Private 代替常用的 Dim 語句,隱藏該數
據成員並且避免應用程式的其它部分引用它。只有定義這些變數的類別模組中的過程才可以修改這些
變數的值。
因為這些變數的名稱也充作屬性名稱,所以要給你的物件資料成員使用有意義的名稱。傳統上使用
m_作為變數名稱的首碼,以表明它們是類的資料成員。
1. 在類別模組 CEmployee 的頂端輸入下述聲明:
Option Explicit
'declarations
Private m_LastName As String
Private m_FirstName As String
Private m_Salary As Currency
Private m_Id As String
注意,每個資料成員變數的名稱都以首碼“m_”
8.定義類的屬性
使用關鍵字 Private 聲明變數保證變數不能從該物件以外直接訪問,意思是說,該類別模組以外的 VBA
過程不能設置或者讀取存儲與這些變數裡的資料。要讓你 VBA 應用程式的其它部分也能夠設置或者
讀取員工資料的話,你就必須在 CEmployee 類別模組裡面添加特殊的屬性過程。這裡有三種屬性過程:
Property Let——該過程允許應用程式的其它部分設置屬性值。
Property Get——該過程允許應用程式的其它部分獲得或讀取屬性值。
Property Set——當設置對物件引用時,該過程用來代替 Property Let。
屬性過程在一個物件屬性需要設置或者讀取的時候被執行。Property Get 過程可以和 Property Let
過程擁有一樣的名稱。你應該給物件的每個可能被 VBA 應用程式其它部分訪問的屬性創建屬性過程。
三種屬性語句中最容易理解的是 Property Get 過程,我們通過仔細看一下 Property Get LastName
過程來檢查一下屬性過程的語法。
屬性過程包含下屬部分:
過程聲明行明確屬性名稱和資料類型:
Property Get LastName ( ) As String

LastName 是屬性名稱,As String 決定該屬性返回值的資料類型


218
設定陳述式,類似於我們在函數過程裡使用的:
LastName = m_LastName
LastName 是屬性名稱,而 m_LastName 是資料成員變數,它存儲著你想要獲取或者設置的屬性
值。m_LastName 變數應該在類別模組的頂端使用關鍵字 Private 定義好了。
如果獲取的資料是一個計算的結果,那麼你可以包含適當的 VBA 語句:
Property Get Royalty()
Royalty = (Sales * Percent)-Advance

End Property
End Property 關鍵字表明屬性過程結束
技巧 11-3 從屬性過程立即退出
正如關鍵字 Exit Sub 和 Exit Function 允許你提前退出子過程或者函數過程一樣,Exit Property
關鍵字同樣也給了立即退出屬性過程的方法。程式執行將會依照 Property Get,Property Let 或者
Property Set 過程語句繼續下去(譯者,原文不可理解,可能有誤)
9.創建 Property Get 過程
CEmployee 類物件有四個屬性需要給當前 VBA 工程裡的其它模組的 VBA 程式使用,使用物件 CEmployee
時,你肯定想要獲得員工的 ID,姓和名以及當前薪水的資訊。
1. 在類別模組 CEmployee 裡輸入下述 Property Get 過程,緊接著聲明部分輸入:
Property Get Id( ) As String
Id = m_Id
End Property
Property Get LastName( ) As String
LastName = m_LastName
End Property
Property Get FirstName( ) As String

FirstName = m_FirstName
End Property
Property Get Salary( ) As Currency
Salary = m_Salary
End Property
每種員工必須的資訊要求一個單獨的 Property Get 過程,上面的每個 Property Get 過程返回當前屬
性的值。注意,Property Get 過程和函數過程非常類似。就像函數過程,Property Get 過程包含一
個設定陳述式。回憶一下第四章的內容,要從函數過程返回值的話,你就必須將該值賦予該函數名稱。
10.創建 Property Let 過程
除了使用 Property Get 過程來獲取存儲在資料成員(私有變數)裡的資料之外,你必須準備相應的
Property Let 過程,以允許其它過程在必要的時候可以更改這些變數的值。然而,如果存儲在私有
變數的某個資料就是唯讀的話,那麼你就不必準備 Property Let 過程了。假設你不希望用戶更改員
工 ID,你只要不給它寫 Property Let 過程,就可以使它為唯讀的了。因此類別模組 CEmployee 將只有
三個屬性(LastName,First- Name 和 Salary),每個屬性需要一個單獨的 Property Let 過程。
1. 在類別模組 CEmployee 裡輸入下述 Property Let 過程:
Property Let LastName(L As String)
m_LastName = L
End Property
Property Let FirstName(F As String)

m_FirstName = F
End Property
Property Let Salary(ByVal dollar As Currency)

m_Salary = dollar
End Property
Property Let 過程至少需要一個參數來明確你想要賦予該屬性的數值,該參數可以按值傳遞(參見
219
Property Let Salary 過程裡的關鍵字 ByVal)或者按引用傳遞(ByRef 是默認的)。如果你需要重新
熟悉一下這些關鍵字的意思的話,那麼參加第四章裡的“按值或者按引用傳遞參數”部分。傳遞給
Property Let 過程的參數資料類型必須和同名稱的 Property Get 或者 Set 過程返回值的資料類型一
致。注意,Property Let 過程擁有前面部分準備的 Property Get 過程相同的名稱。忽略了 ID 屬性的
Property Let 過程,你將創建一個唯讀的 ID 屬性,只能讀取,不能設置。
技巧 11-4 定義屬性過程的範圍
你可以將關鍵字 Public,Private 或 Static 放在屬性過程名稱的前面來確定它的範圍。例如:
要創建一個可以從所有模組的過程裡訪問的 Property Get 過程的話,那麼依照下屬語句格式:
Public Property Get FirstName( ) As String

要讓 Property Get 過程只能從聲明它的模組中其它過程訪問的話,那麼使用下述語句格式:


Private Property Get FirstName( ) As String

在程序呼叫中,要保護 Property Get 過程的當地變數的話,那麼可以使用下述語句格式:


Static Property Get FirstName( ) As String

如果沒有明確使用 Public 或者 Private,熟悉過程默認上就會使公共的,同樣,如果沒有使用關鍵


字 Static, 那麼當地變數在過程的調用中就不會被保護。
11.創建類方法
除了屬性之外,物件通常還一個或者多個方法。方法是該物件可以執行的操作。方法允許你操作存
儲與類物件裡的資料。方法可以使用自過程或者函數過程創建。要讓方法在類別模組之外可用的話,
那麼需要在自過程或函數程序定義前帶關鍵字 Public。你在本章創建的物件 CEmployee 有一個方法,
讓你計算新薪水。假設員工的薪水可以按照一個確定的百分比或值來增加或者減少。
1. 在類別模組裡輸入下述函數過程 CalcNewSalary:
Public Function CalcNewSalary(choice As Integer, _

curSalary As Currency, amount As Long) As Currency

Select Case choice


Case 1
' by percent
CalcNewSalary =curSalary +((curSalary + amount)/100)

Case 2
' by amount
CalcNewSalary = curSalary + amount
End Select
End Function
函數 CalcNewSalary 在類別模組裡使用關鍵字 Public 一起定義,作為類 CEmployee 的一個方法。要計算
一個新的薪水的話,類別模組之外的 VBA 過程必須提供三個參數:choice,CurSalary 和 amount。參數
choice 明確計算類型,假設你想要按 5 個百分點或者 5 美元增加員工的薪水,選擇 1 將按 5 個百分點增
加薪水,而選擇 2 將當前的薪水基礎上增加 5 美元。參數 CurSalary 是員工的當前薪水數字,而 amount
決定了薪水改變量。
技巧 11-5 關於類方法
只有在類別模組之外可以訪問的方法應該聲明為 Public,所有其它的應該為 Private
方法執行一些類包含的資料的操作
如果方法需要返回數值的話,那麼就編寫一個函數過程,否則創建一個子過程
12.創建類的示例
在類裡輸入完所有必須的 Property Get,Property Let,自過程和函數過程後,你可以創建一個類
的信示例了,稱為對象。在你能夠創建物件之前,物件變數必須在一個標準模組裡聲明好,以存儲
對該對象的引用。如果該類別模組的名稱為 CEmployee 的話,該類的新示例就可以使用下述語句創建:
Dim emp As New CEmployee
變數 emp 代表隊類 CEmployee 的一個物件的引用。當你使用關鍵字 New 聲明物件變數的時候,VBA 就會
創建該物件並分配記憶體給它;然而,該對象並沒有獲得示例,直到你在你的程式碼裡通過賦值給
它的屬性或者執行它的方法創建對該物件的引用。你同樣可以通過聲明一個物件變數為類定義的數
220
據類型來創建該物件的一個示例。例如:
Dim emp As CEmployee
Set emp = New CEmployee
如果你不使用關鍵字 New 在 Dim 語句裡(如上所示),那麼 VBA 就不會份哦記憶體給你的自訂物件,直
到你的程式真正需要它。
13.類別模組裡的事件程序
事件基本上是一個物件可以識別的操作。自訂類只可識別兩種事件:Initialize 和 Terminate。
這兩個事件分別在該類的示例創建和消滅的時候引發。
Initialize 事件在從類創建物件的時候產生(參見前面部分關於“創建類的示例”)。在類 CEmployee
例子裡面,Initialize 事件在你代碼裡第一次使用變數 emp 的時候也會引發。因為 Initialize 事件
裡的語句將是該物件第一個要執行的,在任何屬性被賦值之前,也在任何方法被執行之前,所以
Initialize 事件是一個執行類創建的物件的初始化最好的地方。回憶一下,在類別模組 CEmployee 裡,
ID 是唯讀的,你可以使用 Initialize 事件給 m_Id 變數賦予一個單獨的五位數。
1. 在類別模組 CEmployee 裡輸入下述過程 Class_Initialize:
Private Sub Class_Initialize()
Randomize
m_Id = Int((99999 - 10000) * Rnd + 10000)

End Sub
Class_Initialize 過程給變數 m_Id 賦予一個獨特的五位元數,初始化物件 CEmployee。使用下述公式,
可以產生一個介於起始值 10000 和結束值 99999 之間的亂數:
=Int((結束值–起始值)*Rnd +起始值)
Class_Initialize 過程也使用了 Randomize 語句來初始化亂數發生器。可以搜索線上說明,獲得
更多關於使用 Rnd 和 Int 函數,以及 Randomize 語句的資訊。
Terminate 事件發生在釋放該物件的引用時。這是一個執行任何必要的清理任務的好地方。
Class_Terminate 過程使用下述語法:
Private Sub Class_Terminate()
[你的清理代碼]
End Sub
使用下述語法,將物件變數從物件上釋放出來:
Set objectVariable = Nothing
當你設置物件變數為 Nothing 的時候,Terminate 事件就發生了,屆時,任何位於該事件裡的代碼就
會被執行。
14.創建使用者介面
如果你跳過了前面的章節的話,那麼你可能得返回去,因為,執行你的自訂物件 CEmployee 需要
你設計一個自訂表單。
1. 選中當前 VBA 工程,並且選擇“插入”|“使用者表單”
2. 按照圖 11-1 所示準備好該表單:
221
圖 11-1 本表單示範了自訂物件 CEmployee 的使用
3. 給表單和它的控制項設置下述屬性:
物件屬性設置
表單
標籤 1
標籤 Last Name 下面的文字框
標籤 2
標籤 First Name 下面的文字框
標籤 3
標籤 Salary 下面的文字框
框架 1
框架 Salary Modification 下麵的
文字框
選項按鈕 1
選項按鈕 2
框架 2
選項按鈕 3
選項按鈕 4
清單方塊
命令按鈕 1
命令按鈕 2
命令按鈕 3
命令按鈕 4
Name
Caption
Caption
Name
Caption
Name
Caption
Name
Caption
Name
Name
Caption
Name
Caption
Caption
Name
Caption
Name
Caption
Name
Height
Width
Name
Caption
Name
Caption
Name
Caption
Name
Caption
222
Salaries
Employees and Salaries
Last Name
txtLastName
First Name
txtFirstName
Salary
txtSalary
Salary Modification
txtRaise
optPercent
Percent (%)
optAmount
Amount ($)
Change the Salary for
optHighlighted
Highlighted Employee
optAll
All Employees
lboxPeople
91.45
180.75
cmdSave
Save
cmdClose
Close
cmdUpdate
Update Salary
cmdDelete
Delete Employee
命令按鈕 5 Name
Caption
cmdEmployeeList
Update List
4. 準備一個資料登錄工作表,如圖 11-2 所示:
圖 11-2 在表單 Employees and Salaries 上輸入的資料將會轉移到該工作表
5. 切換到 VB 編輯器視窗,按兩下表單背景以啟動表單模組
6. 在表單模組代碼視窗上部輸入下述聲明:
Option Explicit
Dim emp As New CEmployee
Dim CEmployees As New Collection
Dim index As Integer
Dim ws As Worksheet
Dim extract As String
Dim cell As Range
Dim lastRow As Integer
Dim empLoc As Integer
Dim startRow As Integer
Dim endRow As Integer
Dim choice As Integer
Dim amount As Long
第一條語句聲明變數 emp 為類 CEmployee 的一個新示例,第二條語句聲明了一個自訂集合,
集合 CEmployees 將會用來存儲員工資料。這裡聲明的其它變數將會用於表單上各種控制項的 VBA
過程裡面。
7. 輸入下述 UserForm_Initialize 過程來啟動或者禁止表單上的控制項:
Private Sub UserForm_Initialize()
txtLastName.SetFocus
cmdEmployeeList.Visible = False
lboxPeople.Enabled = False
Frame1.Enabled = False
txtRaise.Value = ""
optPercent.Value = False
optAmount.Value = False
txtRaise.Enabled = False
optPercent.Enabled = False
optAmount.Enabled = False
Frame2.Enabled = False
optHighlighted.Enabled = False
optAll.Enabled = False
223
cmdUpdate.Enabled = False
cmdDelete.Enabled = False
End Sub
當表單啟動時,UserForm_Initialize 過程內部的語句只會啟動需要的控制項(見圖 11-3)。
圖 11-3 當表單第一次啟動時,UserForm_Initialize 過程禁用某些控制項
8. 輸入下述過程 cmdSave_Click 將輸入在表單的資料轉移到工作表:
Private Sub cmdSave_Click()
If txtLastName.Value = "" Or txtFirstName.Value = "" Or _

txtSalary.Value = "" Then


MsgBox "Enter Last Name, First Name and Salary."

txtLastName.SetFocus
Exit Sub
End If
If Not IsNumeric(txtSalary) Then
MsgBox "You must enter a value for the Salary."

txtSalary.SetFocus
Exit Sub
End If
If txtSalary < 0 Then
MsgBox "Salary cannot be a negative number."

Exit Sub
End If
Worksheets("Salaries").Select
index = ActiveSheet.UsedRange.Rows.Count + 1

lboxPeople.Enabled = True
'set and enter data into the CEmployees collection

With emp
Cells(index, 1).Formula = emp.Id
.LastName = txtLastName
Cells(index, 2).Formula = emp.LastName

.FirstName = txtFirstName
Cells(index, 3).Formula = emp.FirstName

.Salary = CCur(txtSalary)
If .Salary = 0 Then Exit Sub
Cells(index, 4).Formula = emp.Salary

CEmployees.Add emp
End With
224
‘delete data from text boxes
txtLastName = ""
txtFirstName = ""
txtSalary = ""
‘enable hidden controls
cmdEmployeeList.Value = True
cmdEmployeeList.Visible = True
cmdUpdate.Enabled = True
cmdDelete.Enabled = True
Frame1.Enabled = True
txtRaise.Enabled = True
optPercent.Enabled = True
optAmount.Enabled = True
Frame2.Enabled = True
optHighlighted.Enabled = True
optAll.Enabled = True
txtLastName.SetFocus
End Sub
cmdSave_Click 過程以驗證用戶的姓、名和薪水文字框開始,如果使用者輸入了正確的資料,VBA
將當前工作表裡的第一空白行賦予變數 Index。下一條語句啟動表單的清單方塊控制項。
當程式到達 With emp 結構時,類 CEmployee 的一個新示例便產生了。屬性 LastName,FirstName
和 Salary 的設置基於相應文字框裡輸入的資料,而 ID 屬性則是由 Class_Initialize 事件程序
裡的亂數語句產生的數位設置的。VBA 每次看到對物件 emp 的引用時,它就會調用位於類模
塊裡的適當 Property Let 過程。
本章的最後部分示範如何一步一步地跟蹤過程的運行,準確地查看什麼時候運行屬性過程。
設置完物件的屬性值後,VBA 將員工資料轉移到工作表裡。With emp 結構裡面的最後一條語句
將使用者定義的物件 emp 添加到一個叫做 CEmployee 的自訂集合。
接著,VB 將表單文字框裡的輸入清除並且啟動開始在 UserForm_Initialize 過程裡關閉的命令
按鈕。注意,本代碼塊的第一條指令:cmdEmployeeList.Value = True,該語句導致自動執
行 cmdEmployeeList_Click 過程,該過程附加于按鈕 Update List(順便說一下,這是唯一用
戶從未見到的控制項)。該過程的代碼如下所示。
9. 輸入 cmdEmployeeList_Click 過程,如下所示:
Private Sub cmdEmployeeList_Click()

lboxPeople.Clear
For Each emp In CEmployees
lboxPeople.AddItem emp.Id & ", " & _
emp.LastName & ", " & emp.FirstName & ", $" & _

Format(emp.Salary, "0.00")
Next emp
End Sub
cmdEmployeeList_Click 過程附加在命令按鈕 Update List 之上,該按鈕由 cmdSave_Click 過程
控制,並且導致新的員工資料添加到清單方塊控制項裡。cmdEmployeeList_Click 過程以清除列表
框的內容開始,然後用自訂集合 CEmployees 的成員來填充清單方塊。
225
圖 11-4 清單方塊控制項顯示員工資料,正如在自訂集合輸入的一樣
10. 輸入下述過程 cmdClose_Click:
Private Sub cmdClose_Click()
Unload Me
End Sub
cmdClose_Click 過程讓你將使用者表單從螢幕上清除,並結束使用員工的自訂集合。當你再
次運行表單,你輸入的員工將會成為新集合 CEmployees 的成員。
11. 輸入下述過程 cmdDelete_Click:
Private Sub cmdDelete_Click()
' make sure that an employee is highlighted in the

' list control


If lboxPeople.ListIndex > -1 Then
MsgBox "Selected item number: " & lboxPeople.ListIndex

extract = CEmployees.Item(lboxPeople.ListIndex + 1).Id

MsgBox extract
Call FindId
MsgBox empLoc
Range("A" & empLoc).Delete (3)
MsgBox "There are " & CEmployees.Count & _

" items in the CEmployees collection. "

CEmployees.Remove lboxPeople.ListIndex + 1

MsgBox "The CEmployees collection has now " & _

CEmployees.Count & " items."


cmdEmployeeList.Value = True
If CEmployees.Count = 0 Then
Call UserForm_Initialize
End If
Else
MsgBox "Click the item you want to remove."

End If
End Sub
過程 cmdDelete_Click 讓你從自訂集合 CEmployees 裡面清除員工。要刪除員工的話,你必須
點擊清單方塊的適當成員。當你點擊一個清單成員( 和按鈕 Delete Employee ),
cmdEmployeeList_Click 過程就自動執行。該過程確保更新清單方塊的內容。員工將同時從集合
和清單方塊裡刪除。如果清單方塊裡面只有一個員工,那麼 VBA 將調用過程 UserForm_Initialize
在清除最後一個員工後將某些控制項失活。cmdDelete_Click 過程裡有好幾個 MsgBox 語句,讓你
在做決定的時候檢查清單方塊控制項的內容。除了從自訂集合裡刪除員工之
外, 過程
226
cmdDelete_Click 也必須從工作表的相應行刪除員工資訊。使用函數 FindId 可以很方便地在工
作表裡找到員工資料(該過程的代碼見下面的第 12 步)。該函數將要刪除的行號返回到過程
cmdDelete_Click。
12. 輸入下述函數過程:
Private Function FindId()
Set ws = ActiveWorkbook.Sheets("Salaries")

startRow = ActiveSheet.UsedRange.Rows.Count + _

1 - CEmployees.Count
endRow = ActiveSheet.UsedRange.Rows.Count

For Each cell In ws.Range(Cells(startRow, 1), _

Cells(endRow, 1))
If cell.Value = extract Then
empLoc = cell.Row
FindId = empLoc
Exit Function
End If
Next
End Function
函數過程 FindId 將含有表單清單方塊中當前選擇的員工資料的行號返回到主調過程。工作表中
的資料搜索基於變數 extract 的內容,它存儲員工的唯一號碼。員工 ID 的搜索被限制與工作表
的第一列,並從集合的第一個成員放置的位置開始搜索,這樣使搜索更快一些。你不要在工
作表整個使用的區域裡搜索。想想如果你不只一次地使用了表單,但是,自訂集合的內容
不會包含前面輸入的員工。
13. 輸入下述過程 cmdUpdate_Click:
Private Sub cmdUpdate_Click()
If optHighlighted = False And optAll = False Then

MsgBox "Click the 'Highlighted Employee' or " _

& " 'All Employees' option button."

Exit Sub
End If
If Not IsNumeric(txtRaise) Then
MsgBox "This field requires a number."

txtRaise.SetFocus
Exit Sub
End If
If optHighlighted = True And _
lboxPeople.ListIndex = -1 Then
MsgBox "Click the name of the employee."

Exit Sub
End If
If lboxPeople.ListIndex <> -1 And _

optHighlighted = True And _


optAmount.Value = True And _
txtRaise.Value <> "" Then
extract = CEmployees.Item(lboxPeople.ListIndex + 1).Id

MsgBox extract
Call FindId
MsgBox empLoc
choice = 2
amount = txtRaise
227
CEmployees.Item(lboxPeople.ListIndex + 1).Salary = _

emp.CalcNewSalary(choice, _
CEmployees.Item(lboxPeople.ListIndex + 1).Salary, amount)

Range("D" & empLoc).Formula = CEmployees. _

Item(lboxPeople.ListIndex + 1).Salary
cmdEmployeeList.Value = True
ElseIf lboxPeople.ListIndex <> -1 And _

optHighlighted = True And _


optPercent.Value = True And _
txtRaise.Value <> "" Then
extract = CEmployees.Item(lboxPeople.ListIndex + 1).Id

MsgBox extract
Call FindId
MsgBox empLoc
CEmployees.Item(lboxPeople.ListIndex + 1).Salary = _

CEmployees.Item(lboxPeople.ListIndex + 1).Salary + _

(CEmployees.Item(lboxPeople.ListIndex + 1).Salary * _

txtRaise / 100)
Range("D" & empLoc).Formula = CEmployees. _

Item(lboxPeople.ListIndex + 1).Salary

cmdEmployeeList.Value = True
ElseIf optAll = True And _
optPercent.Value = True And _
txtRaise.Value <> "" Then
For Each emp In CEmployees
emp.Salary = emp.Salary + ((emp.Salary * txtRaise) _

/ 100)
extract = emp.Id
MsgBox extract
Call FindId
MsgBox empLoc
Range("D" & empLoc).Formula = emp.Salary

Next
emp cmdEmployeeList.Value = True
ElseIf optAll = True And _
optAmount.Value = True And _
txtRaise.Value <> "" Then
For Each emp In CEmployees
emp.Salary = emp.Salary + txtRaise
extract = emp.Id
MsgBox extract
Call FindId
MsgBox empLoc
Range("D" & empLoc).Formula = emp.Salary

Next emp
cmdEmployeeList.Value = True
Else
MsgBox "Enter data or select an option."
End If
End Sub
228
有了過程 cmdUpdate_Click,你就可以使用確定的百分比或者特定量來修改薪水。可以為選定的員
工或者清單方塊和集合裡面列出的所有員工更新薪水。cmdUpdate_Click 過程核實用戶是否選擇了適
當的選項按鈕,然後在文字框裡輸入增加數位。取決於你選擇了哪個選項按鈕,給某個員工或者所
有員工更新的薪水量可以是按照百分比,也可以是按照某個固定的量。薪水的更改也會反映在工作
表裡。圖 11-15 顯示 James Nolan 的薪水,以按百分之十增加了該薪水。在文字框裡面輸入負數,你
可以按照特定的百分比或者量減少薪水。
圖 11-5 可以按照特定的百分比或者固定量增加或者減少員工的薪水
14. 選擇“插入”|“模組”在當前工程裡插入一個標準模組,重命名該模組為 WorkAndPay,輸入
下述過程來顯示表單 Employees and Salaries:
Sub ClassDemo( )
Salaries.Show
End Sub
15. 運行過程 ClassDemo,使用自訂類。
你可以點擊表單的背景並且按下 F5,運行表單 Salaries,或者你也可以在工作表裡放置一個按鈕,
並將過程 ClassDemo 指定給它(參見第十章,如何將按鈕放在工作表裡)。
15.觀察 VBA 過程的執行
為了幫助你理解代碼運行時會發生什麼,以及你的自訂物件如何工作,我們來逐步運行過程
cmdSave_Click。本練習也可以說是第十三章裡將包括的調試技巧的簡單介紹。
1. 在工程流覽視窗,選擇 Salaries 表單,並點擊視窗上面的查看代碼按鈕
2. 出現 Salaries 代碼視窗時,從代碼視窗左上角的複合框裡選擇過程 cmdSave
3. 在下述代碼行的左邊框上點擊一下,設置中斷點:
If txtLastName.Value = "" Or txtFirstName.Value = "" Or _

txtSalary.Value = "" Then


229
圖 11-6 邊框上的紅色圓圈代表中斷點。當 VBA 遇到帶中斷點的語句時,它就會自動切換到代碼視窗並且
顯示該白字紅底的文本。
4. 在工程流覽視窗,選中模組 WorkAndPay 並點擊查看代碼按鈕
5. 將游標放在過程 ClassDemo 裡的任意位置,並且按下 F5,或者選擇“運行”|“運行子過程/用
戶表單”
6. 當表單出現時,在 Last Name,First Name 和 Salary 文字框裡輸入資料,然後點擊表單的按鈕
Save。現在 VB 將切換到代碼視窗,因為它碰到了過程 cmdSave_Click 第一行的中斷點。
圖 11-7 當 VB 運行過程遇到中斷點時,它會切換到代碼視窗,並在中斷過程的語句的左邊邊框上顯示
一個黃色箭頭
230
7. 通過按 F8 逐句運行代碼,VB 運行當前語句,並且自動向前移動到下一句然後停止執行。當前語
句的邊框上顯示了黃色箭頭,並且為黃色底色。不斷地按 F8 逐句執行該過程。當 VB 遇到 With emp
語句時,它會切換到過程 Class_Initialize。
圖 11-8 當 VB 遇到對物件 emp 的引用的時候,它就會出去執行過程 Class_Initialize。在它執行完該
過程裡的語句之後,VBA 會返回到過程 cmdSave_Click 裡面。
當 VB 遇到語句 Cells(Index, 1).Formula = emp.ID 時,它就會出去執行類別模組 CEmployee 裡的過程
Property Get Id。
圖 11-9 對自訂物件屬性的讀取要通過過程 Property Get 來實現。
8. 使用 F8 鍵,追蹤過程 cmdSave_Click 的執行,直到結束。
當 VBA 遇到過程的結束(End Sub)時,黃色加亮將會關閉。這時候,點擊螢幕下面的視窗任務
欄上的 Excel 按鈕返回到當前表單。輸入一個新員工的資料,然後點擊按鈕 Save。當 VB 顯示代
碼視窗時,選擇“調試”|“清除所有中斷點”。現在按 F5 運行完剩餘的代碼。
技巧 11-6 VBA 調試工具
VB 提供了許多調試工具,幫助你分析你的應用程式如何操作,以及找到你過程裡的錯誤源。參見第
十三章這些工具的使用。
16.接下來……
在本章裡,你學習了在 VBA 過程裡如何創建和使用你自己的物件和集合。你使用了類別模組來創建一
個使用者定義(自訂)物件。你已經看到了如何使用 Property Get 和 Property Let 程序定義你的自
定義物件的屬性。你也學習了如何給你的自訂物件編寫方法,再有,你看到了如何通過創建一個
自訂表單使得類別模組為使用者可用。最後,你學習了如何通過逐句執行代碼來分析你的 VBA 程式。
在下一章,你將學習如何通過自訂功能表和工具條讓你的 VBA 程式為終端使用者所用。
第十二章使用 VBA 創建自訂功能表和工具列
注意,使用中文版的使用者需要將相應的代碼作一定的修改。下面列出了中文版的工作表功能表列標題
檔(&F) 編輯(&E) 視圖(&V) 插入(&I) 格式(&O) 工具(&T) 資料(&D) 視窗(&W) 説明(&H)
231
使用者肯定期望在任何 Windows 應用程式裡有一個方便的方法來選擇命令和選項,因此,當你對某個
工作表自動化編寫完 VBA 程式時,你還應該花些時間添加一些功能,讓你的應用程式更加使用方便
和快捷。使用者介面最為期望的功能就是自訂功能表和工具列,當你的 VBA 套裝程式含很多個過程時這
尤為重要。簡單地在內置功能表或者自訂工具列上創建一個控制項,就可以對某個命令提供一個快捷
訪問。本章將教你如何程式設計使用功能表和工具列。
1.工具列
術語“工具列”既指工具列又指功能表列,工具列給使用者提供了對應用程式命令的快捷方便的訪問。
可以通過自訂對話方塊(參見圖 12-1)輕鬆才創建和修改工具列。訪問該對話方塊的方法之一是選擇
“工具”|“自訂”,你也可以選擇“視圖”|“工具列”|“自訂”,或者在工具列的任意位置
按一下右鍵,然後從快顯功能表上選擇“自訂”。工具列可以包括按鈕,功能表,或者兩者都有。菜單
欄位於應用程式視窗的頂部(緊挨著它的標題列),它是一種特殊的工具列。除了命令之外,功能表
欄也可以包含圖片,允許使用者很快將命令和工具列上相應的按鈕聯繫起來。例如,“檔”菜單裡
的“新建”和“打開”,在該命令的左邊顯示了圖片,這些相同的圖片也可以在 Excel“常用”工具
欄裡面找到。
圖 12-1 使用自訂對話方塊,可以手動自訂功能表和工具列
自訂對話方塊包含三頁:工具列,命令和選項。使用工具列頁,你可以創建一個新工具列、更改現
有工具列的名稱、清除工具列或者重新設置工具列。命令頁允許你將新的命令拖曳到活動功能表裡或
者任何可見的工具列。點擊某個類別後,你可以看到它裡面可用的命令清單。選項頁讓你通過設置
圖示大小,顯示關於工具列的螢幕提示以及選擇動畫,來設置你個性化的功能表和工具列。如果你需
要重新看一下如何通過對話方塊操作功能表和工具列的話,可以看看線上說明。本章側重於 VBA 語句和
過程,以獲取對應用程式的功能表和工具列的完全控制。
使用物件 CommandBar
CommandBars 是物件集合, 代表當前應用程式裡的所有工具列。該集合裡的
每個物件稱為
CommandBar。術語“CommandBar”用來代表功能表列、快顯功能表或者工具列。因為 CommandBar 物件可
以代表各種工具(工具列,功能表列,快顯功能表),所以該物件有個專門的屬性 Type,可以用來返回
工具列的特定類型,如表 12-1 所示。
表 12-1 集合 CommandBars 裡的 CommandBar 物件類型
對象類型
工具列
功能表列
快顯功能表
索引
0
1
2
常量
msoBarTypeNormal
msoBarTypeMenuBar
msoBarTypePopup
1. 打開一新工作簿並保存為 Chap12.xls
2. 切換到 VB 編輯器螢幕
3. 選擇當前 VBA 工程 Chap12.xls ,並重命名為 CustomTools
4. 添加一個新模組
5. 輸入過程 MyToolBars,如下所示:
232
Sub MyToolBars( )
Dim bar As CommandBar
Dim r As Integer
r=1
ActiveSheet.Range(“A1”).Formula = “List of Toolbars”
For Each bar In CommandBars
If bar.Type = msoBarTypeNormal Then

With Worksheets("Sheet1").Range("A1")

.Offset(r, 0) = bar.Name
.Offset(r, 1) = bar.Index
End With
r=r+1
End If
Next
Set bar = Nothing
End Sub
上面的過程在集合 CommandBars 裡面搜索工具,並且只選擇 Type 屬性為 msoBarTypeNormal 的工具。
如果集合 CommandBars 裡面的某個成員是工具列的話,那麼 VB 就會將它的名稱輸入到活動工作表的
第一列,B 列將保存該物件的索引號。
修改上面的過程,讓它輸入集合 CommandBars 裡所有物件(工具列,功能表列,快顯功能表)的名稱到
工作表中去,使用表 12-1 作為參考。
可以使用工具列的名稱或者索引號來引用 CommandBars 集合裡的某個特定的工具列。
1. 在立即視窗裡輸入下述語句:
?CommandBars(1).Name
當你按下回車鍵後,VB 就會返回 CommandBars 集合裡的第一個成員的名稱。
2. 在立即視窗輸入下述語句:
?CommandBars("Circular Reference").Type

VB 返回 0,這是工具列的索引號碼(參見表 12-1)
3. 要計算 CommandBars 集合裡可用工具的總數,可以使用 Count 屬性。在立即視窗裡輸入下述語句:
?CommandBars.Count
2.創建自訂工具列
要創建自訂工具列、功能表列或者快顯功能表,可以使用 CommandBars 物件的 Add 方法。
假設你想要創建一個叫做“Budget Plans”的新工具列,你要調用的 Add 方法如下所示:
CommandBars.Add(Name, Position, MenuBar, Temporary)

可選參數 Name 是你想要分配給你的新命令條的名稱,如果你不明確該名稱,VB 會分配一個普通名稱,


例如“自訂 1”。Position 參數決定新命令條將出現在螢幕的哪裡(參見表 12-2)。
表 12-2 CommandBar 物件的位置常量
位置常量
msoBarLeft
msoBarRight
msoBarTop
msoBarBottom
msoBarFloating
msoBarPopup
msoBarMenuBar
索引
0
2
1
3
4
5
6
描述
命令列位於應用程式視窗的左邊
命令列位於應用程式視窗的右邊
命令列位於應用程式視窗的上面
命令列位於應用程式視窗的底部
命令列浮在螢幕上
快顯功能表
命令列取代系統功能表(僅用於 Macintosh)
參數 MenuBar 是個邏輯值(True 或 False),它決定新命令條是否取代活動功能表條。如果你想要取代
活動功能表條的話,就輸入 True,否則使用 False。參數 Temporary 是邏輯值(True 或 False),決定何
時刪除命令條。使用 True,當 Excel 程式關閉的時候命令條就會自動刪除。使用 False 的話,當你退
233
出該程式的時候,該工具列不會被刪除。
你可以在立即視窗裡試驗創建工具列。
1. 在立即視窗裡面輸入下述語句,注意要將完整的語句書寫在一行:
set newToolbar = CommandBars.Add("Budget Plans", msoBarRight, False, True)

當你按下回車鍵時,VB 就會在集合 CommandBars 裡面添加一個新的工具列,具體名稱為“Budget


Plans”。切換到 Excel 應用軟體視窗並且選擇“視圖”|“工具列”,你可以看到 Excel 顯示的一
列可用工具列清單,包括你剛才創建的那個(參見圖 12-2)。
圖 12-2 你在 Excel 內建工具列清單裡面添加了一個自訂工具列
2. 切換回 VB 編輯器視窗,並在立即視窗裡輸入下述語句:
CommandBars("Budget Plans").Visible = True

切換到 Excel 應用程式視窗查看該工具列,工具列“Budget Plans”出現在垂直捲動條的右邊。


還記得你創建該工具列的時候嗎?你使用的是常量 msoBarRight 來決定其位置。
3. 現在關閉 Excel 應用軟體,重新打開它並且查看工具列 Budget Plans 是否依然出現在應用軟體
窗口的右邊。
因為你在 Add 方法最後一個參數的位置使用了邏輯值 True,工具列 Budget Plans 應該已經不在
了。
有個好主意,在你試圖創建新工具列之前,需要檢查某個特定名稱的工具列是否已經存在于集合
CommandBars 裡了。下述過程將創建工具列“Budget Plans”,倘若不存在具有該相同名稱的工具列
的話。在工程 CustomTools (Chap12.xls)的代碼視窗裡面輸入該過程,並且運行兩次。第二次執行
該過程的時候,你將看到一資訊,提醒你已經有了這樣一個工具列。
Sub MakeToolBar()
Dim bar As CommandBar
Dim flagExists As Boolean
flagExists = False
234
For Each bar In CommandBars
If bar.Name = "Budget Plans" Then
flagExists = True
MsgBox "The toolbar with this name already exists."

Exit For
End If
Next bar
If Not flagExists Then
Set bar = CommandBars.Add("Budget Plans", _

msoBarBottom, False, True)


CommandBars("Budget Plans").Visible = True

End If
Set bar = Nothing
End Sub
3.刪除自訂工具列
如果你創建了工具列但不想保留它,那麼你可以去掉它而不用關閉 Excel 應用軟體,只要使用 Delete
方法就可以了。例如要刪除工具列“Budget Plans”,你可以在立即視窗裡輸入下述語句:
CommandBars("Budget Plans").Delete
注意,你不能刪除內建工具列。
4.使用 CommandBar 的屬性
物件 CommandBar 有許多屬性,你在立即視窗裡面使用它們中的一些。
1. 使用立即視窗來創建一個叫“My Reports”的工具列:
set myBar= CommandBars.Add("My Reports", msoBarBottom, False)

2. 使用下述語句來確定某個工具列是否是內置的:
?CommandBars("My Reports").BuiltIn
3. 輸入下述語句可以確定新工具列在 CommandBars 集合裡的索引號:
?CommandBars("My Reports").Index
當設置屬性 Visible 為 True 時,該工具列將顯示在螢幕上;而設置屬性 Visible 為 False 時則可以隱
藏該工具列。
5.使用 CommandBar 控制項
一個空的工具列並不能做什麼,要讓你的工具列有用的話,你就需要將想要地控制項放置在上面並且
給它們指定適當的 VBA 過程。有三種類型的命令條控制項,如下表所示:
表 12-3:可以放置在工具列上的控制項類型
對象名稱
CommandBarButton
CommandBarPopup
描述
該物件代表工具列按鈕和功能表選項。當你點擊按鈕或者選擇功能表選項時,
就會執行相應的 VBA 過程
該物件代表彈出控制項,點擊時顯示一功能表或者子功能表
CommandBarComboBox 該物件代表文字方塊、清單方塊或者下拉式清單方塊(例如,格式工具列上的字
體和字型大小控制項,或者常用工具列上的縮放控制項)
CommandBar 物件的一個重要屬性是 Controls 屬性,該屬性返回某特定工具列上所有控制項的集合。
1. 在立即窗口裡敲入下述語句:
?CommandBars(1).Controls. Count
當你按下回車鍵時,VB 就會返回工作表系統功能表條上所有可用控制項的總數。
2. 輸入下述語句來返回工作表系統功能表條上第一個控制項的名稱:
?CommandBars(1).Controls(1).Caption

VB 返回第一個控制項的名稱:&File。字母 F 前面的字元&表明該功能表選項可以通過鍵盤按下 Alt+F


235
來執行。
3. 輸入下面的語句來執行一特定選項:
CommandBars(1).Controls(1).Execute
方法 Execute 啟動該特定的控制項,檔功能表應該被打開了。
4. 在當前工程代碼視窗裡輸入過程 ControlList,來將活動功能表條上所有控制項的名稱寫入立即窗
口:
Sub ControlList()
Dim bar As CommandBar
Dim ctrl As CommandBarControl
Set bar = CommandBars(1)
Debug.Print bar.Name & ": " & bar.Controls.Count

For Each ctrl In bar.Controls


Debug.Print ctrl.Caption
Next
End Sub
5. 運行上述過程後,查看立即視窗,你將看到下述清單:
Worksheet Menu Bar: 10
&File
&Edit
&View
&Insert
F&ormat
&Tools
&Data
A&ction
&Window
&Help
為 CommandBar 添加控制項
要運行期望的 VBA 過程的話,那麼你可以添加一個內置的或者自訂控制項到內建工具列。如果你更
願意,你也可以添加控制項到自訂工具列。無論你是添加內置控制項還是自訂控制項,到內建工具列
或者到自訂工具列,總是要使用 Add 方法,語法如下:
CommandBar.Controls.Add(Type, Id, Parameter, Before, Temporary)

CommandBar 是你要添加控制項的那個物件。
Type 是一常量,決定你添加的自訂控制項的類型,你可以從下述類型中選擇一個:
msoControlButton
msoControlPopup 10
msoControlEdit 2
msoControlDropDown 3
msoControlComboBox 4
1
Id 是個整數,指定你想要添加的內置控制項編號。
Parameter 用來給 VB 過程發送資訊,或者儲存關於該控制項的資訊。
Before 參數是新控制項添加在之前的那個控制項的索引號,如果忽略,那麼 VB 將在該命令條的結尾處添
加控制項。
Temporart 參數是個邏輯值(True 或 False),決定控制項什麼時候被刪除。設置該參數為 True 的話,將
導致應用軟體關閉時,該控制項將自動被刪除。
1. 在代碼視窗裡輸入過程 AddBarAndControls,如下所示:
Sub AddBarAndControls( )
With Application.CommandBars.Add("Test", , False, True)

.Visible = True
.Position = msoBarBottom
236
With .Controls.Add(msoControlButton)
.Caption = "List of Controls"
.FaceId = 4
.OnAction = "ControlList"
End With
End With
End Sub
該過程創建了一個名為 Test 的新工具列,並將它放在應用軟體視窗的底部。接下來,Add 方法在其
上放置一個名為 List of Controls 的按鈕,並用印表機圖示以識別。當使用者點擊該按鈕時,之前已
準備好的過程 ControlList 就會被執行。
6.理解和使用控制項屬性
放置在工具列上的控制項有許多屬性可供你讀取或者設置。要知道某個控制項是內置的或者自訂的
話,那麼你可以使用屬性 BuiltIn,如果返回值為 True,那麼該控制項就是內置控制項;所有用戶定義
的控制項將返回值 False。如果 Enabled 屬性的值為 True,那麼該控制項就是活動的,並且可以對滑鼠點
擊作出反應。非活動控制項的 Enabled 屬性被設置為 False 了。不用說,所有控制項擁有屬性 Caption,
可以用來確定或者設置控制項標題。
以 CommandBarComboBox 物件為代表的組合類型的控制項具有一些特殊的屬性,例如 DropDownLines,
DropDownWidth,List,ListCount,ListIndex 以及 Text。參見表 12-4 對這些屬性的解釋。
表 12-4 物件 CommandBarComboBox 選取的屬性
屬性
DropDownLines
DropDownWidth
List(Index)
ListCount
ListIndex
Text
描述
返回或者設置當用戶點擊下拉式列示方塊下拉箭頭時,顯示的專案數量
返回或者設置下拉式方塊控制項的寬度,以圖元為單位
返回或者設置由 Index 指定的列表專案值(列表裡的第一個專案索引號為
0)
返回清單清單裡總專案數
返回或者設置清單裡選定項
返回或者設置出現在下拉式方塊控制項部分——文字框顯示的文本
1. 在代碼視窗輸入過程 MyCombo,如下所示:
Sub MyCombo()
Dim cbo As CommandBarControl
Set cbo = CommandBars(4).Controls.Add(Type:=4, Before:=1)

With cbo
.AddItem Text:="Row", Index:=1
.AddItem Text:="Column", Index:=2
.Caption = "Insert Row/Column"
.DropDownLines = 2
.DropDownWidth = 80
End With
End Sub
過程 MyCombo 創建了一個下拉式列示方塊(Type:=4 表明 msoControlComboBox)並將其放置在內置“格式”工
具欄(該工具列是 CommandBars 集合裡的第四個 CommandBar 物件)的最前面。接下來,有兩個項目
被添加到下拉式方塊控制項。該過程也設置了下拉式列示方塊標題以及下拉式方塊控制項寬度。
2. 切換到 Excel 視窗檢查格式工具列裡的第一個控制項
3. 返回到 VB 編輯器視窗
4. 在立即視窗裡輸入下述語句,從格式工具列裡刪除由 MyCombo 過程創建的下拉式方塊控制項:
CommandBars(4).Controls(1).Delete
當你按下回車鍵後,VB 就將格式工具列裡的第一個控制項刪除了。
由於有放置在其上的圖像,這些出現在工具列上的按鈕都很好辨認。如果工具列上的控制項是個
237
CommandBarButton 物件,屬性 FaceId 將返回或者設置按鈕上圖示的 ID 編號。大多數情況下, 圖示
的 ID 編號和控制項的 ID 屬性是相同的。使用 CopyFace 方法可以將圖示圖片複製到 Windows 的剪切板上。
接下來的過程 Images 將出現在標準工具列上的按鈕列出到試算表上。除了按鈕名稱,該清單同時
也顯示它的圖示。因為不能複製當前禁用的圖示圖像(參見標準工具列上的“撤銷”和“恢復”按
鈕),所以當 VB 試圖複製按鈕的圖示至剪切版時,就會遇到錯誤。過程 Images 利用 On Error GoTo
ErrorHandler 語句捕獲該錯誤。這樣一來,當 VB 遇到錯誤時,就會跳到 ErrorHandler:標誌並且執
行該標誌下麵的指令。最後一條語句 Resume Next 會讓 VB 回到剛才導致該錯誤的下面一條語句,並
且該過程會繼續直到標準工具列上所有的按鈕都被檢查一遍了。你將在下一章學習更多有關錯誤捕
捉的知識。
圖 12-3 標準工具列上圖示清單。你可以修改過程 Images 來列出任何工具列上的完整按鈕和它們的
圖示。
Sub Images()
Dim i As Integer
Dim total As Integer
Dim buttonId As Integer
Dim buttonName As String
Dim myControl As CommandBarControl
Dim bar As CommandBar
On Error GoTo ErrorHandler
Workbooks.Add
Range("A1").Select
With ActiveCell
.Value = "Image"
.Offset(0, 1) = "Index"
.Offset(0, 2) = "Name"
.Offset(0, 3) = "FaceId"
End With
Set bar = CommandBars(3)
total = bar.Controls.Count
With bar
For i = 1 To total
238
buttonName = .Controls(i).Caption
buttonId = .Controls(i).ID
Set myControl = CommandBars.FindControl(ID:=buttonId)

myControl.CopyFace
' error could occur here
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
With ActiveCell
.Offset(0, 1).Value = buttonId
.Offset(0, 2).Value = buttonName
.Offset(0, 3).Value = myControl.FaceId

End With
Next i
Columns("C:C").EntireColumn.AutoFit

Exit Sub
ErrorHandler:
Set myControl = CommandBars(3).Controls.Add

With myControl
.FaceId = buttonId
.CopyFace
.Delete (False)
End With
Resume Next
End With
End Sub
7.控制項方法
控制項擁有很多相關的方法,這些方法允許你進行一些操作,例如移動、複製和刪除控制項。假設你想
複製格式工具列上的“粗體”按鈕到標準工具列上:
1. 在立即視窗裡輸入下述三條語句:
set myBar = CommandBars(3)
set myControl = CommandBars(4).Controls(3)

myControl.Copy Bar:=myBar, Before:=1

2. 切換到 Excel 應用程式視窗,你應該能看到標準工具列“新建”按鈕的左邊有了“粗體”按鈕。


3. 切換到 VB 編輯器螢幕,並在立即視窗裡輸入下述語句從標準工具列上刪除該粗體按鈕:
CommandBars(3).Controls(1).Delete
將 Copy 方法改成 Move 方法的話,你就可以將粗體按鈕從格式工具列移動到標準工具列,你可以自己
試驗一下。使用方法 Reset,你可以將工具列恢復為缺省設置。當你練習完移動和複製按鈕後,請
在立即視窗裡輸入下述語句:
CommandBars(3).Reset
CommandBars(4).Reset
如果某個控制項是個下拉式列示方塊(CommandBarComboBox)的話,那麼你就可以使用 AddItem 方法給它的

拉清單添加新專案。如果要從該清單刪除專案的話,就可以使用 RemoveItem 方法。我們花上幾分鐘
來在立即視窗裡練習這些方法吧。
1. 啟動你之前準備的過程 MyCombo,運行該過程在格式工具列上放置一個自訂群組合框控制項
2. 在立即視窗裡輸入下述語句:
set myBar = CommandBars(4)
set myControl = CommandBars(4).Controls(1)

myControl.RemoveItem(1)
myControl.AddItem "Cells", 1
239
3. 切換到 Excel 視窗,並且查看格式工具列上該自訂群組合框控制項裡可用的專案
4. 返回到 VB 編輯器視窗,在立即視窗裡面輸入下述語句並且回車,以重新設定格式工具列:
CommandBars(4).Reset
8.使用功能表
就像工具列一樣,功能表也是 CommandBar 物件。有兩類菜單:內置菜單和快顯功能表。內置菜單出現在
應用程式視窗的頂端,緊接著在標題列之下。在 Excel 2002 裡,有兩種內置菜單:工作表菜單和圖
表菜單。
如果當前活動的是工作表時,那麼出現的就是工作表功能表(見圖 12-4),並列出幾個主要的功能表。
每個主功能表組都和某些特定的任務相關聯,可以在工作表或者工作簿上執行。例如,格式菜單包含
各種選項允許將各種各樣的格式應用到工作表。有些功能表選項組合了一些更詳細的選項在子功能表裡
(見圖 12-5)
圖 12-4 Excel 應用軟體的工作表功能表列
圖 12-5 選擇右邊帶三角的功能表選項將展開一個帶有更多選項的子功能表
當使用者正在使用圖表頁或者選擇了內嵌於工作表裡的圖表時,工作表功能表列就會被圖表功能表列取代
(見圖 12-6)。同一時間應用程式視窗上只能顯示一個功能表列。
圖 12-6 Excel 內置的圖表功能表列
當你在某個物件上按一下右鍵或者按下 Shift+F10 時,就會出現快顯功能表。Excel 2002 有 50 個以上的
快顯功能表。快顯功能表包含一些經常使用的命令,例如,當你在工作表的任何儲存格按一下右鍵時,單
元格快顯功能表就會出現(見圖 12-7)。當你在工作表標籤上按一下右鍵時,就會出現工作表標籤的標
准菜單(見 12-8)。
240
圖 12-7 當按右鍵任何儲存格時出現該快顯功能表
圖 12-8 當按右鍵工作表標籤時出現該快顯功能表
功能表列和工具列一樣,用同樣的物件 CommandBar 代表。使用物件 Control 指向功能表,功能表選項,子
菜單或快顯功能表。Control 物件的類型由適當的常量決定。使用 msoControlPopup 指向功能表,
msoControlButton 常量指向功能表選項,而 msoBarPopup 常量則指向快顯功能表。你將在下一章節裡學
習如何使用這些常量。
9.功能表程式設計
使用 VBA,你可以進行一些操作,例如創建新功能表列,添加新功能表到內置功能表列,啟動內置或者自
定義功能表列,刪除用戶定義功能表列,重設內置功能表,判斷某個功能表列時內置的或者自訂的,等等。
1. 在立即視窗裡輸入下述語句,返回當前活動功能表列地名稱:
?CommandBars.ActiveMenuBar.Name
當你按下回車,VB 就會返回活動功能表列的名稱:工作表功能表列。
功能表列上的每個功能表都有一個標題,可以通過屬性 Caption 和 Id 返回或者設置。
2. 在本章的模組裡面輸入下述過程,來返回內置功能表列上格式菜單的 ID:
Sub Return_ID()
Dim myControl As Object
Set myControl = CommandBars("Worksheet menu bar").Controls("Format")

Debug.Print myControl.Caption & " Id is " & myControl.Id

End Sub
如果你想讓上面的過程更靈活一些的話,可以按照下述 Set 語句,讓使用者可以返回工作表功能表
欄上其它菜單的 ID:
Set myControl = CommandBars("Worksheet menu bar").Controls (InputBox("Enter the menu

name (Example: Format):"))


3. 運行過程 Return_Id,然後切換到立即視窗查看其結果
4. 在立即視窗裡輸入下述語句,可以創建一個名為 Other 的自訂功能表,並將它放置在內置工作
表功能表列上:
CommandBars("Worksheet menu bar").Controls.
241
Add(Type:=msoControlPopup,
before:=10).Caption = "&Other"
當你按下回車鍵並切換回 Excel 應用程式視窗時,工作表功能表列將會在“説明”功能表前顯示你
的自訂菜單。如果你沒有將上面的語句輸入在一行的話,它就不會起作用。
現在“Other”功能表是空的,下面一步時示範如何添加功能表命令。
5. 在立即視窗裡輸入下述語句,給自訂功能表裡添加自訂命令(選項):
CommandBars("Worksheet menu bar").Controls("Other").
Controls.Add(Type:=msoControlButton, before:=1) _

.Caption = "Gridlines"
當你按下回車鍵並切換到 Excel 應用程式功能表,然後選擇 Other,你將看到命令 Gridlines。如
果你沒有將上面的語句輸入在一行的話,它就不會起作用。
下一步將要求你給你的自訂功能表選項指定一個適當的 VBA 過程,當使用者選擇該功能表時就會執
行。
6. 在當前工程代碼視窗裡輸入下述過程打開或者關閉格線的顯示:
Sub GridOnOff( )
ActiveWindow.DisplayGridlines = Not ActiveWindow.DisplayGridlines

End Sub
7. 在立即視窗裡輸入下面的代碼,將過程 GridOnOff 指定給你的自訂功能表選項:
CommandBars("Worksheet menu bar").Controls("Other").Controls
("Gridlines").OnAction = "GridOnOff"

當你按下回車,VB 就會將過程 GridOnOff 指定給 Gridlines 功能表項目。如果你沒有將上面的語句輸


入在一行的話,它就不會起作用。
當你切換到 Excel 應用程式視窗並且選擇“Other”|“Gridlines”的時候,如果格線顯示被
關閉時,VB 就會打開格線顯示,反之亦然。
將功能表項目的屬性 Enabled 設置為 False 可以臨時禁用該功能表項目。一個被禁用的功能表項目其名稱將變
為灰色,並且點擊它時不會有任何反應。
8. 在立即視窗裡輸入下述語句在同一行,可以禁用 Other 功能表裡的 Gridlines 命令:
CommandBars("Worksheet menu bar").Controls("Other").Controls ("Gridlines").Enabled
= False
當你按下回車,VB 就會將 Gridlines 功能表項目禁用。如果你沒有將上面的語句輸入在一行的話,
它就不會起作用。當你切換到 Excel 應用程式視窗並且選擇 Other 時,Gridlines 選項不再可用。
9. 要啟動 Other 功能表裡的 Gridlines 命令的話,可以在立即視窗裡將邏輯值 False 取代為 True:
CommandBars("Worksheet menu bar").Controls("Other"). Controls ("Gridlines").Enabled

= True
注意,內置功能表上的每個具體選項都是和它們相似的命令組織在一起的,組和組之間用一條橫
線分割開(見圖 12-9)。使用方法 BeginGroup 可以在功能表項目之間添加這樣一條線。
圖 12-9 每個功能表用橫線分成好幾個部分
10. 在立即視窗裡輸入下述語句,可以在“視窗”功能表的“隱藏”命令上面添加一條橫線:
CommandBars("Worksheet menu bar").Controls("Window").Controls ("Hide").BeginGroup =

242
True
當你按下回車,VB 就會在視窗功能表的隱藏命令上面添加一條橫線。如果你沒有將上面的語句輸
入在一行的話,它就不會起作用。
當你切換到 Excel 應用程式視窗並且選擇視窗,你就會看到隱藏和取消隱藏命令包含在兩條橫
線裡了。上面的那條就是你自己添加的。
當某個功能表項目被選中時,該選項左邊就會出現一個複選記號,例如在圖 12-9 裡的視圖菜單裡,
編輯欄和狀態列左邊的複選記號表明這些選項當前是有效的。
11. 要顯示你的自訂 Other 功能表裡的 Gridlines 選項被選中的話,可以修改過程 GridOnOff,如下
所示:
Sub GridOnOff()
Dim Other As Object
Set Other = CommandBars("Worksheet menu bar").Controls("Other")

ActiveWindow.DisplayGridlines = Not ActiveWindow.DisplayGridlines

If ActiveWindow.DisplayGridlines = True Then


Other.Controls("Gridlines").State = msoButtonDown

Else
Other.Controls("Gridlines").State = msoButtonUp

End If
End Sub
運行該過程,然後切換到 Excel 視窗,並且選擇 Other | Gridlines。如果活動工作表的網格
線時顯示的話,那麼它們現在就會被關閉。再次選擇 Other | Gridlines。
12. 在立即視窗裡輸入下述語句,以刪除內置工作表功能表列裡的自訂菜單:
CommandBars("Worksheet menu bar").Controls("Other").Delete

當你刪除某個自訂功能表的時候,裡面所有的功能表項目都會自動被刪除。自訂功能表以及其選項
一旦被刪除,你就不能恢復它們。
10.創建子功能表
功能表項目在其名稱右邊包含一個黑色三角的,都會顯示一個子功能表,包括一些額外的命令。假設你想
在工具功能表裡添加一個子功能表。
1. 在立即視窗裡輸入下述語句,給工具功能表添加一個子功能表:
CommandBars("Worksheet
bar").Controls("Tools").Controls.Add(Type:=msoControlPopup, Before:=1) _
.Caption = "My Submenu"
menu
當你按下回車鍵,上面的指令會在工具功能表(工作表功能表列)的上面放置一個自訂子功能表,
叫做 My Submenu。如果你沒有將上面的語句輸入在一行的話,它就不會起作用。
2. 在立即視窗裡輸入下述一行指令,可以在子功能表裡添加自訂命令:
CommandBars("Worksheet
Submenu").Controls _
menu bar").Controls("Tools").Controls("My
.Add(Type:=msoControlButton, Before:=1).Caption = "Option 1"

當你按下回車鍵,上面的指令會在工具功能表裡的 My Submenu 裡添加命令 Option 1。如果你沒有


將上面的語句輸入在一行的話,它就不會起作用。你可以使用相同的技術給你的子功能表裡添加
更多的功能表項目。
下面的過程將在內置菜單格式裡添加自訂子功能表 Colors,並且在裡面放置四個選項:Red, Green,
Blue 和 Black。使用這些選項,你可以更改所選工作表儲存格或者儲存格區域裡的文本顏色。接下
來的過程將應用適當的顏色格式。
Sub Colors()
Dim myMenu As Object
Dim mySubMenu As Object
Set myMenu = CommandBars("Worksheet menu bar").Controls("Format")

With myMenu
243
.Controls.Add(Type:=msoControlPopup, Before:=2).Caption = "Colors"

End With
Set mySubMenu = myMenu.Controls("Colors")

With mySubMenu
.Controls.Add(Type:=msoControlButton).Caption = "Red"
.Controls.Add(Type:=msoControlButton).Caption = "Green"

.Controls.Add(Type:=msoControlButton).Caption = "Blue"

.Controls.Add(Type:=msoControlButton).Caption = "Black"

.Controls("Red").OnAction = "ColorRed"

.Controls("Green").OnAction = "ColorGreen"

.Controls("Blue").OnAction = "ColorBlue"

.Controls("Black").OnAction = "ColorBlack"

End With
End Sub
Sub ColorRed()
ActiveCell.Font.Color = RGB(255, 0, 0)

End Sub
Sub ColorGreen()
ActiveCell.Font.Color = RGB(0, 255, 0)

End Sub
Sub ColorBlue()
ActiveCell.Font.Color = RGB(0, 0, 255)
End Sub
Sub ColorBlack()
ActiveCell.Font.Color = RGB(0, 0, 0)

End Sub
11.修改內置快顯功能表
Excel 提供了 60 來個快顯功能表,帶有不同的經常用到的功能表項目。當你在 Excel 應用程式視窗的某個對
象上按一下右鍵時,快顯功能表就會出現。通過使用 VBA,你可以返回快顯功能表的準確編號,還有它們
的名稱。
1. 在當前工程的模組裡輸入過程 ShortcutMenus,如下所示:
Sub ShortcutMenus()
Dim myBar As CommandBar
Dim counter As Integer
For Each myBar In CommandBars
If myBar.Type = msoBarTypePopup Then

counter = counter + 1
Debug.Print counter & ": " & myBar.Name

End If
Next
End Sub
注意, 要使用常量 msoBarTypePopup 來確定 CommandBars 集合裡的快顯功能表類型。使用
常量
msoBarTypeMenuBar,可以返回內置功能表的名稱。
當你運行過程 ShortcutMenus 後,快顯功能表的名稱就會列印在立即視窗裡,這裡也列出來了。
Excel 2002 的內置快顯功能表:
1: Query and Pivot
2: PivotChart Menu
3: Workbook tabs
Excel 2003 的內置快顯功能表:
1: Query and Pivot
2: PivotChart Menu
3: Workbook tabs
244
4: Cell
5: Column
6: Row
7: Cell
8: Column
9: Row
10: Ply
11: XLM Cell
12: Document
13: Desktop
14: Nondefault Drag and Drop
15: AutoFill
16: Button
17: Dialog
18: Series
19: Plot Area
20: Floor and Walls
21: Trendline
22: Chart
23: Format Data Series
24: Format Axis
25: Format Legend Entry
26: Formula Bar
27: PivotTable Context Menu
28: Query
29: Query Layout
30: AutoCalculate
31: Object/Plot
32: Title Bar (Charting)
33: Layout
34: Pivot Chart Popup
35: Phonetic Information
36: Auto Sum
37: Paste Special Dropdown
38: Find Format
39: Replace Format
40: Shapes
41: Inactive Chart
42: Excel Control
43: Curve
44: Curve Node
45: Curve Segment
46: Pictures Context Menu
47: OLE Object
48: ActiveX Control
49: WordArt Context Menu
50: Rotate Mode
51: Connector
52: Script Anchor Popup
4: Cell
5: Column
6: Row
7: Cell
8: Column
9: Row
10: Ply
11: XLM Cell
12: Document
13: Desktop
14: Nondefault Drag and Drop
15: AutoFill
16: Button
17: Dialog
18: Series
19: Plot Area
20: Floor and Walls
21: Trendline
22: Chart
23: Format Data Series
24: Format Axis
25: Format Legend Entry
26: Formula Bar
27: PivotTable Context Menu
28: Query
29: Query Layout
30: AutoCalculate
31: Object/Plot
32: Title Bar (Charting)
33: Layout
34: Pivot Chart Popup
35: Phonetic Information
36: Auto Sum
37: Paste Special Dropdown
38: Find Format
39: Replace Format
40: Shapes
41: Inactive Chart
42: Excel Control
43: Curve
44: Curve Node
45: Curve Segment
46: Pictures Context Menu
47: OLE Object
48: ActiveX Control
49: WordArt Context Menu
50: Rotate Mode
51: Connector
52: Script Anchor Popup
245
53: Canvas Popup
54: Organization Chart Popup
55: Diagram
56: Add Command
57: Built-in Menus
58: System
59: Layout
60: Select
53: Canvas Popup
54: Organization Chart Popup
55: Diagram
56: Layout
57: Select
58: List Range Popup
59: List Range Layout Popup
60: XML Range Popup
61: List Range Layout Popup
62: Built-in Menus
現在,你已經知道裡 Excel 快顯功能表的準確名稱了,你可以輕易地添加其它經常用到的命令到這些
功能表中的任意功能表中去。儘管從工具列上點擊打印圖示或者選擇檔|列印來列印工作表都是很容
易的事情,但是你可能還是想將該列印命令添加到快顯功能表中去,當你在工作表標籤上按一下右鍵時
就會出現在該快顯功能表上。我們來看看如何添加該選項到 Ply 功能表上去。
2. 輸入如下所示地過程 AddToPlyMenu:
Sub AddToPlyMenu()
With Application.CommandBars("Ply")

.Reset
.Controls.Add(Type:=msoControlButton, Before:=2).Caption = _

"Print..."
.Controls("Print...").OnAction = "PrintSheet"

End With
End Sub
上面所用地 Reset 方法避免當你多次運行該過程時,將同樣的選項放置到該快顯功能表上。
3. 運行過程 AddToPlyMenu,然後返回到代碼視窗,並且輸入下述過程,當你從該快顯功能表上選擇
Print 選項時,就會執行該過程:
Sub PrintSheet()
Application.Dialogs(xlDialogPrint).Show

End Sub
4. 切換到 Excel 應用程式視窗,並且在任何工作表標籤上按一下右鍵,選擇 Print 選項,你應該可以
看到當你使用其它內置工具列印時看到的相同的對話方塊。
圖 12-10 自訂選項可以添加到內置快顯功能表上(參見 Print 選項在過程 AddToPlyMenu 裡被添加上
了)
246
12.創建快顯功能表
1. 在當前 VBA 工程的代碼視窗裡輸入過程 Create_ShortMenu,如下所示:
Sub Create_ShortMenu()
Dim sm As Object
Set sm = Application.CommandBars.Add("Information", msoBarPopup)

With sm
.Controls.Add(Type:=msoControlButton).Caption = "Operating System"

With .Controls("Operating System")


.FaceId = 1954
.OnAction = "OpSystem"
End With
.Controls.Add(Type:=msoControlButton).Caption = "Total Memory"

With .Controls("Total Memory")


.FaceId = 1977
.OnAction = "TotalMemory"
End With
.Controls.Add(Type:=msoControlButton).Caption = "Used Memory"

With .Controls("Used Memory")


.FaceId = 2081
.OnAction = "UsedMemory"
End With
.Controls.Add(Type:=msoControlButton).Caption = "Free Memory"

With .Controls("Free Memory")


.FaceId = 2153
.OnAction = "FreeMemory"
End With
End With
End Sub
上面的過程創建了一個名為 Information 的自訂快顯功能表,並給它添加了四個命令。注意,
每個命令都指定了一個圖示。當你從該快顯功能表選擇一命令,步驟 2 裡面的相應過程就會被執
行。
2. 輸入下面為 Create_ShortMenu 程序呼叫的過程:
Sub FreeMemory( )
MsgBox Application.MemoryFree & " bytes", , "Free Memory"

End Sub
Sub OpSystem( )
MsgBox Application.OperatingSystem, , "Operating System"

End Sub
Sub TotalMemory( )
MsgBox Application.MemoryTotal, , "Total Memory"

End Sub
Sub UsedMemory( )
MsgBox Application.MemoryUsed, , "Used Memory"

End Sub
要將名為 Information 的自訂快速鍵顯示在螢幕上的話,你可以使用方法 ShowPopup,如步
驟 3 所示。
3. 在立即視窗裡輸入下述語句:
CommandBars("Information").ShowPopup 0, 0

物件 CommandBar 的方法 ShowPopup 接受兩個可選參數(x, y),決定快顯功能表在螢幕上的位置。


在上面的例子裡,快顯功能表 Information 將出現在螢幕的左上角。
247
假設你正在設計一個自訂表單,並想要當使用者按右鍵一個命令按鈕時顯示一個快顯功能表:
1. 從 VB 編輯器功能表上,選擇插入-使用者表單
2. 使用工具箱上的命令控制項,在空白使用者表單的任意位置放置一個按鈕
3. 通過點擊工程流覽器視窗的查看代碼按鈕,切換到該表單的代碼視窗
4. 在 UserForm1 代碼視窗裡輸入下述過程:
Private Sub CommandButton1_MouseDown(ByVal Button _

As Integer, _
ByVal Shift As Integer, _
ByVal X As Single, _
ByVal Y As Single)
If Button = 2 Then
Call Show_ShortMenu
Else
MsgBox "You must right-click this button."

End If
End Sub
當使用者右鍵點擊表單上按鈕時,該過程就會調用過程 Show_ShortMenu。點擊滑鼠按鈕時 VB 會有兩個
事件程序回應。當你點擊滑鼠按鈕時,VB 會執行 MouseDown 事件程序,當你釋放滑鼠按鍵,MouseUp
事件則會發生。
MouseDown 和 MouseUp 事件程序要求下述參數:
• 參數 object 確定物件,在該例中,是表單上面的命令按鈕名稱
• 參數 Button 是整型資料,確定按下的是哪個滑鼠按鍵
Button 參數值
1
2
3
意義
滑鼠左鍵
滑鼠右鍵
滑鼠中鍵
• 參數 Shift 確定當事件發生時,用戶是否按住了 Shift, Crel 或者 Alt 鍵。
Shift 參數值
1
2
3
4
5
6
7
意義
Shift 鍵
Ctrl 鍵
Shift 和 Ctrl 鍵
Alt 鍵
Alt 和 Shift 鍵
Alt 和 Ctrl 鍵
Alt, Shift 和 Ctrl 鍵
5. 在當前工程模組裡輸入過程 Show_ShortMenu 代碼:
Sub Show_ShortMenu()
Dim shortMenu As Object
Set shortMenu = Application.CommandBars("Information")

With shortMenu
.ShowPopup
End With
End Sub
注意,本過程使用的方法 ShowPopup 沒有使用決定快顯功能表螢幕顯示位置的可選參數,因此,
功能表將出現在滑鼠點擊的位置(見圖 12-11)。
6. 要刪除名為 Information 的快顯功能表的話, 請在代碼視窗輸入並且運行
下述過程
Delete_ShortMenu:
Sub Delete_ShortMenu()
Application.CommandBars("Information").Delete

End Sub
248
圖 12-11 當你右鍵點擊一個物件時出現的自訂快顯功能表
13.接下來……
在本章,你學習了如何使用 VBA 修改內置功能表和工具列,如何創建和顯示你自己的工具列,功能表和
快顯功能表。當使用功能表和工具列時,你使用了物件 CommandBar 的各種屬性和方法。你學習了三種類
型的對象 CommandBar:Normal,MenuBar 和 Popup。使用立即視窗,你試驗了示範如何創建自己的工
具欄和控制項。
下一章將帶你進入錯誤捕捉和調試,換句話說,你將學習當你的程式工作不正確時,該做些什麼。
第十三章調試 VBA 過程和處理錯誤
錯誤要悄悄混入你的 VBA 過程很容易,事實是,無論你多麼仔細,你所有的 VBA 過程第一次就能全部
運行正確,這是極其少見的事。總有一些事情你錯過了或者沒有想到過。從第二章起,你就知道有
三種類型的 VBA 錯誤:語法錯誤,邏輯錯誤和執行時間錯誤。本章將給介紹許多內置工具,你會發
現它們在你的過程代碼的分析和定位錯誤源的過程中是很有用的。
1.測試 VBA 過程
迄今為止,在本書中,你已經創建和執行了很多過程和函數例子。因為這些程式中的大多數都很短,
所以找錯誤並不是非常困難。然而,當你編寫更長更複雜的過程時,查找錯誤源就更緩慢和費時了。
幸運的是,VBA 編輯器提供了一套方便的工具,讓你追蹤你 VBA 問題的過程更簡單,更快捷,有更少
的挫折。程式缺陷是電腦程式中的錯誤,而調試則是定位和解決這些錯誤的過程。調試讓你找到你
的程式為什麼不按預期工作的原因。你可以通過步入程式碼或者檢查變數值來達到目的。
使用下述指南進行你的 VBA 程式調試:
• 如果你想要分析你的過程,通過按 F8 或者選擇調試-逐語句,逐語句地執行你的代碼
• 如果你懷疑程式的某個地方有錯誤發生,那麼可以使用中斷點
• 如果你想監測程式中某個變數或者運算式的值,那麼可以添加一個監視運算式
• 如果你討厭在冗長的程式碼中拉動捲軸到你感興趣的部分去,那麼你可以設置一個書簽,
快速跳到需要的地方。
每條指南在本章中都有實用的例子進行示範。
2.終止過程
你知道如何終止 VB 過程嗎?如果你想到了按 Esc 鍵,那麼你對了。如果你在運行程式,並且突然按
下 Esc 鍵,那麼 VB 就會中斷程式的運行,並顯示如圖 13-1 顯示的資訊。然而除了 Esc 鍵,這個在很多
情況下都很有力而且可靠的方法,VBA 還提供了很多其它的方法來中斷你的過程,進入所謂的中斷
模式:
• 按 Ctrl+Break
• 設置一個或多個中斷點
• 插入 Stop 語句
• 添加監視運算式
當你的程式執行被臨時停止時,中斷點便發生了。VB 會從過程的執行中記住所有變數和語句的值,當
使用者從工具列點擊運行宏(或者“運行”菜單上的相同名稱),或者點擊對話方塊(圖 13-1)上面的
繼續按鈕,可以恢復。
249
圖 13-1 在你運行程式時,如果你按下 Esc 鍵或者 Ctrl+Break 鍵,就會出現該資訊
圖 13-1 顯示的錯誤對話方塊通知你該過程已被中斷,下述按鈕可用:
繼續
結束
調試
幫助
點擊該按鈕可以恢復代碼執行。如果遇到錯誤該按鈕將變灰
如果你這次不想排除故障則點擊該按鈕,VBA 將終止代碼執行
點擊該按鈕進入中斷模式。代碼視窗將出現,並且 VBA 會加亮過程執行時停止
處的代碼行。你可以檢查,調試,中斷或者逐句執行代碼。注意,當 VBA 工程
被保護時,該按鈕變灰
點擊該按鈕查看線上說明,解釋該錯誤資訊的導致原因
技巧 13-1 防止用戶干預
通過將下述語句加入到過程代碼中去,你可以防止使用者中斷你的程式:
Application.EnableCancelKey = xlDisabled

當使用者在程式運行時按下 Esc 或者 Ctrl+Break 時,不會發生任何情況。應用程式物件的屬性


EnableCancelKey 禁用了這些鍵。
3.使用中斷點
如果你多少知道點你程式碼有問題,那麼你應該在那裡(給定的行)暫停代碼執行。要設置中斷點,
簡單點就是當游標位於目標代碼行時按下 F9。當過程運行中 VBA 到達此處時,立即會顯示代碼視窗。
這時,你可以通過按 F8 或者選擇調試-逐語句來一行一行地運行代碼。
我們來看看下面一個方案,看看它是如何工作的。假設在過程 ChangeCode 的執行中下面的代碼行會
出問題:
ActiveCell.Formula = "=VLookup(RC[1],Codes.xls!R1C1:R6C2,2)"

1. 準備好如圖 13-2 和 13-3 所示的試算表。保存圖 13-2 所示的資料為 Chap13.xls,圖 13-3 所示的


數據為 Codes.xls。關閉文件 Codes.xls。
圖 13-2 本檔中輸入在 D 列的編號將會在過程 ChangeCode 中被顯示於土 13-3 中的編號所代替
250
2.
圖 13-3 過程 ChangeCode 使用該編號表作查找目的
3. 啟動文件 Chap13.xls,切換到 VB 編輯器視窗
4. 使用屬性視窗重新命名 VBAProject(Chap13.xls)為 Debugging
5. 插入模組到 Debugging (Chap13.xls)工程,並且更改名 Name 屬性為 Breaks
6. 輸入過程代碼 ChangeCode,如下所示:
Sub ChangeCode()
Workbooks.Open FileName:="C:\Codes.xls" ‘將此處檔路徑改為你的真實路徑
Windows("Chap13.xls").Activate
Columns("D:D").Select
Selection.Insert Shift:=xlToRight
Range("D1").Select
ActiveCell.Formula = "Code"
Columns("D:D").Select
Selection.SpecialCells(xlBlanks).Select

ActiveCell.Formula = "=VLookup(RC[1],Codes.xls!R1C1:R6C2,2)"

Selection.FillDown
With Columns("D:D")
.EntireColumn.AutoFit
.Select
End With
Selection.Copy
Selection.PasteSpecial Paste:=xlValues

Rows("1:1").Select
With Selection
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.Orientation = xlHorizontal
End With
Workbooks("Codes.xls").Close
End Sub
7. 在過程 ChangeCode 裡,點擊下述語句行的任意地方:
ActiveCell.Formula = "=VLookup(RC[1],Codes.xls!R1C1:R6C2,2)"
8. 按下 F9(或者選擇調試-切換中斷點)來在游標所在處設置一個中斷點。設置中斷點的另外一種方法
是點擊你要暫停程式的代碼左邊的頁邊。同時,帶中斷點的代碼行顯示為白字紫紅底色(見圖
13-4)。中斷點的顏色可以在選項對話方塊(工具功能表)的編輯器格式頁上更改。
251
圖 13-4 設置了中斷點的代碼行顯示了選項對話方塊裡編輯器格式設定的顏色
9. 運行過程 ChangeCode。當你運行該過程時,VB 將執行所有的語句,直到它遇到該中斷點。一旦遇
到中斷點,代碼便暫停了,並且螢幕顯示代碼視窗(見圖 13-5)。VB 在該語句左邊的頁邊上顯示
一個黃色箭頭。同時,該語句出現在一個黃色底色的框裡面。錯誤和框表明當前語句將要被執
行。如果當前語句也包含一個中斷點,那麼頁邊將重疊顯示它們(圓圈和箭頭)。
圖 13-5 當 VB 遇到中斷點時,就會顯示代碼視窗並且指定當前語句
10. 按 F8,或者選擇調試-逐語句
11. 重複幾次步驟 9 的指令
12. 按 F5(或者選擇運行宏)來繼續運行該過程,不必逐語句運行該過程。當你運行完該過程後,
VB 不會自動刪除中斷點。注意帶有 VLookup 函數的代碼行還是加亮的。
在本例中,你只設置了一個中斷點。VB 允許你在一個過程裡設置任意多個中斷點。這樣,你就可以
隨心所欲地暫停和繼續你過程的執行了。你可以分析你的程式碼、檢查執行暫停時變數的值。
你也可以通過在立即視窗裡敲入語句進行各種各樣的測試。
13. 通過選擇調試-清除所有中斷點,或者按下 Ctrl+Shift+F9,可以清除中斷點。
所有中斷點都被清除了。如果你在某個過程裡面設置了多個中斷點,並且想要只清除其中的一個或
252
幾個,那麼可以點擊你想要清除中斷點的代碼行並且按下 F9(或者選擇調試-切換中斷點)。當中斷點
不再需要時,你應該清除它們。當你關閉檔時,所有中斷點將自動被清除。
技巧 13-2 什麼時候使用中斷點
如果你懷疑你的過程根本就沒有執行過某段代碼塊的話,那麼設置中斷點。
4.在中斷模式下使用立即視窗
一旦程式執行中斷,當代碼視窗出現時,你可以啟動立即視窗並且輸入 VBA 指令,例如,來查明哪
一個儲存格是當前活動的或者活動工作表名稱是什麼。你也可以使用立即視窗來更改變數的內容,
以便改正可能導致錯誤的值。到現在,你應該已經是個使用立即視窗的專家了。圖 13-6 顯示了該暫
停的過程 ChangeCode,和在中斷模式下,向 VB 問問題的立即視窗。
在中斷模式下,你可以很快地查明代碼視窗裡變數的內容。只要在運行的過程中簡單地將游標移動
到變數上,就可以知道該變數的內容了。例如圖 13-7 裡所示的 VarValue 過程,中斷點設置在第二次出
現的 Workbooks.Add 語句上。
圖 13-6 當代碼執行被暫停時,通過輸入適當的語句到立即視窗裡,你就可以找到很多問題的答案
圖 13-7 在中斷模式下,你可以將滑鼠指向某個變數,查明該變數的值
253
當 VB 遇到該語句,代碼視窗(中斷模式)就會出現。因為 VB 已經執行了那條語句,將當前活動工作
簿名稱儲存到變數 myName(譯者:原文為 myBook),所以你將滑鼠指向該變數名稱時,就可以查明
該變數的值。該變數名稱及其當前值出現在文字框裡面。要同時顯示過程裡使用的多個變數的值的
話,你就應該使用當地視窗,本章的後面將會討論到。
5.使用 Stop 語句
有時候,你不能馬上測試你的程式,如果你設置了中斷點,然後關閉該檔,那麼 Excel 就會清除你
的中斷點,而下次當你準備測試程式時,你將不得不再次設置你的中斷點。如果你需要推遲測試工作,
那麼你可以使用不同的方式,簡單的在你需要暫停程式的地方插入一個 Stop 語句。圖 13-8 顯示了
For…Next 迴圈之前的 Stop 語句。當 VB 遇到 Stop 語句,它就會暫停過程 StopExample 的執行,螢幕會
顯示中斷模式下的代碼視窗。儘管 Stop 語句和設置中斷點具有完全一樣的效果,但是,它有一個弱點
——所有 Stop 語句都留在程式裡,直到你一個一個清除它們。當你不再需要暫停程式時,你必須定
位並且清除所有的 Stop 語句。
圖 13-8 你可以在 VBA 過程代碼的任何地方插入 Stop 語句,當程式到達 Stop 語句時就會暫停,並且出
現代碼視窗,加亮該行
技巧 13-3 在中斷模式下在代碼視窗內部工作
在中斷模式下,你可以改變代碼、添加新語句、每次執行一行語句、跳過代碼行、設置下一條語句、
使用立即視窗、以及更多。當 VB 處於中斷模式時,調試功能表上所有的選項都可用了。你可以通過按
下 Esc,Ctrl+Break,或者通過設置中斷點,進入中斷模式。
當你在中斷模式下時,如果你更改某些代碼,VBA 將會提示你重新設置工程,顯示如下錯誤資訊:
“該操作將重新設置工程,繼續嗎?”你可以點擊確定,終止程式執行並繼續編輯你的代碼,或者
點擊取消,刪除新變化並從中中斷點繼續運行代碼。要查看該錯誤的話,可以將程式進入中斷模式,
然後更改變數的聲明,當你按下 F5 恢復代碼執行的時候,VBA 就會提示你重新設置你的工程。
6.添加監視運算式
程式中的許多錯誤是由變數獲得未預期的值導致的。如果某個過程使用了一個變數,在不同的地方
有不同的值,你可能想要停止程式查看該變數的當前值。VB 提供了一個特別的監看視窗,允許你在
過程運行時密切注視變數或者運算式。
進行下述操作,給你的過程添加監視運算式:
1. 在代碼視窗,選擇你想要監視的變數
2. 選擇調試-添加監視
螢幕上會顯示添加監視對話方塊,如圖 13-9 所示。
添加監視對話方塊包含三部分,描述在下表中:
運算式顯示你的過程中加亮變數的名稱。如果你打開添加監視對話方塊時沒有選擇
254
上下文
監視類型
變數名稱,那麼需要輸入你想要監視的變數名稱到運算式文字框裡
在該節,你應該指明包含該變數的過程名稱和該過程所在的模組名稱
明確如何監視該變數。如果你選擇“監視運算式”選項按鈕,那麼你將在
中斷模式下能夠在監看視窗裡查看該變數的值。當你選擇“當監視值為真
時中斷”選項按鈕,那麼當該變數值為真(非零)時 VB 將自動停止過程。
最後一個選項按鈕,“當監視值改變時中斷”,每當該變數或運算式的值改
變時,過程就會停止
圖 13-9 添加監視對話方塊允許你定義在 VBA 過程運行時監視的情況
你可以在運行過程之前或者在過程執行中斷之後添加監視運算式。
中斷點和監視運算式之間的區別是中斷點總是將過程中斷在某個特定的位置,而監視運算式則是當特定
情況(監視值為真中斷或者監視值改變時中斷)時中斷過程。當你不確定變數在哪兒改變時,監視
是極其有用的。你可以簡單地添加一個監視中斷點在某個變數上並正常運行過程,而不必在這麼多行
代碼裡逐語句來找到變數在那裡獲取該特定的值。我們來看看這是如何實現的。
1. 準備如圖 13-10 所示的過程
圖 13-10 使用監看視窗
過程 WhatDate 使用 For…Next 迴圈來計算將來 x 天后的日期。如果你運行該過程,你不會得到任何結
果,除非你在過程裡插入下述指令:
MsgBox “In “ & x & “ days, it will be “ & NewDate
然而,這次,你不想一天一天地顯示每一個日期。假如你想要當變數 x 等於 160(譯者:翻譯時恰好
用到 160 這個數,原文是 211)的時候停止該程式,換句話說,你想要知道現在 160 天后是哪一天。
255
要得到結果的話,你可以插入下述語句到過程裡:
If x = 160 Then MsgBox "In " & x & " days it will be " & NewDate

假設你想要不輸入任何新語句來得到結果,你如何做呢?如果你添加了監視運算式的話,當滿足特
定條件時,VB 就會停止 For…Next 迴圈,然後,你就可以查看想要的變數值。
1. 選擇調試-添加監視
2. 在運算式文字框裡輸入下述運算式:x=160
3. 在上下文部分,從過程下拉清單裡選擇 WhatDate,從模組下拉清單裡選擇 Breaks
4. 在監視類型部分,選擇“當監視值為真時中斷”選項按鈕
5. 點擊確定,關閉添加監視對話方塊,現在,你已經添加了你的第一個監視運算式
6. 在代碼視窗,將游標放在變數 curDate 內部的任意地方
7. 選擇調試-添加監視,並點擊確定,設置缺省的監視類型
8. 在代碼視窗,將游標放在變數 newDate 內部的任意地方
9. 選擇調試-添加監視,並點擊確定,設置缺省的監視類型
做完上面的步驟後,過程 WhatDate 包含了下述三個監視:
x = 160 當監視值為真時中斷
curDate 監視運算式
newDate 監視運算式
10. 將游標放在過程 WhatDate 代碼的任意地方,並且按下 F5,VB 在 x=160 的時候停下來了(見圖
13-10)
注意,變數 x 在監看視窗的值和你在添加監視對話方塊裡指定的值一樣。另外,監看視窗顯示了
變數 curDate 和 newDate 的值。該過程處於中斷模式。你可以按 F5 繼續或者你可以問另一個問題:
277 天后是哪一天?下一步將示範如何做。
11. 選擇調試-編輯監視,然後輸入下述運算式:x=227。你可以通過按兩下監看視窗裡的運算式,快
速顯示編輯監視對話方塊
12. 點擊確定,關閉編輯監視對話方塊。注意,現在監看視窗顯示運算式的新值,現在 x 為 False。
13. 按 F5,當 x 值為 227 時過程再次停止。curDate 的值相同,但是變數 newDate 現在有了一個新的值
——現在之後 277 天的日期。你可以再次改變運算式的值,或者結束該過程。
14. 按 F5 以完成該過程。當你的過程正在運行,並且監視運算式有值,監看視窗就會顯示該監視表
達式的值,如果你在過程運行結束後打開監看視窗的話,那麼你將看到<溢出上下文>,而不是
變數值了。換句話說,當監視運算式溢出上下文時,它沒有值。
7.清除監視運算式
在監看視窗裡,點擊你要清除的運算式並且按下 Delete。清除你先前定義的所有監視運算式。
8.使用快速監視
如果你想查看一個運算式的值,但是你還沒有定義監視運算式,那麼你可以使用快速監視(見圖
13-11)
圖 13-11 快速監視對話方塊顯示 VBA 過程裡所選運算式的值
可以通過下述方法獲取快速監視對話方塊:
- 在中斷模式下,將游標放在你要監視的變數名稱或者運算式內部
- 選擇調試|快速監視,或者按下 Shift+F9
快速監視對話方塊上面有個添加按鈕,允許你在監看視窗裡添加運算式。
確保過程 WhatDate 裡不含有任何監視運算式,參見前面的章節有關如何從監看視窗清除監視運算式
的內容。現在我們通過例子來看看如何利用快速監視。
1. 在過程 WhatDate 裡,將插入點(游標)放在變數 x 處
256
2. 選擇調試|添加監視
3. 輸入下述運算式:
x = 50
4. 選擇當監視值為真時中斷,並點擊確定
5. 運行過程 WhatDate
當 x 等於 50 時 VB 將中斷過程的執行,注意,監看視窗裡沒有變數 newDate 和 curDate。想要查看
這些變數的值的話,那麼你可以將游標放在代碼視窗裡相應變數名稱上,或者,你也可以調用
快速監看視窗。
6. 在代碼視窗裡,將滑鼠游標放在變數 newDate 上並且按下 Shift+F9。快速監看視窗就會顯示該
運算式名稱和其當前值
7. 點擊取消返回代碼視窗
8. 在代碼視窗,將滑鼠游標放在變數 curDate 上並且按下 Shift+F9。現在,快速監看視窗就會顯
示變數 curDate 的值了
9. 點擊取消返回代碼視窗
10. 按下 F5 繼續運行該過程
9.使用本地視窗和調用堆疊對話方塊
如果在 VBA 過程的執行過程中,你想密切注視所有聲明的變數和它們的當前值,那麼確保你在運行
該過程前選擇視圖|本地視窗。當在中斷模式下時,VB 就會顯示一系列的變數和它們相應的數值在
本地視窗裡(參見圖 13-12)
圖 13-12 本地視窗顯示當前 VBA 過程裡所有聲明的變數和它們當前值
本地視窗包含三列,運算式列顯示聲明在當前過程裡的變數名稱。第一行顯示前面帶加號的模組名
稱,當你點擊該加號,你就可以查看是否有變數聲明在模組層級。類別模組將顯示系統變數 Me。在本地
視窗,全域變數和被其它工程使用的變數不會顯示出來。
第二列顯示變數的當前值,在本列,你可以更改變數的值,只要點擊它並輸入新的值。更改了數值
後,按下回車鍵以記錄該變化。你也可以在更改數值後,按 Tab 鍵,Shift+Tab 鍵或者向上或向下箭
頭,或者也可以點擊本地視窗的其它任意地方。第三列顯示每個聲明了的變數的類型。
想要觀察本地視窗裡的變數值的話,請跟著做:
1. 選擇視圖|本地視窗
2. 點擊過程 WhatDate 裡的任意地方,並按 F8,你將過程置於中斷過程了。本地視窗顯示了當前模
塊的名稱,以及當地變數和它們的初始值
3. 按幾下 F8 鍵,密切關注本地視窗
4. 按 F5 鍵繼續運行該過程
本地視窗也包含一個帶三個點的按鈕,該按鈕將打開調用堆疊對話方塊(參見圖 13-13),它顯示所有
257
活動調用過程的清單。活動的調用過程是指已經開始但是還沒有完成的過程。你也可以通過選擇視
圖|調用堆疊,該選項只有在中斷模式下才是可用的。
圖 13-13 調用堆疊對話方塊顯示了開始但未完成的過程列表
調用堆疊對話方塊特別是在追蹤嵌套的程式時有用。回想一下,嵌套過程是一個被另一個程序呼叫的
過程。如果一個程序呼叫另一個過程,該被調用的過程名稱就會自動添加到調用堆疊對話方塊裡的調
用列表。當 VB 執行完該被調過程後,該過程名稱就會從調用堆疊對話方塊裡自動清除。你可以使用調
用堆疊對話方塊上的顯示按鈕,顯示調用下一個過程的語句。
10.逐句運行 VBA 過程
逐句運行代碼意思是每次只運行一條語句,這樣,可以允許你檢查遇到的每一個過程裡的每一條語
句。想要從頭開始逐句運行過程的話,可以將插入點置於過程代碼的任意地方,並且選擇調試|逐
語句,或者按下 F8。調試功能表包含好幾個選項供你在逐步模式下執行(參見圖 13-14)
圖 13-14 調試功能表提供了許多命令以逐步 VBA 過程
258
當你每次運行一條語句時,VB 將會執行每條語句,直到它碰到關鍵字 End Sub。如果你不希望 VB 逐
句運行的話,那麼你隨時可以按下 F5 來運行過程中剩餘的代碼,而不必逐步運行。
11.逐句運行過程
1. 將插入點置於你想要追蹤的過程中代碼的任意地方
2. 按下 F8 或者選擇調試|逐語句。VB 就會執行當前語句,並且自動跳到下一句並中斷執行。在中
斷模式下,你可以啟動立即視窗,監看視窗或者本地視窗,查看某特定語句中變數和運算式的
值。以及,如果你正在逐語句執行的程序呼叫了其它過程,那麼你也可以啟動調用堆疊視窗來
查看當前哪些過程是活動的
3. 再次按下 F8,執行被選中的語句。執行完該語句後,VB 會選中下一條語句,並且該過程將再次
中斷
4. 按下 F8 繼續逐語句執行該過程,或者按 F5 無停止的執行完剩餘的代碼。你也可以選擇運行|重
新設置來終止該過程的執行,而不執行剩下的語句
當你逐過程運行某個過程(Shift+F8)時,VB 將一次執行一個過程,好像裡面只有一條語句一樣。
如果某程序呼叫了其它過程,並且你並不像逐語句執行這些過程,因為你已經測試過了,或者因為
你只想側重於尚未被調試的新代碼,那麼該選項特別有用。
12.逐過程執行過程
假設過程 MyProcedure 的當前語句調用過程 SpecialMsg。如果你選擇調試|逐過程(Shift+F8),而
非調試|逐語句(F8),那麼 VB 就會快速地執行過程 SpecialMsg 裡面的所有語句,並且選擇主調過程
MyProcedure 裡的下一條語句。在過程 SpecialMsg 的執行期間,VB 將繼續顯示當前過程於代碼視窗。
1. 在當前模組裡輸入下述過程:
Sub MyProcedure()
Dim myName As String
Workbooks.Add
myName = ActiveWorkbook.Name
‘ choose Step Over to avoid stepping through the
‘ lines of code in the called procedure - SpecialMsg
SpecialMsg myName
Workbooks(myName).Close
End Sub
Sub SpecialMsg(n As String)
If n = "Book2" Then
MsgBox "You must change the name."
End If
End Sub
2. 在下面語句處添加一個中斷點:
SpecialMsg myName
3. 將插入點置於過程 MyProcedure 的代碼中,並按下 F5 運行它。VB 到達中斷點時將中斷執行
4. 按下 Shift+F8,或者選擇調試|逐過程。VB 將會快速的運行過程 SpecialMsg 並且跳到緊挨著調
用過程 SpecialMsg 的語句下面的那條語句
5. 按下 F5 無間斷地完成過程的運行
當你不要分析被調過程中具體語句的話,逐過程執行是非常有用的。
調試功能表上的另外一個命令,跳出(Ctrl+Shift+F8),當你步入了某個過程,然後決定不繼續逐步
執行它,那麼就可以使用該命令。當你喧雜該選項時,VB 就會一步執行完該過程裡的剩餘語句,然
後繼續去啟動主調過程中的下一條語句。
在逐步運行過程期間,你可以在逐語句,逐過程和跳出選項之間切換。選擇哪個取決於這時你想要
分析哪個代碼片斷。
調試功能表中運行到游標處(Ctrl+F8)命令讓你運行過程,直到碰到你選中的行。如果你想要在執
行一個大循環之前停止,或者想要跳過一被調過程時,該命令非常有用。
假設你想要執行過程 MyProcedure 到調用過程 SpecialMsg 的行。
259
1. 點擊語句 SpecialMsg myName 內部
2. 選擇調試|運行到游標處。當到達特定行時,VB 將停止執行
3. 按下 Shift+F8 以逐過程地運行過程 SpecialMsg
4. 按下 F5 無間斷地執行完剩下的代碼
13.設置下一條語句
有時,你也許想要重新運行過程中前面的幾行代碼,或者想要跳過一段將導致麻煩的代碼。每遇到
這種情況,你可以使用調試功能表裡的設置下一條語句選項。當你中斷過程的執行時,你可以隨意恢
複任何語句。VB 將會跳過所選語句和中斷處語句之間的語句。假設在過程 MyProcedure(參見前面
部分的代碼)中,你已經在調用過程 SpecialMsg 的語句處設置了中斷點。要跳過過程 SpecialMsg 的執
行,你可以將游標置於語句 Workbooks(myName)內,關閉並按下 Ctrl+F9(或者選擇調試|設置下一
條語句)。除非你中斷了過程的執行,否則不能使用設置下一條語句選項。
技巧 13-4 跳過代碼行
儘管跳過代碼行在你的過程調試中非常有用,但是你得非常小心。當你使用下一條語句選項時,你
告訴 VB 這是你想要執行的下一條語句。中間的所有代碼行將被忽略,這意味著期間有很多你本預期
要發生的事情並沒有發生,這將可能導致意想不到的錯誤。
14.顯示下一條語句
如果你不肯定過程的執行會從哪裡繼續,那麼你可以選擇調試|顯示下一條語句,這樣 VB 就會將光
標放置到下次將運行的代碼行。當你正在看別的過程,不知道下面會執行哪條代碼的時候,該命令
尤其有用。顯示下一條語句選項僅在中斷模式下可用。
15.終止和重新設置 VBA 過程
任何時候在逐步過程代碼時,你可以:
• 按下 F5 無間斷地執行剩餘指令
• 選擇運行|重新設置來終止過程,而不執行剩下的語句
當你重新設置過程時,所有變數將丟失它們的當前值。數字型變數恢復為其初始值 0,變化長度的
字串變數初始化為 0 長度字串(””),而固定長度的字串用 ASCII 碼 0 代表的字元或者 Chr(0)
填充。Variant 型變數初始化為 Empty,物件變數則設置為 Nothing。
16.瞭解和使用條件編譯
當你第一次運行某個過程時,VB 會將你使用的 VBA 語句轉變為電腦能夠理解的機器碼。該過程被
稱為編譯。你也可以選擇調試|編譯(當前 VBA 工程名稱),在你運行該過程之前執行整個 VBA 工程的
編譯。
使用條件編譯,你可以告訴 VB 在編譯或者運行時包括或者忽略某些代碼塊。取決於你設置的條件,
你的過程可能會表現不同。例如,條件編譯用來編譯一個將會運行於不同平臺(Windows 或者
Macintosh,Win16 或者 Win32)上的應用軟體。條件編譯對於當地語系化使用於不同語言的應用軟體也
是很有用的。在條件編譯時排除的程式碼將從最終檔中忽略掉,因此,它對檔大小或程式功
效沒有影響。
要啟動條件編譯的話,你應該使用叫做指示的特殊運算式。首先,你需要使用#Const 指示聲明一個
布林值(True 或者 False)常量,接下來,你在#If . . .Then... #Else 指示中核實該常量。你需
要進行條件編譯的代碼部分必須包括在這些指示中。注意,關鍵字 If 和 Else 前面都帶有一個數字元
號(#)。
如果一部分代碼將要運行,那麼該條件常量必須設置為真(-1),否則為假(0)。在模組的聲明部
分聲明條件常量,例如:
#Const User = True
聲明名為 User 的條件常量。
在接下來的過程中,當名叫 verPolish 條件常量為 True 時,資料就顯示為波蘭語。過程 WhatDate 調
用函數 DayOfWeek,它基於提供的日前返回星期名稱。要用英語編譯該程式的話,你所要做的全部
就是將該條件常量改為 False,然後 VB 就會跳到#Else 指示後面的指令塊去。
1. 在當前 VBA 工程插入一個新模組,並重命名為 Conditional
260
2. 輸入下述過程和函數:
‘ declare a conditional compiler constant
#Const verPolish = True
Sub WhatDay()
Dim dayNr As Integer
#If verPolish = True Then
dayNr = WeekDay(InputBox(“Wpisz date, np. 01/01/2000”))
MsgBox “To bedzie “ & DayOfWeek(dayNr) & “.”
#Else
WeekdayName
#End If
End Sub
Function DayOfWeek(dayNr As Integer) As String

DayOfWeek = Choose(dayNr, “niedziela”, “poniedzialek”, “wtorek”, _


“sroda”, “czwartek”, “piatek”, “sobota”)
End Function
Function WeekdayName() As String
Select Case WeekDay(InputBox(“Enter date, e.g. 01/01/2000”))
Case 1
WeekdayName = “Sunday”
Case 2
WeekdayName = “Monday”
Case 3
WeekdayName = “Tuesday”
Case 4
WeekdayName = “Wednesday”
Case 5
WeekdayName = “Thursday”
Case 6
WeekdayName = “Friday”
Case 7
WeekdayName = “Saturday”
End Select
MsgBox “It will be “ & WeekdayName & “.”
End Function
3. 運行過程 WhatDay。因為條件常量(verPolish)在模組頂端已被設置為 True 了,所以,VB 將運
行波蘭版過程 WhatDay。它用波蘭語詢問用戶輸入日期並且將結果顯示為波蘭語。要運行代碼
的英語版的話,需要將常量 verPolish 設置為 False,然後重新運行過程
除了在模組頂部聲明條件編譯常量之外,你也可以選擇工具|(VBAProject)屬性(參見圖
13-15)。當你使用該屬性視窗,在條件編譯參數文字方塊裡輸入下述內容,以啟動過程 WhatDay
的英語版本:
verPolish = 0
如果還有更多的條件編譯常量的話,每個常量之間必須用冒號分割開。
4. 注釋掉模組上部的#Const verPolish 指示,並且在如圖 13-15 所示地屬性對話方塊裡輸入條件編
譯常量。然後運行過程 WhatDay,看看 Else 部分是如何執行給說英語的用戶的。
261
圖 13-15 條件編譯常量可以在模組上部也可以在屬性視窗聲明,但是,不能同時在兩個地方聲明
17.操縱書簽
在分析和回顧你的 VBA 程式的過程中,你經常會發現你自己跳進了某代碼區域。使用內置的書簽功
能,你可以輕易地標示你需要流覽的地方。設置書簽:
1. 點擊你想要定義為書簽的語句的任意地方
2. 選擇編輯|書簽|切換書簽(或者點擊編輯工具列上的切換書簽按鈕——參見圖 13-16)。VB 將在
語句左邊的邊界上放置一個藍色的圓角矩形。
圖 13-16 你可以使用書簽在經常要用的部分之間切換
你一旦設置了兩個或以上的書簽,就可以通過選擇編輯|下一書簽,或者簡單地點擊編輯工具列上
的下一書簽按鈕,在標誌的代碼處切換。你也可以在代碼視窗的任意地方按一下右鍵(譯者:沒有驗
證該快顯功能表),然後選擇快顯功能表上的下一書簽。要到前面的書簽那裡,則選擇上一書簽。
你隨時可以通過選擇編輯|書簽|清除所有書簽,或者點擊編輯工具列上的清楚按鈕來清除所有書
簽。要清除單個書簽的話,那麼只要點擊書簽的任意地方然後選擇編輯|書簽|切換書簽,或者點擊
編輯工具列上的切換書簽按鈕。
18.捕捉錯誤
沒有人第一次就編寫沒有錯誤的程式。當你創建 VBA 過程,你必須決定你的程式如何應對錯誤。許
多意想不到錯誤在運行時發生,例如,你的過程可能要試圖給一個工作簿一個已經打開的工作簿的
名稱。執行時間錯誤經常不是被程式師發現,而是被試圖做一些程式師沒有預測到的事情的用戶發
現。如果程式執行階段錯誤發生了,那麼 VB 將顯示一個錯誤資訊,並且程式終止。大多情況下 VB 顯示
的錯誤資訊對使用者來說很隱秘。你通過在你的 VBA 過程裡加入錯誤處理代碼,預防使用者經常看到運
行時間錯誤。這樣,當 VB 碰到錯誤,它就會顯示一個更友好更好理解的錯誤資訊,可能指導使用者如
何去改正錯誤,而不是簡單的顯示一個缺省的錯誤資訊。
如何在你的 VBA 過程裡實行錯誤處理呢?第一步,要將 On Error 語句放到你的程式裡。該語句告訴
VBA 當運行時發生錯誤應該做什麼,換句話說,VBA 使用 On Error 語句來啟動錯誤處理常式以捕捉
執行時間錯誤。取決於你的程式類型,你可以通過以下任何方式推出錯誤陷阱:Exit Sub, Exit
Function, Exit Property, End Sub, End Function 或者 End Property。你應該給每個過程寫一個
錯誤處理常式。
262
On Error 語句可以按下述方式之一使用:
On Error GoTo 標籤明確一個標籤,當錯誤發生時跳到該標籤。該標籤標示錯誤處理常式的
開始。錯誤處理是在你的應用軟體中用來捕捉錯誤並作出回應的程式。
該標籤必須和 On Error 語句出現在同一過程裡面
On Error Resume Next 當執行時間錯誤發生時,VB 將忽略該導致錯誤的代碼行,不顯示錯誤信
息,但是從下一行開始繼續運行程式
On Error GoTo 0 關閉程式裡的錯誤捕捉。當 VBA 運行該語句後,錯誤會被發現,但是沒有
錯誤陷阱在程式裡
技巧 13-5 是錯誤(Error)還是失誤(Mistake)
在程式設計中,錯誤與失誤並非相同的事情。失誤,比如錯誤拼寫,漏掉語句,放錯地方的引號或逗號,
或者給變數賦予了不匹配的值,通過適當的調試失誤是可以從程式中清除的。儘管你的程式沒有任
何失誤,但是,這並不意味著不會發生錯誤。錯誤是指一個事件或者操作沒有按預期工作。例如,
如果你的 VBA 程式需要訪問硬碟上某個具體的檔,但是某人將該檔刪除了或者移到別的地方去
了,不管什麼你總會得到一個錯誤。錯誤阻止程式完成具體任務。
下面顯示的過程 Archive 使用了錯誤處理常式(見過程的下部)。該過程使用內置的方法 SaveCopyAs,
將當前工作簿保存複件到一文件,而不修改該已打開的工作簿在記憶體中的情況。
1. 在當前工程裡插入一個新模組,並重命名為 Traps
2. 輸入過程 Archive,如下所示:
Sub Archive()
Dim folderName As String
Dim DriveA As String
Dim BackupName As String
Dim Response As Integer
Application.DisplayAlerts = False
On Error GoTo DiskProblem
folderName = ActiveWorkbook.Path
If folderName = "" Then
MsgBox "You can't copy this file. " & Chr(13) _

& "This file has not been saved.", _

vbInformation, "File Archive"


Else
With ActiveWorkbook
If Not .Saved Then .Save
DriveA = "A:"
MsgBox "Place a diskette in drive " & DriveA & _

" and click OK.", , "Copying to " & DriveA

BackupName = DriveA & .Name


.SaveCopyAs Filename:=BackupName
MsgBox .Name & " was copied to a disk in drive " & _

DriveA, , "End of Archiving"


End With
End If
GoTo ProcEnd
DiskProblem:
Response = MsgBox("There is no disk in drive A " & Chr(13) _

& "or disk in drive " & DriveA & " is not formatted ", _

vbRetryCancel, "Check Disk Drive")


263
If Response = 4 Then
Resume 0
Else
Exit Sub
End If
ProcEnd:
Application.DisplayAlerts = True
End Sub
聲明完變數後,過程 Archive 的語句 Application.DisplayAlerts = False 確保 VB 在運行時,不會
顯示自己的警告和資訊。下一條語句, On Error GoTo DiskProblem,明確了一個標籤,當發生錯
誤時跳過去。保存活動工作簿的路徑名稱存儲在變數 folderName 上。
當 VB 找不到該工作簿路徑時,就會假設該檔沒有保存並且顯示相應的資訊。接下來, VB 跳到 End If
之後的語句處,並執行指令 GoTo ProcEnd,指向僅在 End Sub 之前的 ProcEnd 標籤。注意,標籤帶
有一個冒號。VB 執行語句 Application.DisplayAlerts = True,恢復系統的內置警告和資訊。因為
沒有語句了,所以,過程結束。
如果活動工作簿的路徑不是空字串的話,那麼 VB 就會檢查該工作簿最近的更改是否已保存。如果
沒有,VBA 使用語句 If Not .Saved Then .Save 來保存活動工作簿。Saved 是工作簿物件的 VBA 屬性。
接下來,VB 將軟碟驅動名稱”A:”存儲到變數 DriveA 並且顯示資訊提示使用者插入軟碟。然後軟碟名
稱和活動工作簿名稱合併在一起,並且存儲到一個叫 BackupName 的變數上。
你應該知道,當往軟碟拷檔的時候,所有的事情都可能出錯。例如,軟盤機可能是空的,或者軟碟
未格式化或已經滿了。當 VB 檢測到一錯誤時,它就會跳到以標籤 DiskProblem 開始的代碼行去,並
且會顯示相應的資訊。如果使用者點擊了資訊框上的重試按鈕(值為 4),那麼 VB 就執行語句 Resume 0,
該語句就會將 VB 送到導致錯誤的語句那裡(.SaveCopyAs FileName: = BackupName),然後 VB 會再
次執行它。如果使用者點擊取消按鈕的話,VBA 就會執行語句 Exit Sub,過程結束。
如果軟盤機裡面的 A 盤沒有問題,那麼 VBA 就會複製活動工作簿到該軟碟,並且資訊框會通知使用者複製
操作已成功。
3. 運行幾次過程 Archive,每次回應不同的選項,確保盡可能多的可能性。使用你在本章學習的
多種調試技術。
技巧 13-6 程式測試
你對你編寫的代碼負責,這意味著你在發佈你的程式給其他人測試之前,你自己先測試它。畢竟,
你瞭解它應該如何工作。有些程式師認為測試他們自己的代碼是一種降格的事情,特別是當他們在
一個有專門測試部門的組織中工作的時候。不要犯這種錯誤。程式師級別的測試過程是非常重要的,
如同編寫代碼本身一樣。在你自己測試完過程後,你應該給用戶們去測試。用戶會為你的問題,如:
程式能產生預期的結果嗎?用起來容易並且有趣嗎?符合標準習慣嗎?再有,將你的整個應用軟體
交給某個懂得一些使用該種應用軟體的人,請他使用並試圖打破它。
我們來看看另外一個程式例子,下面顯示的過程 OpenToRead 示範了 Resume Next 和 Error 語句的使
用,以及 Err 物件。
Sub OpenToRead()
Dim myFile As String
Dim myChar As String
Dim myText As String
Dim FileExists As Boolean
FileExists = True
On Error GoTo ErrorHandler
myFile = InputBox("Enter the name of file you want to open:")

Open myFile For Input As #1


If FileExists Then
Do While Not EOF(1) ' loop until the end of file 遍歷文件
myChar = Input(1, #1) ' get one character 獲取一個字元
myText = myText + myChar ' store in the variable myText 存儲至變數
264
myText
Loop
Debug.Print myText
Close #1
End If
Exit Sub
ErrorHandler:
FileExists = False
' print to the Immediate window 列印到立即視窗
' Close the file -commenting out this
instruction will cause
‘ error 52. 關閉檔– 注釋掉該指令(Close
#1)會導致錯誤 52
Select Case Err.Number
Case 71
MsgBox "The diskette drive is empty."

Case 53
MsgBox "This file can’t be found on the specified drive."
Case 75
Exit Sub
Case Else
MsgBox "Error " & Err.Number & " :" & Error(Err.Number)

Exit Sub
End Select
Resume Next
End Sub
過程 OpenToRead 的目的是一位元組一位元組地讀取使用者提供的文字檔內容(操作檔在第八章裡)。
當使用者輸入了一個檔案名,各種各樣的錯誤可能發生。例如,檔案名可能是錯誤的,或者使用者可能
試圖從軟碟上打開檔,而這時軟盤機裡並沒有軟碟,或者試圖打開一個已經打開了的檔。
要捕捉這些錯誤,過程 OpenToRead 結尾處的錯誤處理常式使用了 Err 物件的 Number (原文為 Name)
屬性。Err 物件包含有關執行時間錯誤的資訊。如果程式執行階段錯誤發生了,Err.Number 語句就會
返回錯誤編號。
如果錯誤 71,53 或者 75 發生了,VB 就會顯示寫在 Select…Case 代碼塊裡的友好資訊並且進行到語句
Resume Next,它會將 VB 發送到導致錯誤的代碼行下面的一行。如果是其它(意想不到)的錯誤發
生了,那麼 VB 就會返回錯誤編號(Err.Number)和錯誤描述(Error(Err.Number))
在過程的開始處,變數 FileExists 被設置為真,這樣,如果該程式沒遇到錯誤的話,所有在 If
FileExists Then 代碼塊裡的指令就會被執行。然而,如果 VBA 遇到了錯誤,那麼變數 FileExists
的值就會被設置為假(參見標籤 ErrorHandler 下面的錯誤處理常式的第一行語句)。這樣,VB 在試
圖讀取檔時就不會產生錯誤,導致打開錯誤。如果你注釋掉語句 Close #1 的話,那麼 VB 在下次試
圖打開同一檔時,就會遭遇錯誤。
注意 ErrorHandler 之前的語句 Exit Sub。將 Exit Sub 語句放在錯誤處理常式的上面,你不會希望如
果沒有錯誤發生的時候還執行該錯誤處理常式。
我們來進行下述練習,測試過程 OpenToRead 並更好理解錯誤捕捉:
1. 用記事本準備一個名叫 C:\Vacation.txt 文字檔,輸入任何文本
2. 逐語句執行過程 OpenToRead 四次,每次提供下述之一的資訊:
• C:\Vacation.txt 檔案名稱
• 不存在 C:盤上的檔案名
• A:盤上的任意檔,但是軟盤機是空的
• 注釋掉語句 Close #1,並且輸入檔案名 C:\Vacation.txt
技巧 13-7 錯誤:製造錯誤一測試錯誤處理常式
你可以故意製造一些錯誤來測試你程式裡的錯誤陷阱:
265
- 通過使用下述語法設置內置錯誤:Error error_number。例如,要顯示當除數為 0 時發生的錯誤
的話,可以在立即視窗裡輸入:
Error 11
當你按下回車鍵後,VB 就會顯示錯誤資訊:
執行時間錯誤”11”
除數為零
- 要檢查產生錯誤的意義的話,可以使用語法: Error(error_number)。例如,想要知道編號為 7
的錯誤是什麼意思,可以在立即視窗裡輸入下述指令:
?Error(7)
當你回車後,VB 會返回該錯誤描述:
記憶體溢出
17.接下來……
在本章,你學習了如何測試你的 VBA 過程,以確保他們按計劃進行。你使用中斷點和監視逐步程式來
調試它。你學習了如何在中斷模式下使用立即視窗。你知道了本地視窗如何能幫你檢測變數值,以
及調用堆疊對話方塊如何能在你複雜的程式裡幫你追蹤你在哪裡。你已經學習了在編譯時確定哪些需
要包括哪些需要排除。最後,你也學習了如何使用錯誤處理常式捕捉錯誤。通過使用內置的調試工
具,你可以快速指出程式的問題所在。請試著多花一些時間來熟悉這些工具,掌握了調試藝術,可
以節省你許多時間並避免錯誤。
通過完成一到十三章,你已經獲得了扎扎實實的 VBA 工作知識,很可能,你應該開始自己的 Excel
自動化工程了。本章結束了你使用 Excel 2002 VBA 的中級級別,VBA 提供了許多更高級的功能,這
些在本書的剩餘章節將挖掘出來。
第十四章微軟 Excel 2002 中的事件程式設計
你如何使當用戶點擊工作表儲存格時出現的內置快顯功能表失活?你如何在工作簿打開或者
關閉之前顯示一個自訂資訊?你如何驗證輸入在儲存格或者儲存格區域裡的資料?要想對
Excel 獲得徹底控制的話,你必須學習如何回應事件。學習如何進行事件程式設計將讓你貫徹你自己
的功能性到 Excel 應用軟體裡去。你需要學習有關該主題的第一件事情就是,什麼是事件。這裡
有個簡單的定義:
事件是發生的東西無需說,物件發生的事件是 Excel 的一部分,然而,一旦你學習了 Excel 中的事
件知識,你將發現更容易去理解發生在 Word 或者其它任何微軟辦公軟體的物件事件。事件是由對
象認可的行動。既然你知道了什麼是事件,那麼你需要知道事件是可以被一個應用軟體使用者(例
如你自己),另一個程式或者系統本身引發的。因此,你如何能夠引發事件呢?假設你按右鍵
一個工作表儲存格,該具體操作將顯示一個內置的工作表儲存格快顯功能表,允許你快速的訪問和
工作表儲存格相關的頻繁使用的命令。但是,萬一在某種情況下該內置回應不對呢?你可能想要完
全不接受工作表的按右鍵,或者當用戶按右鍵任何儲存格時,儲存格快顯功能表上出現一個自
定義功能表。有個好消息,就是你可以使用 VBA 來編寫代碼對事件進行反應。
Excel 提供了許多事件供你回應,下述物件可以回應事件:
• 工作表
• 圖表
• 透視表
• 工作簿
• 應用軟體通過編寫時
間過程,你可以決定當事件
發生時發生什麼。
1.事件程序介紹
事件程序,作為一種特殊的 VBA 過程,用來對特定的事件作出反應。該過程包含處理具體事件的
VBA 代碼。有些事件只需要簡單的一行代碼,然而,其它的可能更複雜。事件程序擁有名稱,按
下述方式創建:
物件名稱_事件名稱() 在事件名稱後面的括弧裡,你可以放置需要
266
發送到過程裡的參數。程式師不能更改事件程序名稱。在你編寫事
件過程對 Excel 事件作出反應之前,你需要知道:
• 想要響應的具體對象和事件的名稱
回應事件的物件在代碼視窗的過程下拉清單裡顯示了一系列事件(見圖 14-1)。同樣,你
也可以使用物件流覽器找到事件名稱(見圖 14-2)。
• 你需要放置代碼的地方有些事件在標準模組裡,其它的在類別模組裡。然而,工作簿,圖表
和工作表事件對任何打開的工作表或者工作簿可用。要給一個內嵌的圖表,透視表或者應
用軟體物件創建事件程序的話,那麼你必須首先使用關鍵字 With Events 在類別模組裡創建一
個新對象
圖 14-1 你可以在代碼視窗找到事件名稱
圖 14-2 你可以在物件流覽器裡找到事件名稱
2.啟動和失活事件
你可以使用應用軟體物件的 EnableEvents 屬性來啟動或者失活事件。如果你編寫了 VBA 過程但是
不希望有個具體事件發生,那麼就將 EnableEvents 屬性設置為 False。例如,為了避免在運行過
程 EnterData(參見下面代碼)引發 Workbook_BeforeClose 事件,那麼在調用 Workbook 物件 Close
方法之前,設置 EnableEvents 屬性為 False。在你的程式結束之前,你得將 EnableEvents 屬性設
置回 True,確保事件被啟動了。
1. 打開一新工作簿並另存為 DisableEvents.xls
2. 切換到 VB 編輯器螢幕,按兩下工程流覽器視窗的 ThisWorkbook,並且在出現的代碼視窗裡輸入
Workbook_BeforeSave 事件程序
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, _

Cancel As Boolean)
If MsgBox("Would you like to copy " & vbCrLf _

267
& "this worksheet to " & vbCrLf _
& "a new workbook?", vbYesNo) = vbYes Then

Sheets(ActiveSheet.Name).Copy
End If
End Sub
3. 選擇插入|模組添加一個標準模組啟動 VBA 工程,並且輸入下面顯示的過程:
Sub EnterData()
With ActiveSheet.Range("A1:B1")
.Font.Color = vbRed
.Value = 15
End With
Application.EnableEvents = False
ActiveWorkbook.Save
Application.EnableEvents = True

End Sub
4. 切換到 Excel 應用軟體視窗,並且選擇檔|保存。這時將引發 Workbook_BeforeSave 事件。點
擊是回應該信息框。Excel 會打開一個新工作簿,複製當前的工作表內容。
5. 啟動 DisableEvents 工作簿,並且選擇工具|巨集|巨集。在對話方塊上,點擊 EnterData 然後運行注
意,當你運行 EnterData 過程時,你沒有被提示在保存之前複製工作表。這表明
Workbook_BeforeSave 事件沒有運行。
3.事件次序
事件發生以相應具體的動作並且按預先設計的次序發生。下面的表格示範了打開新工作簿,往工
作簿裡添加新工作表以及關閉工作簿時事件的順序。
動作物件事件順序
打開一個新工作簿 Workbook NewWorkbook,
WindowDeactivate,
WorkbookDeactivate,
WorkbookActivate,
WindowActivate
往工作簿添加新工作表 Workbook WorkbookNewSheet, SheetDeactivate,
SheetActivate
關閉工作簿
4.工作表事件
Workbook WorkbookBeforeClose,

WindowDeactivate,
WorkbookDeactivate,

WorkbookActivate, WindowActivate

工作表物件相應例如工作表啟動和失活事件,計算工作表事件,工作表更改事件和按兩下或右鍵單
擊事件。本節討論一些工作表物件能夠回應的事件。
事件名稱
事件描述
當用戶啟動工作表時,發生

事件
啟動
示例 1
Dim shtName As String ‘declared at the top 在模組上部聲明
‘ of the module
Private Sub Worksheet_Activate()
shtName = ActiveSheet.Name
Range("B2").Select
End Sub
示例程式中,每次該工作表被啟動時,選擇儲存格 B2
示例 1 – 試驗:在 VB 編輯器視窗,啟動工程流覽器視窗並打開 Excel 目的檔夾。按兩下
Sheet2(Sheet2),
並且輸入示例程式到 Sheet2
268
代碼視窗。接下來,切換到 Excel 視窗並啟動 Sheet2。注意,當 Sheet2 被啟動時,被選擇的總是單
元格 B2。
事件名稱失活
事件描述
當用戶啟動不同的工作表時,

生該事件
Private Sub
示例 2
Worksheet_Deactivate()
MsgBox "You deactivated " & _
shtName & "." & vbCrLf & _
"You switched to " & _
ActiveSheet.Name & "."
End Sub
示例程式中,當 Sheet2 失活時,顯示一個資訊框
示例 2 – 試驗:在 VB 編輯器視窗,啟動工程流覽器視窗並且打開 Excel 目的檔夾,按兩下 Sheet2
(Sheet2),然後輸入示例程式。接下來,切換到 Excel 應用程式視窗並且啟動 Sheet2。你在示例 1
裡創建的 Worksheet_Activate 過程將會運行,Excel 會選中儲存格 B2 並且將工作表名稱存儲於你
在 Sheet2 的代碼模組上面聲明的全域變數 shtName 裡面。現在,點擊當前工作簿裡的其它工作表,
注意,Excel 將顯示你離開的工作表名稱和你要切換到的工作表名稱。
事件名稱
事件描述
當用戶選擇工作表儲存格時,

生該事件
選擇變化
示例 3
Private Sub Worksheet_SelectionChange(ByVal Target As

Excel.Range) ‘可省略 Excel,下同。


On Error Resume Next

Set myRange = Intersect(Range("A1:A10"), Target)

If Not myRange Is Nothing Then

MsgBox "Data entry or edits are not permitted."

End If
End Sub
示例程式中,當使用者選擇任何 myRange 中的儲存格或區域時,顯示一個資訊框
示例 3 – 試驗:在 VB 編輯器視窗,啟動工程流覽器視窗並且打開 Excel 目的檔夾,按兩下 Sheet3
(Sheet3),並且在 Sheet3 代碼視窗裡輸入該示例程式。然後切換到 Excel 視窗並啟動 Sheet3。點
擊給定區域 A1:A10 中的任何儲存格。注意,Excel 將顯示一個資訊。
Private Sub Worksheet_Change(ByVal Target _

事件名稱變化
事件描述
當使用者更改儲存格內容時,引

該事件
As Excel.Range)
例 4Application.EnableEvents = False
Target = UCase(Target)
Columns(Target.Column).AutoFit

Application.EnableEvents = True

End Sub
示例程式中,你輸入的內容會自動變為大寫,並且該列列寬將自動適應內容文本長短
(Sheet1),並且在 Sheet1 代碼視窗裡輸入該示例程式。然後切換到 Excel 視窗並啟動 Sheet1。在
任意儲存格裡輸入任何文本,注意,你一旦按下回車鍵,Excel 就會將該文本變為大寫,然後該
列自動適應文本長度。
事件名稱
事件描述
當用戶重新計算工作表時,引

該事件
計算
示例 5
Private Sub Worksheet_Calculate()

MsgBox "The worksheet was recalculated."

End Sub
一旦工作表重新計算,示例程式就會彈出一資訊
示例 5 – 試驗:在當前工作簿裡添加一個新工作表,練習假定 Excel 會在你的工作簿裡放置 Sheet4。
往 Sheet4 的儲存格 A2 裡輸入 1,B2 裡輸入 2。在儲存格 C2 裡輸入下述公式:= A2 + B2。在 VB 編輯器
視窗,啟動工程流覽器視窗並且打開 Excel 目的檔夾, 按兩下 Sheet4 (Sheet4),並且輸入如上所
269
示得 Worksheet_Calculate 事件程式。切換到 Excel 視窗並啟動 Sheet4,在 B2 儲存格裡輸入任何數
字。注意,當退出編輯模式後,Worksheet_Calculate 事件就被引發了,你也就看到了一個自訂
的信息。
事件名稱
事件描述
當用戶按兩下工作表儲存格時,

發該事件
按兩下前
示例 6
Private Sub Worksheet_BeforeDoubleClick(ByVal _

Target As Range, Cancel As Boolean)

If Target.Address = Range("$C$9") Then

MsgBox "No double-clicking, please."

Cancel = True
Else
MsgBox "You may edit this cell."

End If
End Sub
示例程式不允許按兩下時儲存格內部直接編輯 C9
示例 6 – 試驗:在 VB 編輯器視窗,啟動工程流覽器視窗並且打開 Excel 目的檔夾,按兩下 Sheet2
(Sheet2),並且在 Sheet2 代碼視窗裡輸入該示例程式。然後切換到 Excel 視窗並啟動 Sheet2。當
你按兩下儲存格 C9 時,該事件程序取消了內置的 Excel 行為,用戶不允許進行儲存格內部直接編輯。
然而,用戶可以繞過該限制,點擊編輯欄或者按下 F2 鍵。當你編寫一些事件程式來禁止訪問某
些功能時,請編寫一些額外的代碼以禁止一些工作區。(如隱藏編輯欄,隱藏工作表標籤等)
事件名稱按右鍵前
事件描述
當用戶按右鍵工作表儲存格
時,引發該事件
示例 7
Private Sub Worksheet_BeforeRightClick(ByVal _

Target As Range, Cancel As Boolean)

With Application.CommandBars("Cell")

.Reset
If Target.Rows.Count > 1 Or _

Target.Columns.Count > 1 Then

With .Controls.Add(Type:=msoControlButton, _

before:=1, temporary:=True)

.Caption = "Print..."

.OnAction = "PrintMe"

End With
End If
270
End With
End Sub
Sub PrintMe()
Application.Dialogs(xlDialogPrint).Show arg12:=1

End Sub
示例程式中,當使用者選擇區域不止一行一列時,就會在儲存格快顯功能表上添加一個 Print 選項
示例 7 – 試驗:在 VB 編輯器視窗,啟動工程流覽器視窗並且打開 Excel 目的檔夾,按兩下 Sheet2
(Sheet2),並且在 Sheet2 代碼視窗裡輸入該示例程式。在當前工程裡添加一個新模組,並且輸入
PrintMe 過程,如上所示。當使用者從快顯功能表上選擇 Print 選項時, 該過程就會被
Worksheet_BeforeRightClick 事件調用。注意,對話方塊的 Show 方法後面帶了個名為 arg12:=1 的參
數。該參數讓列印對話方塊顯示時,勾選“所選區域”(通常是“所選工作表”)。在適當的模組
裡輸入完兩個過程後,切換到 Excel 視窗並啟動 Sheet2。在任何單個儲存格上按一下右鍵,注意,
這時出現的快顯功能表為缺省選項。現在,我們重新選擇,這次包括多個儲存格, 並且在所選區域
上按一下右鍵,你將看到 Print…選項出現在第一位置。點擊 Print 選項,注意,列印對話方塊顯示的
是所選區域,而不是缺省的所選工作表。
注意:參見第十章中使用快顯功能表的更多資訊,還有,參見圖 10-3,如何定位 Excel 的內置參數列
表。
事件名稱跟蹤連結
事件描述
當用戶點擊 Excel 工作表中的
連結時,引發該事件
示例 8
Private Sub Worksheet_FollowHyperlink(ByVal _

Target As Hyperlink)
Target.AddToFavorites
End Sub
示例程式將使用者點擊的連結添加到 IE 的我的最愛裡
示例 8 – 試驗:在 VB 編輯器視窗,啟動工程流覽器視窗並且打開 Excel 目的檔夾,按兩下 Sheet1
(Sheet1),並且在 Sheet1 代碼視窗裡輸入該示例程式。切換到 Excel 視窗,並在任何儲存格裡輸
入一網址,比如 www.wordware.com 然後回車。現在,點擊該連結啟動該網址,當 IE 視窗出現後,
打開收藏菜單,就會發現 Wordware 網址已經被添加到該菜單裡了。
事件名稱
事件描述
工作表中的樞紐分析表被更新
更新樞紐分析表
示例 9
後,就會發生該事件。它是 Excel Private Sub pivTbl_PivotTableUpdate( _
2002 的新事件。參數 Target 明確
所選地樞紐分析表。pivTbl 是

變數,指向類別模組裡使用
WithEvents 關鍵字聲明的工作
表的對象
ByVal Target As PivotTable)

MsgBox Target.Name & _

" report has been updated." & vbCrLf _

& "The PivotReport is located in cells " & _

Target.DataBodyRange.Address

End Sub
示例程式顯示一資訊,描述被更新的樞紐分析表的名稱和發生在工作表上報告的儲存格區域位址。
示例 9 – 試驗:打開位於本書附帶光碟上的檔 PivotReport_2.xls,點擊樞紐分析表上的任意
儲存格,並且點擊樞紐分析表工具列上的更新資料按鈕。圖 14-3 和 14-4 示範了如何創建
PivotTableUpdate 事件控制碼。
271
圖 14-3 你必須使用類別模組來捕捉 PivotTableUpdate 事件。該類別模組可以是任何有效的模組名稱。
物件變數名稱 pivTbl,也可以是任何有效的變數名稱
圖 14-4 你在能夠捕捉 PivotTableUpdate 事件之前,必須在標準模組裡設立類別模組的示例,並且將
物件 Worksheet 賦予新物件的屬性 pivTbl。
5.工作簿事件
當用戶執行一些操作,如打開,啟動,列印,保存以及關閉工作簿時,發生工作簿物件事件。工
作簿事件不會創建在標準 VBA 模組裡,想要編寫回應工作簿事件的代碼的話,那麼需要按兩下 VB 編輯
器裡的工程流覽器中的 ThisWorkbook。在出現的代碼視窗, 打開物件下拉清單並且選擇 Workbook
對象。在過程下拉清單裡選擇你想要地事件。被選擇的事件程序將出現在代碼視窗。例如:
Private Sub Workbook_Open()
在此放置你的事件處理代碼
272
End Sub
本節描述工作簿一些可用的事件。
事件名稱啟動
事件描述示例 10
當用戶啟動該工作簿時,引發該 Private Sub Workbook_Activate()
事件。當使用者從其它應用程式切
換到 Excel 工作簿時,不會引發該
MsgBox "This workbook contains " & _

ThisWorkbook.Sheets.Count & "sheets."

事件 End Sub
當用戶啟動包含 Workbook_Activate 事件程序的工作簿時,示例程式顯示該工作簿含有的工作表數

示例 10 – 試驗:在 VB 編輯器視窗,啟動工程流覽器視窗並且打開 Excel 目的檔夾,按兩下
ThisWorkbook 並且在其代碼視窗裡輸入示例過程。然後,切換到 Excel 視窗並且打開一個新工作
簿。切換到你輸入了 Workbook_Activate 過程的工作簿,這時,Excel 將顯示該工作簿裡的工作
表總數。
事件名稱
事件描述
當用戶啟動 Excel 的其它工作簿
時,引發該事件。當用戶切換

其它應用軟體時,不會發生該事
件。
失活
示例 11
Private Sub Workbook_Deactivate()

For Each cell In _


ActiveSheet.UsedRange
If Not IsEmpty(cell) Then

Debug.Print cell.Address & _

":" & cell.Value


End If
Next
End Sub
當使用者啟動其它工作簿時,示例程式將在立即視窗裡列印當前工作簿裡有輸入的儲存格地址和數值
示例 11 – 試驗:在 VB 編輯器視窗,啟動工程流覽器視窗並且打開 Excel 目的檔夾。按兩下
ThisWorkbook,然後輸入示例程式於代碼視窗。切換到 Excel 視窗,並且在活動工作表裡輸入一
些東西。然後,啟動某個不同的工作簿。該動作將激發 Workbook_Deactiate 事件程序。切換到
VB 編輯器視窗,並打開立即視窗察看什麼儲存格輸入有了報告。
事件名稱
事件描述示例 12
打開
當用戶打開工作簿時引發該事件 Private Sub Workbook_Open()
ActiveSheet.Range("A1").Value = Format _

(Now(), "mm/dd/yyyy")
End Sub
當工作簿被打開時,示例過程在儲存格 A1 裡放置當前日期
示例 12 – 試驗:打開一個新工作簿,在 VB 編輯器視窗,啟動工程流覽器視窗,並且打開 Excel 對
象資料夾。按兩下 ThisWorkbook, 並輸入示例過程。保存並且關閉該工作簿。當你再次打開該工作
簿時,當前日期就會被放置在當前活動工作表地儲存格 A1 裡。
事件名稱
事件描述
保存前
示例 13
273
該事件發生在工作簿被保存之前。參數 SaveAsUI 是唯讀的,指向 SaveAs 對話方塊。如果該工作簿並
沒有被保存過,那麼 SaveAsUI 參數的值就是 True,否則為 False
Private Sub Workbook_BeforeSave(ByVal _

SaveAsUI As Boolean, Cancel As Boolean)

If SaveAsUI = True And _

ThisWorkbook.Path = vbNullString Then

MsgBox "This document has not yet " _

& "been saved." & vbCrLf _

& "The Save As dialog box will be displayed."

ElseIf SaveAsUI = True

Then MsgBox "You are not allowed to use " _

& "the SaveAs option. "

Cancel = True
End If
End Sub
如果該工作簿沒有被保存過,那麼示例程式將顯示另存為對話方塊。如果該文
件沒有被保存過,那麼該
工作簿的路徑名將為字串 NULL(vbNullString)。過程不允許用戶將該工
作簿保存為一個不同的名
稱;另存為操作將通過設置參數 Cancel 為 True 而中斷。使用者將需要選擇保存
選項來保存該工作簿。
示例 13 – 試驗:打開一個新工作簿,在 VB 編輯器視窗,啟動工程流覽器視窗並打開 Excel 物件文
件夾。按兩下 ThisWorkbook
並且在該代碼視窗裡輸入示例程式。點擊“If SaveASUI…”語句旁邊的頁邊以放置一個中斷點。切
換到 Excel 視窗,並且在任意儲存格輸入一些資料。點擊工具列上保存按鈕。Workbook_BeforeSave
事件程序將會被啟動。執行 If SaveAsUI 後面的語
句。在另存為對話方塊裡輸入 SaveEvent.xls 作為該工作簿的名稱。在保存(並命名)該工作簿之
後,對該工作簿作一些更改,
然後選擇文件|另存為。這次 ElseIf 子句將會被執行,並且你不會被允許通過使用 SaveAs 選項來
保存該工作簿。
事件名稱
事件描述
該工作簿發生工作簿列印之
前,
以及列印對話方塊出現之前
列印前
示例 8
Private Sub Workbook_BeforePrint(Cancel _

As Boolean)
Dim response As Integer
response = MsgBox("Do you want to " & vbCrLf & _

"print the workbook's full name in the footer?", _

vbYesNo)
If response = vbYes Then

ActiveSheet.PageSetup.LeftFooter = _

ThisWorkbook.FullName
Else
ActiveSheet.PageSetup.LeftFooter = ""

End If
End Sub
在列印之前,如果使用者點擊了資訊框的“是”,那麼示例程式就會將工作簿的完整名稱放入文件的
註腳裡
示例 14 – 試驗:打開一個新工作簿,在 VB 編輯器視窗,啟動工程流覽器視窗並打開 Excel 物件文
件夾。按兩下 ThisWorkbook 並且在該代碼視窗裡輸入示例程式。然後,切換到 Excel 視窗,並啟動
任意工作表。在任意儲存格裡輸入你想要輸入的任意內容。當你按下工具列上的預覽列印按鈕時,
Excel 將會詢問你是否想要將工作簿名稱和路徑放入到註腳裡。
事件名稱
事件描述
關閉前
示例 8
274
該事件發生在工作簿關閉之前, 並且在系統詢問使用者是否保存變化之前
Private Sub Workbook_BeforeClose(Cancel _

As Boolean)
If MsgBox("Do you want to change " & vbCrLf _

& " workbook properties before closing?", _

vbYesNo) = vbYes Then

Application.Dialogs(xlDialogProperties).Show

End If
End Sub
如果使用者對資訊框回應“是”時,示例程式將顯示屬性對話方塊
示例 15 – 試驗: 在 VB 編輯器視窗, 啟動工程流覽器視窗並打開 Excel 目的檔夾
。按兩下
ThisWorkbook 並且在該代碼視窗裡輸入示例程式。然後,切換到 Excel 視窗,並
關閉包含
BeforeClose 事件程式的工作簿。在關閉之前,你將看到一個資訊框,詢問你是否要更改檔案屬性。
在察看和修改工作簿屬性之後,該過程關閉該工作簿。如果有些改變你還沒有保存,那麼你還有機
會去保存該工作簿,取消該更改或者乾脆終止該關閉操作。
事件名稱
事件描述
安裝增益集
示例 8
當用戶安裝該工作簿為一個增益集時,引發該事件
Private Sub Workbook_AddinInstall()

MsgBox "To create a calendar, " & vbCrLf _

& "enter CalendarMaker in the " & vbCrLf _

& "Macros dialog box."


End Sub
請參考下述詳細指導來引發 Workbook_AddinInstall 事件程序
示例 16 – 試驗:
1. 打開一個新工作簿
2. 切換到 VB 編輯器,啟動工程流覽器視窗,並打開 Excel 目的檔夾
3. 按兩下 ThisWorkbook 並且輸入示例 16 和示例 17 程式在 ThisWorkbook 代碼視窗
4. 在當前 VBA 工程插入一個新模組,並且輸入過程 CalendarMaker,顯示於示例 17 之後
5. 切換到 Excel 視窗,並選擇檔|屬性,在屬性對話方塊裡輸入下述內容:標題:Calendar Maker 備
注:
Create a monthly calendar in an Excel spreadsheet。當你加亮該增益集名稱時,上面
的資訊將出現在增益集對話方塊裡面
6. 點擊確定以退出屬性對話方塊
7. 選擇文件|另存為並且將該工作簿保存為 Calendar.xls
8. 現在,選擇檔|另存為,將 Calendar.xls 保存為一個增益集。從另存為類型下拉清單,選
擇 Microsoft Excel 增益集。輸入一個新名稱(CalendarMaker.xla)然後點擊保存
9. 關閉 Calendar.xls 工作簿
10. 打開一個新工作簿
11. 選擇工具|增益集。使用流覽按鈕將 CalendarMaker 加入到增益集清單裡面。勾選清單方塊裡的
CalendarMaker 增益集。當你點擊確定後,Workbook_AddInInstall 過程就會被引發。點擊信
息框上的確定
12. 想要創建一個日曆的話,可以選擇工具|巨集|巨集,在巨集名稱文字方塊裡輸入 CalendarMaker 然後
點擊運行,你將會被詢問年和月, 輸入年和月(例如,Nov 2005)並點擊確定,將出現一日
曆頁,如圖 14-5 所示
275
圖 14-5 由過程 CalendarMaker 創建的月曆
13. 想要引發示例 17 中示範的 AddInUninstall 事件程序的話,可以選擇工具|增益集,並清除
CalendarMaker 增益集前面的勾選標誌就可以了。
事件名稱
事件描述
卸載增益集
示例 17
當用戶卸載一個增益集時,引發該事件
Private Sub Workbook_AddinUninstall()

MsgBox "The CalendarMaker " & vbCrLf _

& "add-in was unloaded."

End Sub
一旦該工作簿作為一個增益集被卸載後,示例程式將顯示一個資訊
示例 17 – 試驗:參見示例 16
下述過程 CalendarMaker 可在 CodeLibrarian 增益集裡獲得,這裡用來示範工作簿物件的安裝載入
宏和卸載增益集事件的使用(參見示例 16 和 17)。
Sub CalendarMaker()
Dim MyInput As String

Dim StartDay As Date

Dim DayofWeek As Integer, CurYear As Integer, CurMonth As Integer

Dim FinalDay As Long


Dim cell As Range
Dim RowCell As Integer, ColCell As Integer

Dim x As Integer
‘以上變數聲明為譯者加上的,以確保程式正常運行
' protect sheet if had previous calendar to prevent error. 保護工作表,以避免之前
日曆帶來的錯誤(原文誤為 Unprotect)
ActiveSheet.Protect DrawingObjects:=False, Contents:=False, _

Scenarios:=False
' Prevent screen flashing while drawing calendar. 製作日歷時防止螢幕閃爍
Application.ScreenUpdating = False

' Set up error trapping. 設置錯誤陷阱


On Error GoTo MyErrorTrap

' Clear area a1:g14 including any previous calendar. 清除 a1:g14 儲存格區域的內容
Range("a1:g14").Clear

' Use InputBox to get desired month and year and set variable

' MyInput. 使用輸入框從用戶獲得年月,並賦予變數 MyInput


MyInput = InputBox("Type in Month and year for Calendar ")

' Allow user to end macro with Cancel in InputBox. 允許使用者點擊取消以終止宏


If MyInput = "" Then Exit Sub

' Get the date value of the beginning of input month. 獲取輸入年月的日期序號
StartDay = DateValue(MyInput)

276
' Check if valid date but not the first of the month 檢查是否是有效日期,但不是當
月第一天
' -- if so, reset StartDay to first day of month. 如不是,則設置 StartDay 為當月第一

If Day(StartDay) <> 1 Then

StartDay = DateValue(Month(StartDay) & "/1/" & _

Year(StartDay))
End If
' Prepare cell for Month and Year as fully spelled out. 準備年月的儲存格,年月為完
整拼寫
Range("a1").NumberFormat = "mmmm yyyy"

' Center the Month and Year label across a1:g1 with appropriate

' size, height and bolding. 將年月於 A1:G1 區域跨列居中顯示,並設置字型大小,粗體何行高


With Range("a1:g1")
.HorizontalAlignment = xlCenterAcrossSelection

.VerticalAlignment = xlCenter
.Font.Size = 18
.Font.Bold = True
.RowHeight = 35
End With
' Prepare a2:g2 for day of week labels with centering, size,

' height and bolding. 設置 A2:G2 區域為星期標誌,設置為居中,大小,行高和粗體


With Range("a2:g2")
.ColumnWidth = 11
.VerticalAlignment = xlCenter

.HorizontalAlignment = xlCenter

.VerticalAlignment = xlCenter

.Orientation = xlHorizontal

.Font.Size = 12
.Font.Bold = True
.RowHeight = 20
End With
' Put days of week in a2:g2. 輸入星期
Range("a2") = "Sunday"
Range("b2") = "Monday"
Range("c2") = "Tuesday"
Range("d2") = "Wednesday"

Range("e2") = "Thursday"
Range("f2") = "Friday"

Range("g2") = "Saturday"
' Prepare a3:g7 for dates with left/top alignment, size, height

' and bolding.設置 A3:G7 區域為左對齊和上對齊,大小,行高和粗體


With Range("a3:g8")
.HorizontalAlignment = xlLeft

.VerticalAlignment = xlTop

.Font.Size = 18
.Font.Bold = True
.RowHeight = 21
End With
' Put input month and year fully spelling out into "a1". 在儲存格 A1 裡輸入年月
Range("a1").Value = Application.Text(MyInput, "mmmm yyyy")
' Set variable and get which day of the week the month starts. 設置變數,並獲取該月第

一天的星期
DayofWeek = Weekday(StartDay)

' Set variables to identify the year and month as separate

' variables. 設置變數分別獲取年和月


CurYear = Year(StartDay)

CurMonth = Month(StartDay)
' Set variable and calculate the first day of the next month. 設置變數並計算下個月地第
277
一天
FinalDay = DateSerial(CurYear, CurMonth + 1, 1)

' Place a "1" in cell position of the first day of the chosen

' month based on DayofWeek. 基於星期序號,在選定月份第一天的位置放置“1”


Select Case DayofWeek

Case 1
Case 2
Case 3
Case 4
Case 5
Case 6
Case 7
Range("a3").Value = 1

Range("b3").Value = 1

Range("c3").Value = 1

Range("d3").Value = 1

Range("e3").Value = 1
Range("f3").Value = 1

Range("g3").Value = 1

End Select
' Loop through range a3:g8 incrementing each cell after the "1"

' cell. 在儲存格區域 A3:G8 裡面迴圈,在 1 之後,每個儲存格增加 1


For Each cell In Range("a3:g8")

RowCell = cell.Row
ColCell = cell.Column
' Do if "1" is in first column. 如果 1 在第一列
If cell.Column = 1 And cell.Row = 3 Then

' Do if current cell is not in 1st column. 如果目前的儲存格並非第一列


ElseIf cell.Column <> 1 Then

If cell.Offset(0, -1).Value >= 1 Then

cell.Value = cell.Offset(0, -1).Value + 1

' Stop when the last day of the month has been

' entered. 當遇到當月的最後一天時,停止


If cell.Value > (FinalDay - StartDay) Then
cell.Value = ""
' Exit loop when calendar has correct number of

' days shown.當日曆顯示了正確的數位時,退出迴圈


Exit For
End If
End If
' Do only if current cell is not in Row 3 and is in Column 1. 僅當目前的儲存格不在第

三行,而在第一列時
ElseIf cell.Row > 3 And cell.Column = 1 Then

cell.Value = cell.Offset(-1, 6).Value + 1

' Stop when the last day of the month has been entered. 當遇到當月的最後一天

時,停止
If cell.Value > (FinalDay - StartDay) Then

cell.Value = ""
Next
' Exit loop when calendar has correct number of days

' shown. 當日曆顯示了正確的數位時,退出迴圈


Exit For
End If
End If
278
' Create Entry cells, format them centered, wrap text, and border

' around days. 創建輸入儲存格,居中,自動換行,並在每日周圍設置邊框


For x = 0 To 5
Range("A4").Offset(x * 2, 0).EntireRow.Insert

With Range("A4:G4").Offset(x * 2, 0)

.RowHeight = 65
.HorizontalAlignment = xlCenter

.VerticalAlignment = xlTop

.WrapText = True
.Font.Size = 10
.Font.Bold = False

' Unlock these cells to be able to enter text later after

' sheet is protected. 解開這些區域的鎖定,以供將來輸入文本


.Locked = False
End With
' Put border around the block of dates. 在每日周圍設置邊框
With Range("A3").Offset(x * 2, 0).Resize(2, _

7).Borders(xlLeft)

.Weight = xlThick
.ColorIndex = xlAutomatic

End With
With Range("A3").Offset(x * 2, 0).Resize(2, _
7).Borders(xlRight)

.Weight = xlThick
.ColorIndex = xlAutomatic

End With
Next
Range("A3").Offset(x * 2, 0).Resize(2, 7).BorderAround _

Weight:=xlThick, ColorIndex:=xlAutomatic

If Range("A13").Value = "" Then Range("A13").Offset(0, 0) _

.Resize(2, 8).EntireRow.Delete

' Turn off gridlines. 關閉格線


ActiveWindow.DisplayGridlines = False

' Protect sheet to prevent overwriting the dates. 保護工作表,防止覆蓋日期


ActiveSheet.Protect DrawingObjects:=True, Contents:=True, _

Scenarios:=True
' Resize window to show all of calendar (may have to be adjusted

' for video configuration). 設置視窗大小以顯示完整日曆


ActiveWindow.WindowState = xlMaximized

ActiveWindow.ScrollRow = 1

' Allow screen to redraw with calendar showing. 允許螢幕重繪日曆顯示


Application.ScreenUpdating = True

' Prevent going to error trap unless error found by exiting Sub

' here. 放置沒有錯誤時也允許錯誤陷阱


Exit Sub
' Error causes msgbox to indicate the problem, provides new input box, 錯誤導致信

息框指明問題,提供新的輸入框
' and resumes at the line that caused the error. 並恢復至導致錯誤的代碼行
MyErrorTrap:
MsgBox "You may not have entered your Month and Year correctly." _

& Chr(13) & "Spell the Month correctly" _

& " (or use 3 letter abbreviation)" _

& Chr(13) & "and 4 digits for the Year"

MyInput = InputBox("Type in Month and year for Calendar")

If MyInput = "" Then Exit Sub

Resume
End Sub
279
事件名稱
事件描述
新建工作表
示例 18 當用戶新建一個工作表後,引發該事件
Private Sub Workbook_NewSheet(ByVal Sh As Object)

If MsgBox("Do you want to place " & vbCrLf _

& "the new sheet at the beginning " & vbCrLf _


& "of the workbook?", vbYesNo) = vbYes Then

Sh.Move before:=ThisWorkbook.Sheets(1)

Else
Sh.Move After:=ThisWorkbook.Sheets( _

ThisWorkbook.Sheets.Count)
MsgBox Sh.Name & _

" is now the last sheet in the workbook."

End If
End Sub
當使用者在彈出的資訊框上點擊是的話,示例程式就會將新建的工作表置於工作簿的開始,否則在結

示例 18 – 試驗:打開一個新工作簿,在 VB 編輯器視窗,啟動工程流覽器視窗並且打開 Excel 物件
資料夾。按兩下 ThisWorkbook 並且輸入示例程式於 ThisWorkbook 代碼視窗。切換到 Excel 視窗,並且
在工作表標籤的任意地方按一下右鍵,從快顯功能表上選擇插入, 選擇你想要插入的工作表類型,並
點擊確定。Excel 將會問你將新建的工作表放置到什麼地方。下面是一個 Excel 工作表可以回應的
事件列表。
事件名稱
SheetActivate
SheetDeactivate
SheetSelectionChange

SheetChange
SheetCalculate
SheetBeforeDoubleClick

SheetBeforeRightClick

事件名稱
事件描述
當使用者啟動任何顯示該工作簿

窗口時,引發該事件
描述
當用戶啟動當前工作簿裡的任何工作表發
生該事件。SheetActivate 事件同樣也可
以發生在應用軟體級別,當你啟動任意打
開的工作簿裡的任意工作表時,都會啟動
該事件
當用戶啟動工作簿裡一個不同的工作表時,引發該事件
當用戶選擇不同儲存格時,引發該事件。
該事件發生在該工作簿裡的每個工作表

本事件發生在使用者更改儲存格內容時
本事件發生在用戶重新計算工作表的時候
當用戶按兩下工作表上的儲存格時,引發該事件
當用戶在工作表儲存格上按一下右鍵時,引發該事件
啟動窗口
示例 19
Private Sub Workbook_WindowActivate(ByVal _

Wn As Window)
Wn.GridlineColor = vbYellow
End Sub
當使用者啟動包含 Workbook_WindowActivate 程式的工作簿時,示例程式將更改工作表的格線顏色
為黃色
示例 19 – 試驗: 在 VB 編輯器視窗, 啟動工程流覽器視窗並打開 Excel 目的檔夾
。按兩下
ThisWorkbook,並輸入示例程式。切換到 Excel 視窗,打開一個全新的工作簿。使用視窗功能表將
Excel 工作簿設置為豎直並排排列。當你啟動含有示例程式的工作簿時, 格線應該會變為黃色
了。
事件名稱
事件描述
窗口失活
示例 20
280
當使用者將焦點從該工作簿視窗移走時,引發該事件
Private Sub Workbook_WindowDeactivate(ByVal _

Wn As Window)
Wn.GridlineColor = vbRed
End Sub
當用戶從包含 Workbook_WindowActivate 過程代碼的工作簿切換到另外一個
工作簿時,示例程式將其格線設置為紅色
示例 20 – 試驗:在 VB 編輯器視窗,啟動工程流覽器視窗並打開 Excel 資料夾,按兩下 ThisWorkbook
並輸入示例程式。切換到 Excel 視窗,打開一個全新的工作簿。使用視窗功能表將所有 Excel 工作簿
設置為豎直排列。當你失活包含 Workbook_WindowDeactivate 事件代碼的工作簿並切換到空工作
簿時,被失活地工作表裡的格線就會被變為紅色。
事件名稱
事件描述
視窗調整大小
示例 21
當使用者打開視窗,調整視窗大小, 最大化或者最小化視窗時,引發該事件
Private Sub Workbook_WindowResize(ByVal Wn As Window)

If Wn.WindowState <> xlMaximized Then

Wn.Left = 0
Wn.Top = 0
End If
End Sub
當使用者調整視窗大小時,示例程式將工作簿視窗移至螢幕的左上角
示例 21 – 試驗:在 VB 編輯器視窗,啟動工程流覽器視窗並且打開 Excel 目的檔夾。按兩下
ThisWorkbook 並輸入示例程式。切換到 Excel 視窗並且點擊還原按鈕(工作簿,非 Excel 應用軟體)。
通過拖曳視窗內部邊框改變當前使用中視窗大小時,一旦你完成大小調整操作,該工作簿視窗會
自動跳到螢幕的左上角(Excel 應用軟體視窗的左上角)。
下述表格描述了 Excel 2002 裡增加的工作簿事件。
事件名稱
PivotTableOpenConnection
描述
當樞紐分析表報告打開對其資料來源的連接時發生該事件。該事件
要求你在類別模組裡面使用 WithEvents 關鍵字聲明一個
Application 或者 Workbook 類型物件。(參見“圖表事件”和“應
用程式物件識別的事件”部分有關該關鍵字使用的示例)
PivotTableCloseConnection 當樞紐分析表報告關閉對其資料來源的連接
後發生該事件。該事件要求你在類別模組裡
面使用 WithEvents 關鍵字聲明一個
Application 或者 Workbook 類型物件。
(參見“圖表事件”和“應用程式物件識
別的事件”部分有關該關鍵字使用的示
例)
SheetPivotTableUpdate

需要下述兩個參數:
Sh – 被選擇的工作表
Target – 被選擇的樞紐分析表
報告
當樞紐分析表報告被更新後發生該事件。該事件要求你在類別模組裡使
用關鍵字 WithEvents 聲明一個 Application 或者 Workbook 的 type 對
象(參見示例 9,需要使用關鍵字 WithEvents 設置事件處理的相關
信息)。該事件處理可以在附帶 CD 裡的 PivotReport.xls 裡找到。
Private Sub App_SheetPivotTableUpdate( _

ByVal Sh As Object, ByVal Target As PivotTable)

MsgBox "Pivot Table has been updated."

End Sub
281
6.圖表事件
眾所周知,你可以創建一個內嵌在工作表裡的圖表,也可以創建一個單獨的圖表工作表。在本節,
你將學習如何控制圖表事件, 不管你決定了將你的圖表放在哪裡。在你試驗圖表事件之前,做以
下幾件事情:
1. 打開一個新 Excel 工作簿,並且保存為 ChartEvents.xls
2. 輸入示例資料,如圖 14-6 所示
3. 選擇儲存格區域 A1:D4,並點擊工具列上的圖表按鈕
4. 準備一個柱型圖,如圖 14-6 所示,並將其內嵌於工作表內
5. 使用相同的資料,創建一個折線圖於一個單獨的圖表工作表(參見圖 14-7)
6. 將圖表工作表名稱改為 Sales Analysis Chart。
圖 14-6 內嵌於工作表的柱型圖
圖 14-7 置於圖表工作表裡的折線圖
下面的表格列出了圖表物件的事件。在該表格裡示範的示例程式應該在你剛才放置在圖表工作表裡
的折線圖上試驗(參見圖
14-7)。內嵌於工作表裡的圖表的事件需要特別設置,本章稍候將講解。
1. 在 VB 編輯器視窗,啟動工程流覽器視窗並打開 Excel 目的檔夾
2. 按兩下圖表物件 Chart1 (Sales Analysis Chart)
3. 在代碼視窗,輸入事件程式,如下表所示
4. 啟動圖表工作表,並執行一些將引發事件程序的操作。例如,點擊圖表標題後,
282
Chart_MouseDown 和 Chart_Select 事件就會被引發。
事件名稱
Activate
Deactivate
Select
描述
當使用者啟動圖表工作表時引發該事件
Private Sub Chart_Activate()
MsgBox "You've activated the chart sheet."

End Sub
當使用者離開該圖表時引發該事件(進入其它工作表)
Private Sub Chart_Deactivate()

MsgBox "It looks like you want to leave the chart sheet."

End Sub
當使用者選擇了某個圖表成員時引發該事件
Private Sub Chart_Select(ByVal ElementID As Long, _

ByVal Arg1 As Long, ByVal Arg2 As Long)

If Arg1 <> 0 And Arg2 <> 0 Then


MsgBox ElementID & ", " & Arg1 & ", " & Arg2

End If
If ElementID = 4 Then

MsgBox "You've selected the chart title."

ElseIf ElementID = 24 Then

MsgBox "You've selected the chart legend."

ElseIf ElementID = 12 Then

MsgBox "You've selected the legend key."

ElseIf ElementID = 13 Then


MsgBox "You've selected the legend entry."

End If
End Sub
ElementId 返回一個代表所選圖表成員的常數。參數 Arg1 和 Arg2 用於某些相關的
圖表成員上。例如,圖表坐標軸(ElementId=21),可以明確是主坐標軸(Arg1=0)
或者次坐標軸(Arg1=1),而坐標軸的類型則由參數 Arg2 確定,它可以是下述
三種之一:
0 – 分類軸,1 – 數值軸,或者 3 – 系列軸
SeriesChange
Calculate
Resize
DragOver
DragPlot
BeforeDoubleClick
BeforeRightClick
當使用者更改圖表資料點時,引發該事件。圖表物件應該在類別模組
裡使用關鍵字 WithEvents 進行聲明。
當使用者選擇新資料或者改變圖表資料時,引發該事件。
Private Sub Chart_Calculate()

MsgBox "The data in your spreadsheet has " & vbCrLf _

& "changed. Your chart has been updated."

End Sub
當使用者改變圖表大小時,引發該事件。該圖表物件應該在類別模組
裡面使用 WithEvents 關鍵字來聲明
當使用者拖曳資料到該圖表時,引發該事件。該圖表物件應該在類
模組裡面使用 WithEvents 關鍵字來聲明
當使用者拖曳儲存格區域到圖表時,引發該事件。該圖表物件應
該在類別模組裡面使用 WithEvents 關鍵字來聲明
當使用者按兩下圖表時,引發該事件
當使用者按右鍵圖表時,引發該事件
Private Sub Chart_BeforeRightClick(Cancel As Boolean)

Cancel = True
End Sub
當你將參數 Cancel 設置為 True 後,你就不能訪問圖表區域快顯功能表了
MouseDown 當游標在圖表之上並按下滑鼠時,引發該事件
Private Sub Chart_MouseDown(ByVal Button As Long, _

ByVal Shift As Long, ByVal x As Long, ByVal y As _

Long)
If Button = 1 Then
MsgBox "You pressed the left mouse button. "

ElseIf Button = 2 Then

283
Else
MsgBox "You pressed the right mouse button. "

MsgBox "You pressed the middle mouse button. "

End If
End Sub
按鍵參數決定哪個滑鼠鍵被按下(MouseDown 事件),或者釋放(MouseUp 事件)了:
1 – 滑鼠左鍵,2 – 滑鼠右鍵,4 – 滑鼠中鍵
Shift 參數明確 Shift 鍵,Ctrl 鍵和 Alt 鍵的狀態:
1- 選擇了 Shift 鍵,2 – 選擇
了 Ctrl 鍵,以及 4 – 選擇了 Alt
鍵參數 x, y 分別明確滑鼠指
針座標
ouseMove
MouseUp
7.內嵌圖表事件
當滑鼠指標座標在圖表之上變化時,引發該事件
當滑鼠按鍵在圖表之上釋放時,引發該事件
想要捕捉工作表內嵌圖表的事件的話,那麼你首先要在類別模組裡使用關鍵字 WithEvents 創建一個
新的物件。我們按照下述步驟來看看如何實現它:
1. 啟動 VB 編輯器視窗
2. 在工程流覽器裡,選擇 VBAProject(ChartEvents.xls)
3. 選擇插入|類別模組
4. 在類別模組資料夾裡,你將看到一個名叫 Class1 的模組
5. 在屬性視窗,將 Class1 重命名為 clsChart
6. 在類別模組的代碼視窗裡,聲明一個物件變數,它將代表產生事件的圖表物件
Public WithEvents xlChart As Excel.Chart 關鍵字 Public 將使物件變數 xlChart 可用於當
前 VBA 工程裡的所有模組。使用 WithEvents 關鍵字聲明一個物件變數,將暴露該被定義物件
類型的所有事件。輸入上述聲明之後,物件變數 xlChart 就會添加到代碼視窗左上角地物件
下拉清單中去,而與該物件變數相對應的事件就會出現在代碼視窗右上角的過程下拉清單
裡面。
7. 打開物件下拉式清單方塊並選擇變數 xlChart 名稱,現在代碼視窗將出現 xlChart_Activate 過程的
構架:
Private Sub xlChart_Activate()
End Sub
8. 在該事件程序裡添加你的代碼。在本練習中,我們將添加一條語句,顯示一個資訊框。添
加完語句後,VBA 過程應該像這樣:
Private Sub xlChart_Activate()

MsgBox "You’ve activated a chart embedded in " & _

ActiveSheet.Name
End Sub
輸入完事件程序後,你仍然需要通知 VB,你計畫使用它。
9. 在工程流覽器視窗,按兩下 ThisWorkbook 物件,並且輸入下述語句來創建名為 clsChart 的新示例:
Dim myChart As New clsChart 上面顯示的指令聲明一個名為 myChart 的物件變數,該變數將
指向位於類別模組 clsChart 裡的對象 xlChart。關鍵字 New 告訴 VB 去創建特定物件的新示例。
10. 在 ThisWorkbook 代碼視窗輸入下述過程,以初始化物件變數 myChart:
Sub InitializeChart()

' connect the class module and its objects with the Chart object

Set myChart.xlChart = _

Worksheets(1).ChartObjects(1).Chart

End Sub
11. 運行 InitializeChart 過程。運行該過程之後,輸入在類別模組裡的事件程序就會被相應的事件
引發
12. 啟動 Excel 視窗,並且點擊內嵌圖表。這次,你在第七步輸入的 xlChart_Activate 事件將會
被引發
13. 現在,你可以在類別模組裡給內嵌圖表輸入其它的事件程序了。
284
8.可為應用軟體物件識別的事件
如果你想要你的事件程序無論在哪個當前活動的 Excel 工作簿上都能執行的話,那麼你需要創建
應用軟體物件的事件程序。應用軟體物件的事件程序具有全域性。這意味著只要 Excel 應用軟體
是開啟的,那麼該過程代碼就會回應某個事件被執行。Application 物件的事件列在接下來的表
格中。和內嵌圖表類似,Application 物件的事件程序要求你在類別模組裡使用關鍵字 WithEvents
創建一個新的物件。表格中示範的事件事例程式應該在類別模組裡面輸入。如何操作呢?在 VB 編輯
器視窗選擇插入|類別模組,在屬性視窗重命名類別模組為 clsApplication。在 clsApplication 代碼
視窗聲明一個物件變數來代表 Application 物件,如下所示:
Public WithEvents App As Application 在聲明語句下面,輸入下述事件程序,如表格所示:
App_NewWorkbook,App_WorkbookOpen,App_WorkbookBeforeSave, App_WorkbookBeforeClose,
App_Sheet- SelectionChange 和 App_WindowActivate 事件程序。你輸入完這些過程在類別模組裡
之後,插入一個標準模組到當前 VBA 工程裡(選擇插入|模組)。在標準模組裡,創建一個類
clsApplication 的新示例,並且將位於類別模組 clsApplication 裡的物件和代表 Application 物件
的物件變數 App 連接起來,如下所示:
Dim DoThis As New clsApplication
Public Sub InitializeAppEvents ()

Set DoThis.App = Application

End Sub 現在將滑鼠游標置於過程 InitializeAppEvents 裡並且按下 F5 鍵運行它。運行過程


InitializeAppEvents 的結果是類
模組的物件 App 將會指向 Excel 應用軟體。從現在開始,當某個具體事件發生時,你已經輸入在
類別模組裡的事件程序就會被執行。如果你不想相應 Application 物件產生的事件的話,你可以通
過在一個標準模組裡輸入下述過程(並運行它):
Public Sub CancelAppEvents()

Set DoThis.App = Nothing

End Sub
當你設置物件變數為 Nothing 的時候,你實際上釋放了記憶體,並且斷開了物件變數和該變數指向的
物件之間的連接。當你運行過程 CancelAppEvents 後,寫在類別模組裡面的事件程序在某個事件發
生就不會自動執行。注意:所有在本表格裡示範的事件程序,都需要你在類別模組裡使用關鍵字
WithEvents 聲明個物件變數。
事件名稱
NewWorkbook
WorkbookOpen
WorkbookActivate
WorkbookDeactivate
WorkbookNewSheet
WorkbookBeforeSave
描述
當用戶創建一個新工作簿時引發該事件
Private Sub App_NewWorkbook(ByVal _
Wb As Workbook)
Application.DisplayAlerts = False

If Wb.Sheets.Count = 3 Then

Sheets(Array(2, 3)).Delete

End If
Application.DisplayAlerts = True

End Sub
該事件發生於用戶打開一個工作簿
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)

If Wb.FileFormat = xlCSV Then

If MsgBox("Do you want to save " & vbCrLf _

& " this file as an Excel workbook?", vbYesNo, _

"Original file format: " _

& "Comma delimited file") = vbYes Then

Wb.SaveAs FileFormat:=xlWorkbookNormal

End If
End If
End Sub
當用戶將焦點移到一個開啟的工作簿時引發該事件
當用戶將焦點從一個開啟的工作簿移開時引發該事件
當用戶在一個打開的工作簿上新建一個工作表時引發該事件
該事件發生在以大開工作簿被保護之前
285
WorkbookBeforePrint
WorkbookBeforeClose
ErrorHandle:
WorkbookAddInInstall
WorkbookAddInUninstall
SheetActivate
SheetDeactivate
SheetSelectionChange

Private Sub App_WorkbookBeforeSave(ByVal _

Wb As Workbook, _
ByVal SaveAsUI As Boolean, _

Cancel As Boolean)
If Wb.Path <> vbNullString Then

ActiveWindow.Caption = Wb.FullName & _

" [Last Saved: " & Time & "]"


End If
End Sub
該事件發生在以大開工作簿被列印之前
Private Sub App_WorkbookBeforePrint(ByVal _

Wb As Workbook, Cancel As Boolean)


Wb.PrintOut
Copies:=2
End Sub
該事件發生於關閉工作簿之前
Private Sub App_WorkbookBeforeClose( ByVal _

Wb As Workbook, Cancel As Boolean)

Dim r As Integer
Sheets.Add
r=1
For Each p In Wb.BuiltinDocumentProperties

On Error GoTo ErrorHandle


Cells(r, 1).Value = p.Name & " = " &

ActiveWorkbook. _
BuiltinDocumentProperties _

.Item(p.Name).Value

r=r+1
Next
Exit Sub
Cells(r, 1).Value = p.Name
Resume Next
End Sub
該事件發生於用戶安裝增益集之時
該事件發生於用戶卸載增益集之時
當用戶啟動開啟工作簿中某個工作表時引發該事件
當用戶離開某個工作表時引發該事件
當使用者改變選擇工作表上的區域時引發該事件
Private Sub App_SheetSelectionChange( _

ByVal Sh As Object, ByVal Target As Range)

If Selection.Count > 1 Or _

(Selection.Count < 2 And _

IsEmpty(Target.Value)) Then

Application.StatusBar = Target.Address

Else
End If Application.StatusBar = Target.Address & _
End Sub "(" & Target.Value & ")"
SheetChange

SheetCalculate
SheetBeforeDoubleClick

SheetBeforeRightClick

WindowActivate
當用戶在一個打開的工作簿裡改變儲存格裡的內容時引發該事
當用戶重新計算某個開啟的工作簿裡的工作表時引發該事件
當用戶按兩下工作表時引發該事件
當用戶按右鍵工作表儲存格時引發該事件
當使用者啟動一個打開的視窗時引發該事件
Private Sub App_WindowActivate(ByVal _

Wb As Workbook, ByVal Wn As Window)

Wn.DisplayFormulas = True
286
WindowDeactivate
WindowResize
WorkbookPivotTableClose-
Connection
(Excel 2002 的新事件)
WorkbookPivotTableOpen-
Connection
(Excel 2002 的新事件)
End Sub
當使用者將焦點從開啟的視窗移走時引發該事件
當使用者調整開啟的視窗的大小時引發該事件
在樞紐分析表報告連接被斷開後,引發該事件
在樞紐分析表報告連接被打開後引發該事件
287
9.查詢表時間
查詢表是 Excel 工作表裡代表從外部資料來源得來的資料,例如 SQL 伺服器資料庫,Access 資料
庫,網頁,或者文字檔。查詢表用物件 QueryTable 代表。Excel 為 QueryTable 物件提供了兩
種事件:BeforeRefresh 和 AfterRefresh。想要試驗一下本章後面示範的這些示例過程的話,
那麼請執行下面的這些操作。本示例假設你的機器上安裝了 Access 以及其例子 Northwind 資料
庫。
1. 在 Excel 應用軟體視窗,選擇資料|導入外部資料,並且選擇新建資料庫查詢以創建一個新
資料庫查詢
2. 在資料來源對話方塊裡,選擇新資料來源,並點擊確定
3. 在創建新資料來源對話方塊裡,輸入 SampleDb 作為資料來源名稱
4. 在創建新資料來源對話方塊上,從第二步旁邊的下拉清單裡,選擇 Microsoft Access driver
(*.mdb)
5. 點擊連接按鈕
6. 在 ODBC Microsoft Access 安裝對話方塊上點擊選擇按鈕
7. 在選擇資料庫對話方塊上,找到檔 Northwind.mdb。該檔通常可以在 C:\Program
Files\Microsoft Office\Office\Samples 資料夾找到(譯者用的是 Office 2003,沒有
該檔,有一個類似的 Nwind.mdb 文件。大家可以在電腦上查找一下)
8. 選擇該檔並且點擊確定以關閉該選擇資料庫對話方塊
9. 在點擊確定退出 ODBC Microsoft Access 安裝對話方塊
10. 在創建新資料來源對話方塊的第四步,在下拉式清單方塊裡選擇 Categories
11. 點擊確定以關閉創建新資料來源對話方塊
12. 在選擇資料來源對話方塊上,資料來源名稱 SampleDb 現在應該被加亮了,點擊確定
13. 在查詢嚮導– 選擇列對話方塊裡,點擊>按鈕,將 Categories 表中所有的區域移到查詢框
的列中去
14. 點擊下一步,直到你看到查詢嚮導– 完成對話方塊
15. 在查詢嚮導– 完成對話方塊上,確保將資料返回到 Microsoft Excel 選項按鈕是被勾選上
的,並且點擊完成
16. 在導入資料對話方塊,當前試算表儲存格是被選中的,點擊儲存格 A1 並點擊確定關閉對
話框。完成上述步驟後,Northwind 資料庫裡 Catetory 表中的資料應該被放置在當前工作表裡
面了。重新獲得資料是得花費好些步驟的。在下章,你將學習如何程式設計創建查詢表。想要給
查詢表物件編寫事件程序的話,你就必須創建一個類別模組並且使用 WithEvents 關鍵字聲明一
個 QueryTable 對象。
1. 插入類別模組到當前 VBA 工程並重命名為 clsQryTbl
2. 在 clsQryTbl 代碼視窗,輸入下述語句:
Public WithEvents qrytbl As QueryTable

當你使用 WithEvents 關鍵字聲明完新物件 qrytbl 後,它就會出現在類別模組的物件下拉清單



3. 在 clsQryTbl 代碼視窗,輸入兩個事件程序,如下面的表格所示:QryTbl_BeforeRefresh
和 QryTbl_AfterRefresh。在你能夠引發這些事件程序之前,你必須將你在類別模組裡聲
明的物件和某個特定的 QueryTable 物件連接起來
4. 插入一個標準模組,並輸入下述代碼:
Public Sub Auto_Open()
' connect the class module and its objects with the Query object

Set sampleQry.qrytbl = ActiveSheet.QueryTables(1)

End Sub 上面的程式創建了一個 QueryTable 類(clsQryTbl)的新示例,並且將它和活動


工作表裡的第一個查詢表連接起來。當你打開該工作簿時,Auto_Open 過程會自動執行。
因此你不必手動運行它,以確保當資料被刷新時,查詢事件將會被引發。
5. 運行第四步輸入 Auto_Open 過程,在你運行完該初始化過程後,你在類別模組裡聲明的物件就
會指向特定的查詢表物件
6. 在你放置從 Access 裡導入的 Category 的工作表裡,更改某個類別。選擇查詢表中的任意單
元格,並且點擊外部資料工具列上的刷新資料,或者選擇資料|刷新資料。這次,事件程序
qrytbl_BeforeRefresh 將會被引發了,你將看到一個自訂資訊框。如果你點擊是,該
資料將會被資料庫裡存在的資料刷新掉,你更改過的資料將會被覆蓋掉。
事件名稱描述
288
BeforeRefresh
AfterRefresh
數 Success
10.接下來……
該事件發生在查詢表被刷新資料之前
Private Sub qryTbl_BeforeRefresh(Cancel As Boolean)

Response = MsgBox("Are you sure you " _

& " want to refresh now?", vbYesNoCancel)

If Response = vbNo Then Cancel = True

End Sub
該事件發生在查詢完成或者被取消。如果查詢成功完成則參
為 True。
Private Sub qryTbl_AfterRefresh(ByVal Success As

Boolean)
If Success Then
MsgBox "The data has been refreshed."

Else
MsgBox "The query failed."

End If
End Sub
在本章中,你獲得了便利的事件經驗和 Excel 的事件程式設計,這是無價的技術,不管你是否
計畫給他人創建試算表應用軟體,還是簡單地將你的日常任務自動化。Excel 提供了許多你
可以回應的事件。通過編寫事件程序,你可以更改物件對事件的響應方式。你的事件程序可以
簡單為一條語句,僅僅顯示一自訂資訊;也可以複雜到包括一些判斷語句和其它允許你改變你
的程式流的程式設計結構。當某個事件發生時,VB 將會直接運行適當的事件程序,而不是按標準
的內置方式進行回應。你已經學習了一些編寫在標準模組裡的事件程序(工作簿,工作表,
圖表工作表),然而,其它的(內嵌圖表,應用軟體,查詢表)則需要你在類別模組裡面使用
WithEvents 關鍵字創建一個新物件。你也學習了你可以使用 EnableEvents 屬性啟動或者禁止事
件。
在下章,你將學習如何使用 Excel VB 環境下的 VBA 過程來使用 Access 資料庫。
第十五章在 Excel 裡使用 Access
在第九章裡面,你已經學習了從 Excel 裡通過自動控制(用於允許一個應用程式控制另外
一個應用程式的物件)來操縱 Word 和 Outlook。本章將給演示如何程式設計從 Excel 裡使用 Access,
使用下述方法獲取 Access 資料到試算表裡面:Automation,DAO (Data Access Objects)以及
ADO (ActiveX Data Objects)。在你學習如何使用 Excel VBA 在 Access 資料庫裡執行各種任務
以及獲取和存儲資料於 Access 資料庫之前,我們來粗略地介紹一下,Microsoft Access 用來
程式設計對其物件訪問的資料存取方法。
1.物件程式庫
Access 資料庫包含各種類型的物件,儲存在不同的物件程式庫裡面,用來使用 VBA 語言顯示、存
儲或者管理資料。在本章,你將涉獵下面列出的幾個庫裡的物件、屬性和方法。
Access 10.0 物件程式庫提供了用來顯示資料和在 Access 2002 應用軟體上使用的物件。該庫儲存子
在 MSACC10.OLB 檔裡,並且
可以自阿 C:\Program Files\Microsoft Office\Office 資料夾裡面找到。在引用對話方塊上設置
了對該庫的引用之後(將在下節涉及), 你將能夠在物件流覽器裡面訪問該庫的物件、屬性和
方法(參見圖 15-1)。
289
圖 15-1 Access 庫(譯者:截圖為 Office 2003。Access 庫文件為 MSACC.OLB)
Access DAO 3.6 物件程式庫提供了資料訪問物件(DAO),
該庫儲存在 DAO360.DLL
檔裡,並且可以在 C:\Program Files\Common Files\Microsoft Shared\DAO 資料夾裡找到。
在引用對話方塊上設置了對該庫的引用之後(將在下節涉及),你將能夠在物件流覽器裡面訪
問該庫的物件、屬性和方法(參見圖 15-2)。
圖 15-2 DAO 庫
Microsoft ActiveX Data Objects 2.5 (ADO) 提供了控制項資料物件(ADO)並且允許你使用 OLE DB
供應者訪問和運算元據。ADO
使得在 Access 資料庫裡對資料來源創建連結,讀取,插入,修改和刪除資料成為可能。該庫儲存
於 MSADO15.DLL 裡面,並可以
在 C:\Program Files\Common Files\system\ado 資料夾裡找到。在引用對話方塊上設置了對該庫
的引用之後,你將能夠在物件流覽器裡面訪問該庫的物件、屬性和方法(參見圖 15-3)。
290
圖 15-3 ADODB 庫
Microsoft ADO Ext. 2.5 for DDL(動態資料鏈接) and Security(安全) (ADOX) 儲存讓你
定義資料庫結構和安全的物件。例如, 你可以定義表格,索引和關係,以及創建和修改用戶
和用戶群組帳戶。
該庫儲存在 MSADOX.DLL 裡並且可以在 C:\Program Files\Common Files\System\ado 資料夾裡找
到。在引用對話方塊上設置了對該庫的引用之後,你將能夠在物件流覽器裡面訪問該庫的物件、
屬性和方法(參見圖 15-4)。
圖 15-4 ADOX 庫
Microsoft Jet and Replication Objects 2.6 庫(JRO)包含用於物件程式庫複製的物件。該庫儲存
在 MSJRO.DLL 裡並在 C:\Program
Files\Common Files\System\ado 資料夾裡可以找到。在引用對話方塊上設置了對該庫的引用之
後,你將能夠在物件流覽器裡面訪問該庫的物件、屬性和方法(參見圖 15-5)。
291
圖 15-5 JRO 庫
VBA 物件程式庫提供了很多 VBA 物件,函數和方法供你訪問檔案系統,操作日期和時間函數,進行數學
和財務計算,與用戶互動,轉
換數據和讀取文字檔。該庫儲存在 VBE6.DLL 文件裡,位於 C:\Program Files\Common
Files\Microsoft Shared\VBA\VBA6 資料夾裡。當你安裝 Microsoft Excel 2002 時,就會自動
設置對該庫的引用。該庫在 Office 2002 所有的應用軟體中共用(參見圖 15-6)。
圖 15-6 VBA 庫
2.建立對物件程式庫的引用
要操作 Access 2002 裡的物件的話,首先就得創建對 Microsoft Access 10.0 物件程式庫的引用。
1. 在 VB 編輯器視窗,選擇工具|引用以打開引用對話方塊。該對話方塊列出了你電腦上所有可用的
型別程式庫
2. 在清單上找到 Microsoft Access 10.0 Object Library,並勾選上它
3. 關閉引用對話方塊一旦創建了對 Access 型別程式庫的引用,你就可以使用物件流覽器來查看該
應用軟體的物件,屬性和方法了(參見前面的圖 15-1)。使用引用對話方塊創建對其它將在本章
練習中訪問物件程式庫的引用。你可以在前面部分裡找到庫清單。你可以忽略對 Microsoft Jet and
Replication Objects 2.6 Library (JRO)的引用,因為這裡不會用到它。如果你對資料庫
複製有興趣的話,那麼可以找到很多有關 Access 程式設計的書涉及該主題,包括本人的書 Learn
Microsoft Access
292
2000 Programming by Example (Wordware Publishing (ISBN 1-55622-770-1))。
技巧 15-1 創建對 Access 物件程式庫引用的好處
當你設置對 Access 物件程式庫的引用時,你將獲得下述好處:
- 你可以在物件流覽器裡面查看 Access 的物件,屬性和方法
- 你可以在 VBA 過程裡直接運行 Access 函數
- 你可以聲明該應用軟體類型的物件變數,而不必聲明普通的 Object 類型。聲明變數為 Dim
objAccess As Access.Application

(早期捆綁)要比將其聲明為 Dim objAccess As Object (後期捆綁)要快。


- 你可以在你的 VBA 代碼裡使用 Access 內置常量
- 你的 VBA 過程將運行得更快一些
3.連結到 Access
本章中的示例使用了多種連結到 Access 的方法,本節將詳細討論每種連結方法。你可以使用
下述三種方法之一來建立對 Access 的連結:
□ Automation
□ Data Access Objects (DAO)
□ ActiveX Data Objects (ADO)
要訪問資料庫裡的資料的話,你就得打開它。如何打開某個具體的資料庫,很大程度上取決於
你使用了哪種資料庫的連結方法。
4.使用 Automation 連結到 Access 資料庫
當你通過 Automation 從 Excel(或者其它應用軟體)裡使用 Access 時,你需要採取下述步驟:
1. 設置對 Microsoft Access 10.0 Object Library 的引用(參見本章前面的“建立對物件程式庫
的引用”)
2. 聲明一個物件變數,代表 Access 應用軟體物件
Dim objAccess As Access.Application
在該聲明中,objAccess 為變數名稱,而 Access.Application 用提供該物件的 VB 物件程式庫的名
稱來限定該變數。
3. 返回引用到應用軟體物件上,並且將該引用到該物件變數。使用 CreateObject 函數,
GetObject 函數或者關鍵字 New 來返回應用軟體物件的引用。使用 Set 語句將應用賦值到對
象變數。
Dim objAccess As Object

Set objAccess = CreateObject(“Access.Application.10”) 當物件還沒有實例時,可


以使用 CreateObject 函數返回一個引用到應用軟體物件。如果 Access 已經運行了的話,
那麼新的實例就已創建了並且確定的物件也已創建了。
Dim objAccess As Object

Set objAccess = GetObject(, “Access.Application.10”)


或者
Set objAccess = GetObject(“C:\Program Files\ _
& “Microsoft Office\Office\Samples\Northwind.mdb”) 使用 GenObject 函數返回引
用到應用軟體物件,以使用 Access 的當前實例,或者開啟 Access 並打開一個檔(更多資訊
參見技巧 15-2)
Dim objAccess As New Access.Application 上面的語句使用關鍵字 New,聲明了一個
物件變數,返回引用到應用軟體物件,並且將該引用賦予物件變數,一步完成。你也可
以使用兩步法來聲明一個物件變數,這樣,對該物件的控制會更多些:
Dim objAccess As Access.Application

Set objAccess = New Access.Application

□ 當使用關鍵字 New 聲明物件變數時,Access 應用軟體並不會打開,直到你開始在 VBA 代碼裡


真正使用該物件變數。
□ 當使用關鍵字 New 聲明應用軟體物件變數時,Access 的一個新實例就會自動創建,你不需要
使用 CreateObject 函數
□ 使用關鍵字 New 創建應用軟體物件的新實例比使用 CreateObject 函數要快因為電腦上
可能安裝了多個版本的 Access,所以需要在函數 GetObject 或者 CreateObject 裡包括該版本
號。下面列出最後四個 Access 版本:
Microsoft Access 2003 Access.Application.11 (譯者加)
Microsoft Access 2002 Access.Application.10

Microsoft Access 2000 Access.Application.9

Microsoft Access 97 Access.Application.8

293
Microsoft Access 95 Access.Application.7 一旦使用第三步列出的方法之一創建了應用軟
件類的新實例,你就可以通過 OpenCurrentDatabase 或者 NewCurrentDatabase 方法的幫助來打
開一個資料庫或者創建一個新資料庫。你可以使用 CloseCurrentDatabase 方法關閉你在程式裡
打開的 Access 資料庫。
技巧 15-2 GetObject 函數的參數
GetObject 函數的第一個參數– Pathname – 是可選的。當你想要使用某個特定檔裡的物件
是要用到它。第二個參數– Class –
是必需的,它明確哪個應用軟體創建該物件,以及該物件的類型。當第一個參數為可選的而第二
個為必需的時,你就必須在第一
個參數位置放置一個逗號,如下所示:
Dim objAccess As Object

Set objAccess = GetObject(, Access.Application.10”)


因為 GetObject 函數的第一個參數(Pathname)被忽略了,所以,就返回對 Access 應用軟體類的
現存的實例的引用。
Dim objAccess As Object

Set objAccess = GetObject(“C:\Program Files\ & “Microsoft


Office\Office\Samples\Northwind.mdb”)
如果 GetObject 函數的第一個參數是個資料庫檔的名稱,那麼就會使用該具體資料庫,啟動或
者創建 Access 應用軟體類的新實例。
既然你知道了如何創建代表應用軟體的物件變數,那麼我們就來看看一個從 Excel VBA 過程裡直
接打開 Access 資料庫的程式示例吧。下頁顯示的過程 AccessViaAutomation 將打開一個 Access
檔 Northwind 資料庫。該過程將使用 Access 自動控制伺服器的當前實例,如果它可用的話。
如果 Access 沒有運行,執行時間錯誤將發生,並且該物件變數將被設置為 Nothing。你可以通
過在程式裡放置 On Error Resume Next 語句捕捉該錯誤。因此,如果 Access 沒有運行,新的
Access 實例就會被打開。本例子使用關鍵字 New 來啟動 Access 的新實例。正如前面所述,除了
使用關鍵字創建新物件實例之外,你也可以使用 CreateObject()函數來啟動
自動控制伺服器的新實例,如下所示:
Set objAccess = GetObject(, “Access.Application.10”)
If objAccess Is Nothing Then

Set objAccess = CreateObject(“Access.Application.10”)


End If 當你使用自動控制啟動 Access 時,你將在工作列上看到 Access 圖示。Access 應用軟體
物件的 Visible 屬性被設置為 False。想要恢復該應用軟體視窗的話,就得將其 Visible 屬性
設置為 Trie。在使用時,物件要消耗記憶體和系統資源。要釋放這些資源的話,那麼你每次使
用完它的時候總應該關閉該物件。下面示範的程式例子首先使用 CloseCurrentDatabase 方法
關閉 Northwind 資料庫。接著,使用 Quit 方法關閉 Access 應用程式物件。在關閉對象後,你也應
該將物件變數設置為關鍵字 Nothing 以釋放該變數使用的記憶體資源。你可以將物件變數聲明為
模組層級,而不是過程級變數,以避免 Access 實例被關閉。在這樣的環境下,對資料庫的連結
就會保持,直到你關閉該自動控制的控制源(Excel)或者在 VBA 代碼裡使用 Quit 方法關閉它。
Sub AccessViaAutomation()
Dim objAccess As Access.Application

Dim strPath As String


On Error Resume Next

Set objAccess = GetObject(, "Access.Application.9")

If objAccess Is Nothing Then

' Get a reference to the Access Application object

Set objAccess = New Access.Application

End If
strPath = "C:\Program Files\Microsoft Office\" _

& "Office\Samples\northwind.mdb"

' Open the Northwind database

With objAccess
.OpenCurrentDatabase strPath

If MsgBox("Do you want to make the Access " & vbCrLf _

& "Application visible?", vbYesNo, _

"Display Access") = vbYes Then

294
.Visible = True
MsgBox "Notice the Access Application icon " _

& "now appears on the Windows taskbar."

End If
' Close the database and quit Access

.CloseCurrentDatabase

.Quit
End With
Set objAccess = Nothing

End Sub
使用 F8 鍵逐語句運行上面的過程。
技巧 15-3 打開被保護了的 Access 資料庫
如果該 Access 資料庫用密碼保護了,那麼會提示使用者輸入正確的密碼。你必須使用 Data Access
Objects (DAO)或者 ActiveX Data
Access (ADO)來程式設計打開密碼保護的 Access 資料庫。下面的例子使用了 Access 物件的 DBEngine
屬性來確定該資料庫的密碼。
要讓該過程工作的話,你就必須創建對 Microsoft DAO 3.6 物件程式庫的引用,如本章開頭所述。
Sub OpenSecuredDB()

Static objAccess As Access.Application

Dim db As DAO.Database
Dim strDb As String

strDb = "C:\Program Files\Microsoft Office\" & "Office\Samples\ _

Northwind.mdb"
Set objAccess = New Access.Application

Set db = objAccess.DBEngine.OpenDatabase(Name:=strDb, Options:=False, _

ReadOnly:=False, Connect:=";PWD=test")

With objAccess
.Visible = True
.OpenCurrentDatabase strDb
End With
db.Close
Set db = Nothing
End Sub
5.使用 DAO 連結到 Access 資料庫
要使用資料訪問物件(DAO)連接到 Access 資料庫的話,你就必須首先在引用對話方塊裡創建對
Microsoft Data Access Objects 3.6

Library 的引用。下面示範的程式例子 DAOOpenJetDatabase,使用 DBEngine 物件的 OpenDatabase


方法來打開 Northwind 資料庫, 並且通知使用者該資料庫已被打開。DBEngine 對象讓你初始化稱
為 Microsoft Jet Engine 的標準資料庫引擎並打開一個資料庫檔
(.mdb)。過程使用 Close 方法關閉資料庫檔。
Sub DAOOpenJetDatabase()
Dim db As DAO.Database
Set db = DBEngine.OpenDatabase _

("C:\Program Files\Microsoft Office\Office\Samples\Northwind.mdb")

MsgBox "Northwind database has been opened."

db.Close
MsgBox "Northwind database has been closed."

End Sub
6.使用 ADO 連結到 Access 資料庫
最新的,最建議的建立對 Access 資料庫連結的方法是使用 ActiveX 資料物件(ADO)。你必須先
設置對微軟 ActiveX 資料物件 2.5 庫或者更高版本的引用。示例程式 ADOOpenJetDatabase 使用
Connection 物件連結到 Northwind 資料庫。該物件通過 Open 方法打開。注意,Open 方法需要
一個包含資料提供者名稱(本例中為 Microsoft.Jet.OLEDB.4.0)和資料來源名稱(本例中為要
打開的資料庫檔完整名稱)的連結字串參數:
con.Open _
"Provider=Microsoft.Jet.OLEDB.4.0;" _

& "Data Source=C:\Program Files\Microsoft Office\" _

& "Office\Samples\NorthWind.mdb;" 在建立對 Northwind 資料庫的連結之後,你可以使用


Recordset 物件來訪問其資料。Recordset 物件用來在記錄級運算元據。Recordset 對象由記
295
錄(行)和欄位(列)組成。要獲得一套記錄,你就得使用 Open 方法打開 Recordset。該方法
需要明確的資訊,例如 Recordset 記錄源:
rst.Open "SELECT * FROM Customers " & _
"WHERE City = 'London'", con, _

adOpenForwardOnly, adLockReadOnly 記錄源可以是返回記錄的資料庫表,或查詢或 SQL 語


句。在明確記錄源後,你還需要表明對資料庫(con)和兩個常數,一個定義指標類型
(adOpenForwardOnly),另一個為鎖定類型(adLockReadOnly)。常數 adOpenForwardOnly
告訴 VBA 創建只能向前翻的 Recordset。第二個常數 adLockReadOnly 明確在編輯時記錄上的鎖
定類型。該記錄為唯讀,意味著你不能改變該資料。過程的下一部分使用 For…Each…Next 循
環遍歷 Recordset 並將第一條記錄的內容列印到立即視窗:
For Each fld In rst.Fields

Debug.Print fld.Name & "=" & fld.Value & vbCr

Next
在獲取第一條記錄的資料後,過程使用了 Close 方法關閉 Recordset 和對 Access 資料庫的連結:
rst.Close
con.Close
ADOOpenJetDatabase 過程如下:
Sub ADOOpenJetDatabase()
Dim con As New ADODB.Connection

Dim rst As New ADODB.Recordset

Dim fld As ADODB.Field

' Connect with the database

con.Open _
"Provider=Microsoft.Jet.OLEDB.4.0;" _

& "Data Source=C:\Program Files\Microsoft Office\" _

& "Office\Samples\NorthWind.mdb;"

' Open Recordset based on the SQL statement

rst.Open "SELECT * FROM Customers " & _

"WHERE City = 'London'", con, _


adOpenForwardOnly, adLockReadOnly

' Print the values for the fields in

' the first record in the debug window

For Each fld In rst.Fields

Debug.Print fld.Name & "=" & fld.Value & vbCr

Next
' Close the Recordset and connection with Access

rst.Close
con.Close
' Destroy object variables to reclaim the resources

Set rst = Nothing


Set con = Nothing
End Sub
7.從 Excel 執行 Access 任務
從 Excel 連結到 Access 後,你就可以執行 Access 應用軟體的不同任務。本節示範如何使用 VBA 代
碼來:
□ 創建新 Access 資料庫
□ 打開現存在的資料庫表
□ 創建全新的資料庫表
□ 打開資料庫報表
□ 運行 Access 函數
8.創建新 Access 資料庫
如果你想要通過程式設計將 Excel 資料傳送到一個新的 Access 資料庫裡面,那麼你需要使用 VBA 代
碼創建一資料庫。下面的過程示例示範了如何使用 DAO 來建立和 Access 的連結。Workspace 對象
的 CreateDatabase 方法創建一個名為 ExcelDump.mdb 的新資料庫於 C 盤根目錄下。然後,
Database 物件的 CreateTableDef 方法用來創建一個名為 tblStates 的表。在表能夠添加到資料
庫之前,必須先創建一個欄位並附在該表上。該過程創建了三個文本欄位(dbText),每個分
別可以儲存 2,25 和 25 個字元。每個欄位創建後,使用 Append 方法將這些欄位添加到 TableDef
296
物件的 Fields 集合裡。欄位一旦創建並添加到表之後,表本身就會使用 Append 方法被添加到
資料庫。因為名為“C:\ExcelDump.mdb”的資料庫可能已經存在於該目錄下,該過程包括了一
個錯誤處理常式,將刪除現有檔,以確保資料庫創建過程繼續。因為其它錯誤也可能發生,
Else 子句包括了顯示錯誤描述的資訊,並允許退出過程
Sub NewDB_DAO()
Dim db As DAO.Database
Dim tbl As DAO.TableDef
Dim strDb As String
Dim strTbl As String
On Error GoTo Error_CreateDb_DAO
strDb = "C:\ExcelDump.mdb"
strTbl = "tblStates"
' Create a new database named ExcelDump

Set db = CreateDatabase(strDb, dbLangGeneral)


' Create a new table named tblStates

Set tbl = db.CreateTableDef(strTbl)


' Create fields and append them to the Fields collection

With tbl
.Fields.Append .CreateField("StateId", dbText, 2)

.Fields.Append .CreateField("StateName", dbText, 25)

.Fields.Append .CreateField("StateCapital", dbText, 25)

End With
' Append the table object to the TableDefs

db.TableDefs.Append tbl

' Close the database


db.Close
Set db = Nothing
MsgBox "There is a new database on your hard disk. " & vbCrLf _

& "This database file contains a table " & strDb & vbCrLf _

& "named " & strTbl & "." & vbCrLf _

& "Before you activate this database, close the Excel application."
Exit_CreateDb_DAO:

Exit Sub
Error_CreateDb_DAO:
If Err.Number = 3204 Then
' Delete the database file if it already exists

Kill "C:\Exceldump.mdb"

Resume
Else
MsgBox Err.Number & ": " & Err.Description

Resume Exit_CreateDb_DAO

End If
End Sub
297
圖 15-7 Excel VBA 過程創建的 Access 資料庫表
9.打開 Access 表單
你可以從 Excel 裡打開 Access 表單。你也可以創建新表單。下述例子使用自動控制連結到
Access。一旦連結建立後,就使用 OpenCurrentDatabase 方法來打開例子 Northwind 資料庫。
接著,使用 DoCmd 物件的 OpenForm 方法打開 Customers 表單。該表單被打開為普通視圖
(acNormal)。如果要將表單在設計檢視裡打開的話,那麼可以使用 acDesign 常數代替。DoCmd
物件的 Restore 方法確保該表單顯示在螢幕上而不是最小化。Access 應用軟體物件
(objAccess)的 Visible 屬性必須設置為 True,以確保表單可見。注意,Access 應用軟體的
物件變數(objAccess)在模組上面聲明。為了讓該過程運行正確,你必須建立對 Access 物件程式庫
的引用。圖 15-8 顯示了被打開的 Customers 表單。
‘ declare at the top of the module
Dim objAccess As Access.Application

Sub DisplayAccessForm()
Dim strDb As String

Dim strFrm As String

strDb = "C:\Program Files\Microsoft Office\" _

& "Office\Samples\Northwind.mdb"

strFrm = "Customers"
Set objAccess = New Access.Application

With objAccess
.OpenCurrentDatabase(strDb)

.DoCmd.OpenForm strFrm, acNormal

.DoCmd.Restore
.Visible = True
End With
End Sub
298
圖 15-8 可以用 Excel VBA 過程打開的 Access 表單
如果你還想在程式設計中再進一步的話,那麼從 Excel VBA 過程裡創建一個全新的 Access 表單,如下
所示:
‘ declare at the top of the module
Dim obAccess As Access.Application (譯者:原文為 myAccess)
Sub CreateAccessForm()
Dim myForm As Form

Dim myDb As String

Dim myCtrl As Control


Dim strFrmName As String

On Error GoTo Error_CreateForm

myDb = "C:\Program Files\Microsoft Office\" _

& "Office\Samples\Northwind.mdb"

strFrmName = "frmCustomForm"

Set obAccess = New Access.Application

obAccess.OpenCurrentDatabase myDb

Set myForm = obAccess.CreateForm

myForm.Caption = "Form created by Excel"

myForm.RecordSource = "Employees"

obAccess.DoCmd.Save , strFrmName

' Create a label and text box on the form

Set myCtrl = CreateControl(FormName:=strFrmName, _

ControlType:=acLabel, _
Left:=1000, Top:=1000)

myCtrl.Caption = "Last Name:"

myCtrl.SizeToFit
Set myCtrl = CreateControl(FormName:=strFrmName, _

ControlType:=acTextBox, _
Parent:="", _
299
ColumnName:="LastName", _

Left:=2200, Top:=1000)

With obAccess
With .DoCmd
.Save , strFrmName

.Close acForm, strFrmName

End With
.CloseCurrentDatabase

.Quit
End With
Set obAccess = Nothing

MsgBox "In the Northwind database there is now " & vbCrLf _

& "a new form named " & strFrmName & "." & vbCrLf _

& "Close Excel prior to opening the Northwind " & vbCrLf _

& "database to view this form."

ErrorHandler:
Exit Sub
Error_CreateForm:
MsgBox Err & " :" & Err.Description

Resume ErrorHandler
End Sub
圖 15-9 Access 表單可以由 Excel VBA 過程創建(參見上面的
CreateAccessForm 過程代碼)
10.打開 Access 報表
你可以從 Excel 裡打開 Access 報表。下述過程示範了如何直接從
Excel 裡顯示已經存在的 Access 報表。
‘ declare at the top of the module
Dim objAccess As Access.Application

Sub DisplayAccessReport()
Dim strDb As String

Dim strRpt As String

strDb = "C:\Program Files\Microsoft Office\" _

& "Office\Samples\Northwind.mdb"

strRpt = "Products by Category"

Set objAccess = New Access.Application

With objAccess
.OpenCurrentDatabase (strDb)

.DoCmd.OpenReport strRpt, acViewPreview

.DoCmd.Maximize
.Visible = True
End With
End Sub 下面的過程更通用,因為它允許你在任意 Access 資料庫裡顯示任意 Access 報表。注意,
該過程需要兩個字串參數:Access 資料庫名稱和報表名稱。
Sub DisplayAccessReport2(strDb As String, strRpt As String)

Set objAccess = New Access.Application

With objAccess
.OpenCurrentDatabase (strDb)

.DoCmd.OpenReport strRpt, acViewPreview

.DoCmd.Maximize
.Visible = True
End With
End Sub
你可以從立即視窗或者從如下所示的一個子過程裡運行 DisplayAccessReport2:
□ 從立即窗口
運行
300
DisplayAcce
ssReport2 過
程在立即窗
口裡在一行
輸入下述語
句:
Call DisplayAccessReport2("C:\Program Files\Microsoft

Office\Office\Samples\Northwind.mdb", "Sales Totals by

Amount")

從一個子過程運行 DisplayAccessReport2 過程:
' Enter the following procedure in the Code window

Sub ShowReport()
Dim strDb As String

Dim strRpt As String

strDb = InputBox("Enter the name of the database (full path): ")

strRpt = InputBox("Enter the name of the report:")

Call DisplayAccessReport2(strDb, strRpt)

End Sub
圖 15-10 Access 報表可以在 Excel VBA 過程裡打開
11.運行 Access 查詢
接下來的兩個程式例子將示範如何從 Excel VBA 過程裡運行 Access 查詢。在 Access 使用者介面最
常用的查詢類型是選擇查詢和參數查詢。兩個示例程式都使用 Range 物件

CopyFromRecordset 方法將查詢到的資料放置到 Excel 工作表。和資料庫的連結是通過 ADO 建立
的。
ADOX 物件程式庫(參見本章前面的圖 15-4)讓你訪問資料庫結構,安全和儲存在資料庫裡面的過程。
該庫中最上面的物件是 Catalog 物件,代表整個資料庫。該物件包含一些資料庫成員,例如,
301
表,欄位,索引,視圖和儲存的過程。使用 Catalog 物件的 Create 方法,你可以創建一個新的
資料庫,例如:
Dim cat As ADOX.Catalog

Set cat = New ADOX.Catalog


cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;" & _

"Data Source=C:\ExcelDump2.mdb;" 上面的例子示範如何使用 ActiveX 資料物件創建新


的資料庫。回想一下,在本章前面你使用 DAO 創建了一個叫做 NewDB_DA 的新資料庫。示例過程
RunAccessQuery,首先創建一個可以指向該 Catalog 物件的物件變數 cat。接著,Catalog 物件
的屬性 ActiveConnection 定義對資料庫創建連結的方法:
Set cat = New ADOX.Catalog
cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

"Data Source=" & dbPath

ADODB 物件程式庫裡的 Command 物件(參見本章前面的圖 15-3)明確你為了從資料來源獲取資料而想


要執行的命令。我們的過程嘗試訪問資料庫某特定查詢。
Set cmd = cat.Views(strQryName).Command Views 集合,ADOX 物件程式庫的一部分,包含某特定
目錄的所有 View 物件。視圖是篩選後的一組記錄,或者由其它表或者視圖創建的虛擬表。獲
得對資料庫裡需要的查詢的訪問後,你就可以按下述方式運行查詢:
Set rst = cmd.Execute Command 物件的 Execute 方法允許你啟動某個特定的查詢,SQL 語句,
或者儲存的過程。然後,返回的一組記錄會通過 Set 關鍵字被賦予物件變數 Recordset。創建
該組記錄後,這些記錄就會通過使用方法 CopyFromRecordset 放置到 Excel 工作表中。
12.運行選擇查詢
Sub RunAccessQuery(strQryName As String)

' prior to running this procedure you must set up

' references to the required object libraries

Dim cat As ADOX.Catalog


Dim cmd As ADODB.Command
Dim rst As ADODB.Recordset
Dim i As Integer
Dim dbPath As String
dbPath = "C:\Program Files\Microsoft Office\" _

& "Office\Samples\Northwind.mdb"
Set cat = New ADOX.Catalog
cat.ActiveConnection = _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _

"Data Source=" & dbPath


Set cmd = cat.Views(strQryName).Command

Set rst = cmd.Execute


Sheets(2).Select
For i = 0 To rst.Fields.Count - 1
Cells(1, i + 1).Value = rst.Fields(i).Name

Next
With ActiveSheet
.Range("A2").CopyFromRecordset rst
.Range(Cells(1, 1), _

Cells(1, rst.Fields.Count)).Font.Bold = True

.Range("A1").Select
End With
Selection.CurrentRegion.Columns.AutoFit

rst.Close
Set cmd = Nothing

Set cat = Nothing


End Sub
想要運行上述過程的話,可以在立即視窗裡面輸入下述語句並回車:
RunAccessQuery("Current Product List")

302
圖 15-11 從 Excel VBA 過程運行 Access 查詢的結果被放在了一個工作表裡了
(譯者,從立即視窗運行可能遇到編譯錯誤,如果這樣的話,可以從一個子程式裡調用該過程。)
13.運行參數查詢
你可以運行 Access 參數查詢並將其結果放置於 Excel 試算表裡面。例
如,過程
RunAccessParamQuery 通過 Access 資料庫的參數查詢運行 Employee Sales by Country,並且
取得 7/1/96 和 7/30/96 區間內的記錄。Employee Sales by Country 查詢要求兩個參數:開始
和結束時間。
應該使用 Command 物件的 Paramenters 集合定義這些參數:
cmd.Parameters("[Beginning Date]") = StartDate

cmd.Parameters("[Ending Date]") = EndDate

設置參數後,該查詢就可以使用下述語句執行了:
Set rst = cmd.Execute 該查詢返回的記錄會被賦予物件變數 Recordset 並使用
CopyFromRecordset 方法複製到工作表(參見本章後面更多相關使用資訊)。
Sub RunAccessParamQuery()
' prior to running this procedure you must set up

' references to the required object libraries

Dim cat As ADOX.Catalog


Dim cmd As ADODB.Command
Dim rst As ADODB.Recordset
Dim i As Integer
Dim dbPath As String
Dim StartDate As String
Dim EndDate As String
dbPath = "C:\Program Files\Microsoft Office\" _

& "Office\Samples\Northwind.mdb"

StartDate = "7/1/96"

EndDate = "7/31/96"

Set cat = New ADOX.Catalog


cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

"Data Source=" & dbPath


Set cmd = cat.Procedures("Employee Sales by Country").Command

cmd.Parameters("[Beginning Date]") = StartDate

cmd.Parameters("[Ending Date]") = EndDate

Set rst = cmd.Execute


Sheets(1).Select
For i = 0 To rst.Fields.Count - 1

Cells(1, i + 1).Value = rst.Fields(i).Name

Next
With ActiveSheet
.Range("A2").CopyFromRecordset rst
303
.Range(Cells(1, 1), Cells(1, rst.Fields.Count)).Font. _

Bold = True
.Range("A1").Select
End With
Selection.CurrentRegion.Columns.AutoFit

rst.Close
Set cmd = Nothing
Set cat = Nothing
End Sub
(譯者:本人運行上述過程不成功,將 Parameters 參數改為 cmd.Parameters(0) = StartDate,
cmd.Parameters(1) = EndDate 才運行成功。另外過程給定的日期範圍查詢結果為空,可以更
改查詢日期範圍。譯者使用示例檔 Nwind.mdb。自此以下,因刪除非法軟體,譯者使用 Excel
2003 英文版,Access 2002 英文版)
14.調用 Access 函數
你可以通過自動控制從 Excel 裡直接運行 Access 內置函數。下面的程序呼叫 EuroConvert 函數將
1000 西班牙比賽塔轉變為歐元。EuroConvery 函數使用歐盟確定的固定匯率。
Sub RunAccessFunction()
Dim objAccess As Object
On Error Resume Next
Set objAccess = GetObject(, "Access.Application")

'if no instance of Access is open, create a new one

If objAccess Is Nothing Then


Set objAccess = CreateObject("Access.Application")

End If
MsgBox "You will get " & _
objAccess.EuroConvert(1000, "ESP", "EUR") & _

" euro dollars. "


Set objAccess = Nothing
End Sub
15.獲取 Access 資料到 Excel 工作表
有很多種方法獲取外部資料到 Excel。本節給你示範下述不同的技巧將 Access 資料導入 Excel 工
作表:
• 使用 GetRows 方法
• 使用 CopyFromRecordset 方法
• 使用 TransferSpreadsheet 方法
• 使用 OpenDatabase 方法
• 創建一個文字檔
• 創建一個查詢表
16.使用 GetRows 方法獲取資料
你可以使用 GetRows 方法,將 Access 資料放置於 Excel 工作表。該方法返回一個二維的陣列,第一個
下標是一個代表欄位的數字, 而第二個下標則是代表記錄的數位。記錄和欄位從 0 開始。你通
過在 VBA 過程裡使用 DAO 返回資料到 Excel 工作表。下述示例過程示範了如何運行 Northwind 資料
庫裡的 Invoices 查詢,並記錄返回到 Excel 工作表。為了確保該過程工作正確,你必須首先建
立對 Microsoft Access 3.6 Object Library 的引用。參考本章前面的創建對物件程式庫的引用。
打開 Access 資料庫後,GetData_withDAO2 過程示範使用下述語句運行 Invoices 查詢:
Set qdf = db.QueryDefs("Invoices")
Microsoft Access 3.6 物件程式庫裡的 QueryDefs 物件代表一選擇或者行動查詢。選擇查詢從一
個或者多個表或者查詢裡返回資料, 然而,行動查詢允許你修改資料(使用行動查詢你可
以添加,修改或者刪除記錄)執行查詢後,過程將查詢返回的記錄通過 OpenRecordset 方法
放置到物件變數 Recordset 上,如下所示:
Set rst = qdf.OpenRecordset 接下來,通過 RecordCount 方法獲取記錄數目並且放置於變
量 countR 上。注意,為了獲得正確的記錄數目,記錄指標必須通過使用 MoveLast 方法移動
到 Recordset 裡的最後一條記錄。
rst.MoveLast
countR = rst.RecordCount 接著,過程提示用戶輸入要返回到工作表的記錄數目。你可以點擊
輸入對話方塊上的取消按鈕就此取消,或者輸入記錄數目獲取資料。如果你輸入的數字大於該
304
記錄數目,過程將獲取全部記錄。在獲取記錄之前,你必須使用方法 MoveFirst 將記錄指標移
動到第一條記錄。如果你忘了做這個,那麼記錄指標會停留在最後一條記錄上,並且將只能
獲取一條記錄。然後,該過程繼續執行, 啟動 Get Records 工作表和清除當前範圍內容。首先,
通過使用 Recordset 物件的 GetRows 方法,記錄將返回到一個二維陣列的 Variant 類型變數。接
著,過程在陣列的兩維中迴圈將記錄放置到工作表中,從儲存格 A2 開始。這一切完成後,另一
個迴圈將在工作表第一行裡放置欄位名稱,並且將每列設置為自動適應列寬,以正確現實數
據。
Sub GetData_withDAO2()

Dim db As DAO.Database
Dim qdf As DAO.QueryDef

Dim rst As DAO.Recordset

Dim recArray As Variant

Dim i As Integer
Dim j As Integer
Dim strPath As String
Dim a As Variant
Dim countR As Long
Dim strShtName As String
strPath = "C:\Program Files\Microsoft Office\" _

& "Office\Samples\northwind.mdb"

strShtName = "Returned records"

Set db = OpenDatabase(strPath)

Set qdf = db.QueryDefs("Invoices")

Set rst = qdf.OpenRecordset

rst.MoveLast
countR = rst.RecordCount

a = InputBox("This recordset contains " & _

countR & " records." & vbCrLf _

& "Enter number of records to return: ", _

"Get Number of Records")


If a = "" Or a = 0 Then Exit Sub
If a > countR Then
a = countR
MsgBox "The number you entered is too large." & vbCrLf _

& "All records will be returned."


End If
Workbooks.Add
ActiveWorkbook.Worksheets(1).Name = strShtName

rst.MoveFirst
With Worksheets(strShtName).Range("A1")

.CurrentRegion.Clear
recArray = rst.GetRows(a)

For i = 0 To UBound(recArray, 2)

For j = 0 To UBound(recArray, 1)

.Offset(i + 1, j) = recArray(j, i)

Next j
Next i
For j = 0 To rst.Fields.Count - 1

.Offset(0, j) = rst.Fields(j).Name

.Offset(0, j).EntireColumn.AutoFit

Next j
End With
db.Close
End Sub
17.使用 CopyFromRecordset 方法獲取資料
想要將整個 Recordset 導入工作表的話,你可以使用 Range 物件的 CopyFromRecordset 方法。
該方法可以使用三個參數:Data, MaxRows 和 MaxColumns。只有第一個參數 Data 是必須的。
該參數可以是 Recordset 物件。可選參數 MaxRows 和 MaxColumns 允許你明確應該返回的記錄數
目(MaxRows)和欄位數目(MaxColumns)。如果你忽略 MaxRows 參數,那麼所有返回的記錄將
會複製到工作表;如果你忽略 MaxColumns 參數,那麼所有的欄位將會被獲取。下麵示範的過
程 GetProducts 使用 ADO 物件建
305
立對 Northwind 資料庫的連結。為了讓該過程工作正常,你必須先建立對 Microsoft ActiveX
Data Objects 2.6 Library 的引用(參見本章前面有關創建對物件程式庫引用的指導)。
Sub GetProducts()
Dim conn As New ADODB.Connection

Dim rst As ADODB.Recordset

Dim strPath As String


strPath = "C:\Program Files\Microsoft Office\" _

& "Office\Samples\Northwind.mdb"

conn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" _

& "Data Source=" & strPath & ";"

conn.CursorLocation = adUseClient

' Create a Recordset from all the records

' in the Products table


Set rst = conn.Execute(CommandText:="Products", _

Options:=adCmdTable)
' begin with the first record rst.MoveFirst
' transfer the data to Excel
' get the names of fields first

With Worksheets("Sheet3").Range("A1")

.CurrentRegion.Clear
For j = 0 To rst.Fields.Count - 1

.Offset(0, j) = rst.Fields(j).Name

Next j
.Offset(1, 0).CopyFromRecordset rst

.CurrentRegion.Columns.AutoFit

End With
rst.Close
conn.Close
End Sub
上述過程從 Northwind 資料庫的 Products 表中複製所有的記錄到 Excel 工作表。如果你想要複
制某一些記錄的話,那麼你可以使用 MaxRows 參數,如下所示:
.Offset(1, 0).CopyFromRecordset rst, 5 該語句告訴 VB 僅複製 5 條記錄。
該 Offset 方法導致輸入到試算表裡的記錄從試算表當前行的第二行開
始。想要僅將兩個表字段的所有記錄發送到工作表的話,可以使用下述語
句:
.Offset(1, 0).CopyFromRecordset rst, , 2

該語句告訴 VB 從開始兩列複製所有資料。在 rst 和數字 2 之間的逗號是個預留位置,給被忽略的


MaxRows 參數。
18.使用 TransferSpreadsheet 方法獲取資料
可能使用 TransferSpreadsheet 方法在當前 Access 資料庫(.mdb)或者 Access 項目(.adp)和
試算表之間導入或者匯出資料。你也可以將 Excel 試算表裡的資料連結到當前 Access 資料
庫。對於連結的試算表,當 Access 仍然允許從 Excel 程式裡完全訪問時,你可以使用 Access
來查看和編輯試算表資料。在 VB 裡執行 TransferSpreadsheet 操作的 TransferSpreadsheet
方法語法如下:
DoCmd.TransferSpreadsheet [transfertype][, spreadsheettype], _

tablename, filename [, hasfieldnames][, range] 參數 transfertype 可以是以下常


數之一:acImport(缺省設置),acExport 或者 acLink。這些常數定義資料是否是導入,匯出
或者連結到資料庫。
參數 spreadsheettype 可能是下述常數之一:
0 acSpreadsheetTypeExcel3 (default setting)
6 acSpreadsheetTypeExcel4
5 acSpreadsheetTypeExcel5
5 acSpreadsheetTypeExcel7
8 acSpreadsheetTypeExcel8
8 acSpreadsheetTypeExcel9
2 acSpreadsheetTypeLotusWK1
3 acSpreadsheetTypeLotusWK3
7 acSpreadsheetTypeLotusWK4
不難猜到,spreadsheettype 參數明確試算表名稱和版本號。tablename 參數是個字串表
306
達式,明確你想要往裡面導入試算表資料,或者從裡面匯出試算表資料,或者將試算表
資料連結到的 Access 表的名稱。除了表名稱之外,你也需要明確你想要匯出資料到試算表
的選擇查詢名稱。hasfieldnames 參數是個邏輯值 True(-1)或者 False(0)。True 表明工作表第
一行包含欄位名稱;False 則表示第一行包含普通資料。缺省設置為 False(第一行裡沒有字
段名稱)。參數 range 是個字串運算式,明確工作表中的儲存格區域或者區域名稱。該參數
僅用於導入。如果你忽略 range 參數的話,那麼整個試算表將會被導入。如果你想要匯出的
話,就將該參數空在那裡,除非你需要明確該工作表名稱。下麵示範的 ExportData 示例程式
使用 TransferSpreadsheet 方法從 Northwind 資料庫裡的 Shippers 表中匯出資料到
Shippers.xls 試算表中。注意,該過程使用了自動控制來建立對 Access 的連結。建立連結後,
使用 OpenCurrentDatabase 方法打開 Northwind 資料庫。運行完 ExportData 過程後,請打開
C:\Shippers.xls 檔查看獲取的資料。
‘ declare at the top of the module
Dim objAccess As Access.Application

Sub ExportData()
Set objAccess = CreateObject("Access.Application")

objAccess.OpenCurrentDatabase filepath:= _

"C:\Program Files\Microsoft Office\Office\" _

& "Samples\Northwind.mdb"

objAccess.DoCmd.TransferSpreadsheet _

TransferType:=acExport, _

SpreadsheetType:=acSpreadsheetTypeExcel9, _

TableName:="Shippers", _

Filename:="C:\Shippers.xls", _

HasFieldNames:=True, _

Range:="Sheet1"
objAccess.Quit
Set objAccess = Nothing

End Sub
(譯者:原文為 acSpreadsheetTypeExcel10 運行失敗)
圖 15-12 使用 TransferSpreadsheet 方法可以將 Access 表裡的資料匯出到 Excel 試算表裡
19.使用 OpenDatabase 方法
Excel 2002 提供了一個操縱資料庫的新方法,OpenDatabase 方法,應用于 Workbooks 集合,是將
資料庫資料導入 Excel 電子錶
格最容易的方法。該方法要求你明確你想要打開的資料庫檔案名稱。下面的示例過程打開位於
C:\Program Files\Microsoft

Office\Office10\Samples 資料夾裡的 Northwind 資料庫。當你運行該過程,Excel 顯示一個對


話框,列出了該資料庫裡的所有表和查詢(參見圖 15-13)。從列表裡選擇後,就會打開一
個全新的工作簿,顯示被選上的表或者查詢裡的資料。
Sub OpenAccessDatabase()
Workbooks.OpenDatabase _

Filename:="C:\Program Files\Microsoft Office\" _

& "Office10\Samples\Northwind.mdb"
End Sub
307
圖 15-13 使用帶一個參數(資料庫檔案名稱)的 OpenDatabase 方法允許從一個清單方塊裡選擇一
個表或者查詢
圖 15-14 使用 Excel 2002 裡新增的 OpenDatabase 方法可以輕易地將儲存在表或者查詢裡的資料
庫資料導入 Excel 工作簿
OpenDatabase 方法有四個可選參數,可供你進一步限定你要獲取的資料:
OpenDatabase 方法的可選參數資料類型描述
CommandText Variant SQL 查詢字串。參見使用該參數的
示例
CommandType Variant 查詢的命令類型。可
用的命令類型有:Deault,SQL 和表 BackgroundQuery
Variant 查詢的背景。可以是以下常數之
一:PivotCache 或者 QueryTable ImportDataAs
Variant
xlQueryTable 報告創建一個查詢表,或者
明確查詢的格式。使用
xlPivotTableReport 來創建一個樞紐分析表
接下來的示例過程示範了如何使用帶可選參數的 OpenDatabase 方法。該過程從獲取的客戶記錄
創建了一個樞紐分析表。當你運行該過程時,Excel 就會基於提供的查詢的字串顯示一個可
用欄位的列表。你可以拖曳一個或者多個欄位到該透視表中,以創建資料透視報告。圖 15-15
顯示了按國家分類的 CustomerId 欄位。
Sub CountCustomersByCountry()

Workbooks.OpenDatabase _

Filename:="C:\Program Files\Microsoft Office\" _

& "Office10\Samples\Northwind.mdb", _
CommandText:="Select * from Customers", _

BackgroundQuery:=PivotTable, _

ImportDataAs:=xlPivotTableReport

End Sub
308
(譯者:Excel 2003+Access 2002 運行該過程有問題:BackgroundQuery: = PivotTable。此
處有矛盾,上面的參數解釋說該參數為 PivotCache 或者 QueryTable,而這裡卻是 PivotTable。
這三個參數均導致錯誤。搜索對象流覽器說該參數為布林類型。譯者將該參數改為-1,1,2,
10,True,False 等運行,結果沒有區別。)
圖 15-15 使用 OpenDatabase 方法的可選參數,你可以明確獲取資料庫資料到一個特定的格式,
例如資料透視報告或者查詢表報告
20.從 Access 資料創建文字檔
你可以使用 Excel 的 VBA 過程從 Access 資料創建一個以逗號或者 tab 分開的文字檔。文字檔
對於傳輸大量資料到試算表特別有用。下面的示例程式示範了如何從一個 ADO recordset 創
建一個 tab 分開的文字檔。為了確保該過程運行正確,你必須創建
對 Microsoft ActiveX Data Objects 2.6 Library 的引用。參考第八章中操作文字檔的詳細
信息。運行該過程後,請在 Excel
裡打開 C:\ProductsOver50.txt
Sub CreateTextFile()
Dim strPath As String
Dim conn As New ADODB.Connection

Dim rst As ADODB.Recordset

Dim strData As String

Dim strHeader As String


Dim strSQL As String

strPath = "C:\Program Files\Microsoft Office\" _

& "Office\Samples\Northwind.mdb"

conn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" _

& "Data Source=" & strPath & ";"

conn.CursorLocation = adUseClient

strSQL = "SELECT * FROM Products WHERE UnitPrice > 50"

Set rst = conn.Execute(CommandText:=strSQL, Options:=adCmdText)

'save the recordset as a tab-delimited file

strData = rst.GetString(StringFormat:=adClipString, _

ColumnDelimeter:=vbTab, _
RowDelimeter:=vbCr, _
nullExpr:=vbNullString)

Open "C:\ProductsOver50.txt" For Output As #1

For Each f In rst.Fields

strHeader = strHeader + f.Name & vbTab

Next
Print #1, strHeader

Print #1, strData


Close #1
End Sub
(譯者:如果為強制要求聲明物件則還需聲明變數 f。) 在第八章中,你學習了如何使用
FileSystemObject 操作文字檔。下面的過程演示了如何使用該物件來創建一個名為
309
ProductsOver100.txt 的文字檔:
Sub CreateTextFile2()

Dim strPath As String


Dim conn As New ADODB.Connection

Dim rst As ADODB.Recordset

Dim strData As String

Dim strHeader As String


Dim strSQL As String

Dim fso As Object


Dim myFile As Object

Set fso = CreateObject("Scripting.FileSystemObject")

Set myFile = fso.CreateTextFile("C:\ProductsOver100.txt", True)


strPath = "C:\Program Files\Microsoft Office\" _

& "Office\Samples\Northwind.mdb"

conn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" _

& "Data Source=" & strPath & ";"

conn.CursorLocation = adUseClient

strSQL = "SELECT * FROM Products WHERE UnitPrice > 100"

Set rst = conn.Execute(CommandText:=strSQL, Options:=adCmdText)

'save the recordset as a tab-delimited file

strData = rst.GetString(StringFormat:=adClipString, _

ColumnDelimeter:=vbTab, _
RowDelimeter:=vbCr, _
nullExpr:=vbNullString)

For Each f In rst.Fields

strHeader = strHeader + f.Name & vbTab

Next
With myFile
.WriteLine strHeader
.WriteLine strData
.Close
End With
End Sub
(譯者:如果為強制要求聲明物件則還需聲明變數 f。)
圖 15-16 因為文字檔可以輕易地在 Excel 打開,所以你可以使用它在 Access 和 Excel 之間傳輸
數據
21.從 Access 資料創建查詢表
如果你想要在 Excel 使用來自外部的資料來源,而且你知道你將使用的資料會經常改變,那麼你需
要創建一個查詢表。查詢表是 Excel 工作表裡的特殊表,它連結到外部資料來源,例如 Access 數
據庫,SQL 伺服器,網頁或者文字檔。用戶可以輕易地刷新查詢表來獲取最更新的資訊。
Excel 提供了專門的功能表選項來獲取外部資料:只要選擇“資料”|“導入外部資料”,並選
擇“新資料庫查詢”。通過查詢外部資料庫,你可以帶來一些正好適合你要求得資料。例如,
不必將所有的產品資訊都帶入你的試算表來回顧,你只要在獲取資料之前明確資料必須達
到的條件就行。因此,你可以只獲取單價大於 20 美金的產品,而不是從 Access 導入所有的產
品。在 VBA 裡,你可以使用 QueryTable 物件訪問外部資料。每個 QueryTable 代表從外部資料
源例如 SQL 伺服器或者 Access 資料庫創建的工作表表格。要程式設計創建一個查詢的話,你可以
使用 QueryTabes 集合物件的 Add 方法。該方法要求三個參數。本章結尾處的示例過程使用下
述語句在活動工作表上創建一個查詢表:
Set myQryTable = ActiveSheet.QueryTables.Add(strConn, Dest, strSQL) strConn 是為第
一個參數——Connection 提供數值的變數。它是必須的參數,為 Variant 資料類型,明確查詢
表資料來源。Dest 是為第二個參數——Destination 提供數值的變數。這是個必須的參數,為
Range 資料類型,明確在哪個儲存格放置查詢表。strSQL 是為第三個參數——SQL 提供數值的
變數。這是個必須的參數,為字串資料型別,定義要從查詢返回的資料。當你使用 Add 方法
創建查詢時,該查詢不會運行,直到你調用 Refresh 方法。該方法接受一個參數——
BackgroundQuery。這是一個 Variant 資料類型的可選參數,允許你決定是否在建立了對資料
庫的連結以及查詢被提交(True)後將控制返回給過程,或者在查詢已經運行並且所有資料
已經獲取到工作表裡了(False)才將控制返回給過程。接下來的過程 CreateQueryTable 僅僅
從 Northwind 資料庫獲取產品單價大於 20 的 Products 表中的產品。注意,該過程僅在所有相關
記錄都被獲取之後,控制才交回給過程。方法 RefreshStyle 決定資料如何插入工作表。下述常
310
數可供使用:
• xlOverwriteCells – 現存的儲存格會被新資料覆蓋
• xlInsertDeleteCells – 插入或者刪除儲存格以容納新資料
• xlInsertEntireRows – 插入整行以容納新資料
Sub CreateQueryTable()
Dim myQryTable As Object

Dim myDb As String

Dim strConn As String


Dim Dest As Range
Dim strSQL As String

myDb = "C:\Program Files\Microsoft Office\Office\" _

& "Samples\Northwind.mdb"

strConn = "OLEDB;Provider=Microsoft.Jet.OLEDB.4.0;" _

& "Data Source=" & myDb & ";"

Set Dest = Worksheets(1).Range("A1")

strSQL = "SELECT * FROM Products WHERE UnitPrice>20"

Set myQryTable = ActiveSheet.QueryTables.Add(strConn, _

Dest, strSQL)
With myQryTable
.RefreshStyle = xlInsertEntireRows

.Refresh False
End With
End Sub
圖 15-17 使用 QueryTable 物件可以在 Excel 裡分析來自外部資料來源例如 Access 資料庫的資料
22.在 Excel 裡使用 Access 資料
使用上面討論過的方法之一從 Access 資料庫獲取資料之後,你可以使用許多 Excel 內置的工具
來分析該資料。基於獲取的資訊來創建一些圖表經常是很有用的。
23.用 Access 資料創建內嵌圖表
使用 VBA,你可以輕鬆的基於從 Access 資料庫獲取的資料創建圖表。下面顯示的 ChartData 過程
使用從 Access Northwind 資料裡獲取的資料創建了一個內嵌圖表。該圖表由 Charts 集合的 Add
方法創建。圖表的資料來源由 Range 物件提供。CurrentRegion 方法返回儲存格 A1 周圍的所有非
空儲存格。過程的剩餘部分則通過設置圖表的各種屬性來設置圖表格式。圖表代碼部分錄製在
一個分開的宏裡,然後移到該 VBA 過程裡並作一些修改以設置一些圖表屬性。
Sub ChartData()
Dim db As DAO.database
Dim qd As DAO.QueryDef

Dim rs As DAO.Recordset
Dim mySheet As Worksheet
311
Dim recArray As Variant

Dim i As Integer
Dim j As Integer
Dim pathDb As String

Dim qdName As String


pathDb = "C:\Program Files\Microsoft Office\" _

& "Office\Samples\northwind.mdb"

qdName = "Category Sales for 1997"

Set db = OpenDatabase(pathDb)

Set qd = db.QueryDefs(qdName)

Set rs = qd.OpenRecordset

Set mySheet = Worksheets("Sheet2")

With mySheet.Range("A1")

.CurrentRegion.Clear
recArray = rs.GetRows(rs.RecordCount)

For i = 0 To UBound(recArray, 2)

For j = 0 To UBound(recArray, 1)

.Offset(i + 1, j) = recArray(j, i)

Next j
Next i
For j = 0 To rs.Fields.Count - 1

.Offset(0, j) = rs.Fields(j).Name

.Offset(0, j).EntireColumn.AutoFit

Next j
End With
mySheet.Activate
Charts.Add
ActiveChart.ChartType = xl3DColumnClustered

ActiveChart.SetSourceData _

Source:=mySheet.Cells(1, 1).CurrentRegion, PlotBy:=xlRows

ActiveChart.Location Where:=xlLocationAsObject, Name:=mySheet.Name

With ActiveChart
.HasTitle = True
.ChartTitle.Characters.Text = qdName

.Axes(xlCategory).HasTitle = True

.Axes(xlCategory).AxisTitle.Characters.Text = ""

.Axes(xlSeries).HasTitle = False

.Axes(xlValue).HasTitle = True

.Axes(xlValue).AxisTitle.Characters.Text = mySheet.Range("B1") _

& "($)"
.Axes(xlValue).AxisTitle.Orientation = xlUpward

End With
db.Close
End Sub
過程 ChartData 的運行結果顯示在圖 15-18 上。
312
圖 15-18 你可以使用 VBA 程式設計基於從 Access 表,查詢或者 SQL 語句獲取的資料創建內嵌圖表
24.傳輸 Excel 試算表到 Access 資料庫
世界上許多大的資料庫都是從試算表開始的。當你需要從你的試算表創建一個資料庫應
用軟體時,你可以求助於緩慢呆滯的手動方法來傳輸資料,你也可以使用你新學的 VBA 程式設計技巧
自動將你的試算表變為資料庫表。一旦在資料庫格式了,你的 Excel 資料就可以使用在高級的
公司範圍的報告上使用,或者作為一個獨立的應用軟體使用(不必多言,後面的要求就是你要擁
有資料庫應用軟體的設計技巧)。本章剩餘的部分將示範如何將 Excel 試算表連結和導入到
Access 資料庫。在你移到 Excel 資料到 Access 之前,你應該盡可能地整理好資料,這樣,傳輸
操作就會順利。請牢記,試算表裡你要傳輸的每一行都會變為表中的一個記錄,而每一列
都會作為表的欄位。因為這個原因,你計畫傳輸到 Access 的試算表的第一行應該包含欄位名
稱。你要傳輸的資料列中間應該沒有空白。換句話說,你的資料應該是連續的。如果你要傳輸
的資料有大量的列,那麼應該先列印出你的資料並檢查確保以後沒有意外。如果你的資料的第
一列為欄位名稱,那麼建議你使用內置的 Transpose 函數先將資料轉置,以確保資料是從上到
下,而不是從左到右。讓你資料順利導入的關鍵是使你的試算表盡可能象資料庫表。
25.將 Excel 試算表連結到 Access 資料庫
你可以使用 TransferSpreadsheet 方法將 Excel 試算表連結到 Access 資料庫(參見本章“使
用 TransferSpreadsheet 方法獲取資料”部分有關該方法的使用詳情)。下面的示例過程將圖
15-19 顯示的試算表連結到 Northwind 資料庫。在使用 OpenCurrentDatabase 方法打開 Access
資料庫之後,該過程從 Chap15.xls 試算表文件的 mySheet 工作表中位於儲存格(A1:D7) 區域
裡,使用 Access DoCmd 物件的 TransferSpreadsheet 方法創建了一個名為 ExcelSheet 的連結表。
注意,DoCmd 語句中的參數-1 表明試算表的第一行包含列標題。接著,該過程打開該連結的
表為編輯模式,因此使用者可以添加或者修改資料。如果在添加一條或者多條記錄後,你要更
改回到 Excel,那麼你將注意到在連結的 Access 表裡作的改變將在 Excel 裡立即可用。
圖 15-19 VBA 過程 LinkExcel_ToAccess 將試算表連結到 Access 的 Northwind 資料庫
Sub LinkExcel_ToAccess()

Dim objAccess As Access.Application

Dim strName As String

strName = "Linked_ExcelSheet"

Set objAccess = New Access.Application

With objAccess
.OpenCurrentDatabase "C:\Program Files\Microsoft Office\" _

& "Office\Samples\Northwind.mdb"

.DoCmd.TransferSpreadsheet acLink, acSpreadsheetTypeExcel9, _

strName, _
"C:\Chap15.xls", _

-1, "mySheet!A1:D7"

.DoCmd.OpenTable strName, acViewNormal, acEdit

End With
End Sub
313
圖 15-20 Excel 試算表可以被連結到 Access 資料庫
26.將 Excel 試算表導入 Access 資料庫
在前面的部分,你學習了如何將 Excel 試算表連結到 Access 資料庫。導入你的試算表也
是一樣簡單。你甚至可以使用用於連結的相同的 VBA 過程,作一些細微的改變:簡單地將常
數 acLink 改為 acImport,就完工了。下述過程將圖 15-19 裡顯示的試算表(見前面部分)
導入到 Northwind 資料庫。
Sub ImportExcel_ToAccess()

Dim objAccess As Access.Application

Dim strName As String

strName = "Imported_ExcelSheet"

Set objAccess = New Access.Application

With objAccess
.OpenCurrentDatabase "C:\Program Files\Microsoft Office\" _

& "Office\Samples\Northwind.mdb"

.DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel9, _

strName, _
"C:\Chap15.xls", _

-1, "mySheet!A1:D7"

.DoCmd.OpenTable strName, acViewNormal, acEdit

End With
End Sub
27.放置 Excel 數據到 Access 表中
除了連結或者內嵌你的 Excel 試算表,你萬一想要從頭開始創建一個 Access 表並且裝載電子
表格裡面的資料呢?使用一些你已經在本書裡獲得的程式設計技巧,你可以輕鬆完成該任務。我們
來看看一個 VBA 過程,基於圖 15-19 顯示的 Excel 試算表,動態地創建一個 Access 表。注意,
該過程使用 ADO 和 MicrosoftJet.OLEDB.4.0 提供者連結到 Access 資料庫。創建連結之後,該過
程使用 ADOX 物件程式庫裡的 Catalog 和 Table 物件創建一個新 Access 表。接著,對應試算表冽名稱
的欄位被添加到該表中。注意,每個文本欄位明確了它可以接受的最大字元數。如果試算表
儲存格的長度大於該欄位大小,那麼錯誤處理常式將顯示 Access 內置的資訊,提示該錯誤並
終止過程。該過程的最後一個任務是資料傳輸操作。要執行該任務,該過程打開了 Access 表的
Recordset 對象。因為你需要添加記錄到該表, 所以該過程使用了一個 adOpenKeyset 指標類型。
現在該表已打開,該過程使用 For…Next 迴圈在 Excel 資料行中迴圈,將在每個儲存格中找到
的資訊放置到相應的表字段中。注意,使用 Recordset 物件的 AddNew 方法往 Access 表裡添加新
的記錄。從每行儲存格複製完資料之後,該過程使用 Recordset 物件的 Update 方法來保存該表
記錄。
314
Sub AccessTbl_From_ExcelData()
Dim conn As ADODB.Connection
Dim cat As ADOX.Catalog
Dim myTbl As ADOX.Table
Dim rstAccess As ADODB.Recordset
Dim rowCount As Integer
Dim i As Integer
On Error GoTo ErrorHandler
' connect to Access using ADO

Set conn = New ADODB.Connection

conn.Open "Provider = Microsoft.Jet.OLEDB.4.0;" & _

"Data Source = C:\Program Files\Microsoft

Office\Office\Samples\Northwind.mdb;"

' create an empty Access table

Set cat = New Catalog


cat.ActiveConnection = conn
Set myTbl = New ADOX.Table
myTbl.Name = "TableFromExcel"

cat.Tables.Append myTbl
' add fields (columns) to the table

With myTbl.Columns

.Append "School No", adVarWChar, 7

.Append "Equipment Type", adVarWChar, 15

.Append "Serial Number", adVarWChar, 15

.Append "Manufacturer", adVarWChar, 20

End With
Set cat = Nothing
MsgBox "The table structure was created."

' open a recordset based on the newly created

' Access table


Set rstAccess = New ADODB.Recordset

With rstAccess
.ActiveConnection = conn
.CursorType = adOpenKeyset

.LockType = adLockOptimistic
.Open myTbl.Name
End With
' now transfer data from Excel spreadsheet range

With Worksheets("mySheet")

rowCount = Range("A2:D7").Rows.Count

For i = 2 To rowCount + 1
With rstAccess .AddNew ' add a new record to an Access table

.Fields("School No") = Cells(i, 1).Text

.Fields("Equipment Type") = Cells(i, 2).Value

.Fields("Serial Number") = Cells(i, 3).Value

.Fields("Manufacturer") = Cells(i, 4).Value

.Update ' update the table record


End With
Next i
End With
' close the Recordset and Connection object and remove them

' from memory


rstAccess.Close
conn.Close
Set rstAccess = Nothing

Set conn = Nothing


AccessTbl_From_ExcelDataExit:

Exit Sub
ErrorHandler:
MsgBox Err.Number & ": " & Err.Description

Resume AccessTbl_From_ExcelDataExit

End Sub
315
28.接下來……
本章提供了多個例子將 Excel 資料放入 Access,以及從 Access 獲取資料到工作表。你學習了如
何從 Excel VBA 過程中控制 Access 應用軟體,執行一些任務,例如打開 Access 表單和報告,創
建新表單,運行選擇和參數查詢,以及調用 Access 內置函數。另外,本章示範了一些創建文本
檔,查詢表,和圖表的技術。你也學習了如何使用連結,導入和動態 Access 表,將 Excel 資料
放置到 Access 資料庫裡。在下章中,你將學習如何使用 Excel 創建,察看和分析網際網路資料。
316__

You might also like