Free Essay

Study Vba

In: Computers and Technology

Submitted By kkey
Words 27477
Pages 110
免费下载-分享--Excel VBA 应用教程—





一、VBA语言基础 ...................................................................................................................1 第一节 第二节 第三节 第四节 第五节 标识符...................................................................................................................................1 . 运算符...................................................................................................................................1 . 数据类型...............................................................................................................................1 . 变量与常量............................................................................................................................1 数组.......................................................................................................................................2 .

第六节 注释和赋值语句....................................................................................................................2 第七节 书写规范...............................................................................................................................2 . 第八节 判断语句...............................................................................................................................2 . 第九节 循环语句...............................................................................................................................3 . 第十节 其他类语句和错误语句处理................................................................................................4 第十一节 过程和函数........................................................................................................................4

一.Sub过程 ................................................................. 二.Function函数 ............................................................ 三.Property属性过程和Event事件过程 ......................................... 第十二节内部函数 .............................................................................................................................5 一.测试函数 ................................................................ 二.数学函数 ................................................................ 三.字符串函数 .............................................................. 四.转换函数 ................................................................ 五.时间函数 ................................................................ 第十三节 文件操作............................................................................................................................6 文件 ........................................................................ 删除 ........................................................................ 打开 ........................................................................ 读入 ........................................................................ 写入 ........................................................................ 关闭 ........................................................................ 其他文件函数 ................................................................
二、VISUAL BASIC程序设计网络教学 .....................................................................................1 第一课 VBA是什么 .................................................................................................................1

4 5 5 5 5 5 6 6 6 6 6 7 7 7 7

1.1 VBA是什么...................................................................................................................................1 . 1.2 EXCEL环境中基于应用程序自动化的优点................................................................................1 1.3 录制简单的宏..............................................................................................................................1
1.4 1.5 1.6 1.7 1.8 执行宏............................................................................................................................... ...........2 查看录制的代码..........................................................................................................................2 编辑录制的代码..........................................................................................................................3 录制宏的局限性..........................................................................................................................3 小结............................................................................................................................... ...............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 4.4.2 4.4.3 4.4.4 4.4.5

变量的数据类型 ........................................................ 9 用Dim语句创建变量(声明变量) .......................................... 10 变量命名的惯例 ....................................................... 10 使用数组 ............................................................. 10 变量赋值 ............................................................. 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 20 21 22 保存宏 ............................................................................................................................... ...........32 打印宏 ............................................................................................................................... ...........32 保存宏在个人宏工作簿 ..............................................................................................................32 打开含有宏的工作簿 ..................................................................................................................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 19 20 21 22

设置注释块/解除注释块 ............................................................................................................53 使用对象浏览器 ..........................................................................................................................53 使用VBA对象库 ............................................................................................................................58 用对象浏览器来定位过程 ..........................................................................................................59 使用立即窗口 ..............................................................................................................................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)关系运算符 = (相同)、(不等)、>(大于)、=(不小于)、 大于

102

< >=
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 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 0 Then
ActiveCell.Offset(0, 1).Value = "positive"

ElseIf ActiveCell.Value 0 ), 如 果 该 值 不 大 于 0 , VB 将 跳 到 下 个 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 =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 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 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

1 2 3 4 5 6 7

A Product Name Apples Pears Bananas Cherries Blueberries Strawberries

B Sales (in Pounds) 120 0 100 0 0 160

4. 运行过程DeleteZeroRows。 我们来一行接一行地检查一下过程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 3 4 5 (2,1) (3,1) (4,1) (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 Japanese Yen 128.2 (1,1) (1,2) (1,3) Mexico Mexican Peso 9.423 (2,1) (2,2) (2,3) Canada Canadian Dollar 1.567 (3,1) (3,2) (3,3) Norway Norwegian Krone 8.351 (4,1) (4,2) (4,3)

123

Hungary Hungarian Forint 266.7 (5,1) (5,2) (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) cities(2) cities(3) cities(4) cities(5) cities(6)

= = = = = =

"Baltimore" "Atlanta" "Boston" "Washington" "New York" "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) cities(2) cities(3) cities(4) cities(5) cities(6)

= = = = = =

"Baltimore" "Atlanta" "Boston" "Washington" "New York" "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 lpt1:")

End Sub 32.创建快捷方式 当你开始传播你的VBA应用程序的时候,用户可能会要求你自动在他们的桌面上放置一个你的软件
的快捷方式。VBA自己没有提供创建快捷方式的方法。很幸运的是,你现在知道如何使用 WSH了,你 可以使用它的对象Shell创建应用程序或者网页的快捷方式,不必要用户的干涉。对象 WshShell使

用了方法CreateShortcut,你可以按照下述方法:
Set myShortcut = WshShell.CreateShortcut(Pathname)

Pathname是明确快捷文件完整路径的字符串。所有的快捷方式文件都有扩展名 .lnk,并且该扩展名 必须包括在文件路径名里面。CreateShortcut方法返回快捷方式对象,下面的表格里列出了很多属

性和一个方法。 方法/语法 TargetPath WindowStyle 示例 TargetPath属性是可执行文件的路径
WshShell.TargetPath = ActiveWorkbook.FullName

HotKey

IconLocation

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 WorkingDirectory

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

WorkingDirectory属性明确快捷方式的工作目录 strWorkDir = WshShell.SpecialFolders("Desktop") WshShell.WorkingDirectory = strWorkDir

Save

这是对象Shortcut的唯一方法。在使用方法CreateShortcut创建

165

一个快捷方式对象并且设置该快捷方式的属性后,必须使用Save 方法将快捷方式对象保存到硬盘上。 创建快捷方式是个三步的过程: 1. 创建一个WshShortcut对象 2. 初始化它的属性 3. 用方法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 0 窗口被隐藏 vbNormalFocus 1 普通大小,并带焦点 vbMinimizedFocus ( 默 认 2 最小化,并带焦点(这是缺省设置) 设 3 最大化,并带焦点 置) 4 普通大小,并失去焦点 vbMaximizedFocus 6 最小化,并失去焦点 vbNormalNoFocus vbMinimizedNoFocus 如果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 激活控制面板图标的一些文件示例 控制面板图标 .cpl或者.dll文件 电话和调制解调器选项 TELEPHON.CPL或MODEM.CPL 添加/删除程序 APPWIZ.CPL 网络和拨号连接 NETCPL.CPL或NETSHELL.DLL 32-Bit ODBC ODBCCP32.CPL 系统 SYSDM.CPL 邮件 MLCFG32.CPL 用户和密码 PASSWORD.CPL或NETPLWIZ.DLL 日期/时间 TIMEDATE.CPL 区域选项 INTL.CPL Internet选项 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 xlMicrosoftAccess FoxPro xlMicrosoftFoxPro Mail xlMicrosoftMail PowerPoint xlMicrosoftPowerPoint Project xlMicrosoftProject Schedule xlMicrosoftSchedulePlus Word xlMicrosoftWord 2.在应用程序之间切换 因为用户可以同时在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语句里使用的按键代码 键 代码 键 代码 空格键 {BACKSPACE} 滚动锁定 {SCROLLLOCK} {BS} Tab {TAB} {BKSP} 向上箭头 {UP} Break键 {BREAK} F1 {F2} 大写锁定键 {CAPSLOCK} F2 删除键 {DELETE} F3 {F3} {DEL} F4 {F4} 向下箭头 {DOWN} F5 {F5} End键 {END} F6 {F6} 回车键 {ENTER} F7 {F7} ~ F8 {F8} Esc键 {ESC} F9 {F9} 帮助键 {HELP} F10 {F10} Home键 {HOME} F11 {F11} 插入键 {INSERT} F12 {F12}

170

{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 msoFileDialogFolderPicker

值 1 2 3 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 AddControl AfterUpdate BeforeDragOver BeforeDropOrPaste BeforeUpdate Change Click DblClick Deactivate DropButtonClick Enter Error Initialize Exit KeyDown KeyPress KeyUp Layout MouseDown MouseMove MouseUp QueryClose RemoveControl Resize Scoll SpinDown SpinUp
Terminate

N N N N

N N

N N

N N

N N

N N

N N

N N

N N N

N N N

N N

N N

N N

N N N

N N

N N

N N N N N N N N N N N N N N N N N

N N

N N

N

N N N N N N N N N

N N
N

N N

N N

N N

N N

N N

N N N N N N N N N

N

N

N

N

N

N

N

N

N

N

N

N

N

N

N
N N N

N

N

N

N

N

N

N

N

N

N N N N N N N N N

N N N N N N N

N N N N N N N

N N N N N N N

N N N N N N N

N N N N N N N

N N N N N N N

N

N N

N N N N N N N

N

N N N N N N

N N N N N N N N

N

N N N N N N N

N N N N N N N

N

N N

N

N

N N N

N N N

N N N

N N N

N

表10-2 窗体和控件事件 你创建的每个窗体都有一个窗体模块用来储存VBA事件过程。你可以通过以下几种方式来进入窗体 模块,编写事件过程或者找到某个控件可识别的事件: • 双击某个控件 • 在控件上单击右键,并选择快捷菜单上的查看代码

Us e rFo rm rFo

La b e l

T e x tB o x tBo

Co m b o B o x

Lis tBo x tBo

200

Ch e ck B o x

Op tio n Bu tto n tio tto

T o g g le Bu tto n tto

F ram e ram

Co m m a n d Bu tton tton

T a bS trip trip

Mu lt iPa g e ltiP iPa

S c ro llBa r llBa

S pin Bu tto n tton

Im a g e

R e fEd it fEd

• 点击工程浏览窗口的查看代码按钮 • 双击用户窗体上任何未用的区域 执行以上任意操作都会导致打开窗体的代码窗口。图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 .AddItem .AddItem .AddItem "B" "C" "D" "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. 给窗体和它的控件设置下述属性: 对象 属性 窗体 Name Caption 标签1 Caption 标签Last Name 下面的文字框 Name 标签2 Caption 标签First Name 下面的文字框 Name 标签3 Caption 标签Salary 下面的文字框 Name 框架1 Caption 框架Salary Modification下面的 Name 文字框 Name 选项按钮1 Caption 选项按钮2 Name Caption 框架2 Caption 选项按钮3 Name Caption 选项按钮4 Name Caption 列表框 Name Height Width 命令按钮1 Name Caption 命令按钮2 Name Caption 命令按钮3 Name Caption 命令按钮4 Name Caption

设置 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

222

命令按钮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. 6. 将光标放在过程ClassDemo里的任意位置,并且按下F5,或者选择“运行”|“运行子过程/用

户窗体”
当窗体出现时,在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 msoBarTypeNormal 菜单栏 1 msoBarTypeMenuBar 快捷菜单 2 msoBarTypePopup 1. 2. 3. 4. 5. 打开一新工作簿并保存为Chap12.xls 切换到VB编辑器屏幕 选择当前VBA工程Chap12.xls ,并重命名为CustomTools 添加一个新模块 输入过程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 0 命令栏位于应用程序窗口的左边 msoBarRight 2 命令栏位于应用程序窗口的右边 msoBarTop 1 命令栏位于应用程序窗口的上面 msoBarBottom 3 命令栏位于应用程序窗口的底部 msoBarFloating 4 命令栏浮在屏幕上 msoBarPopup 5 快捷菜单 msoBarMenuBar 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 该对象代表工具栏按钮和菜单选项。当你点击按钮或者选择菜单选项时, 就会执行相应的VBA过程 CommandBarPopup 该对象代表弹出控件,点击时显示一菜单或者子菜单 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 1 msoControlPopup 10 msoControlEdit 2 msoControlDropDown 3

msoControlComboBox 4 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) 返回或者设置由Index指定的列表项目值(列表里的第一个项目索引号为 0) ListCount 返回列表清单里总项目数 ListIndex 返回或者设置清单里选定项 Text 返回或者设置出现在组合框控件部分——文字框显示的文本 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. Add(Type:=msoControlPopup,

241

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 menu bar").Controls("Tools").Controls.Add(Type:=msoControlPopup, Before:=1) _

.Caption = "My Submenu" 当你按下回车键,上面的指令会在工具菜单(工作表菜单栏)的上面放置一个自定义子菜单, 叫做My Submenu。如果你没有将上面的语句输入在一行的话,它就不会起作用。 2. 在立即窗口里输入下述一行指令,可以在子菜单里添加自定义命令: CommandBars("Worksheet menu bar").Controls("Tools").Controls("My Submenu").Controls _
.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: 5: 6: 7:

Cell Column Row Cell

8: Column

9: Row 10: Ply 11: XLM Cell
12: Document

13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52:

Desktop Nondefault Drag and Drop AutoFill Button Dialog Series Plot Area Floor and Walls Trendline Chart Format Data Series Format Axis Format Legend Entry Formula Bar PivotTable Context Menu Query Query Layout AutoCalculate Object/Plot Title Bar (Charting) Layout Pivot Chart Popup Phonetic Information Auto Sum Paste Special Dropdown Find Format Replace Format Shapes Inactive Chart Excel Control Curve Curve Node Curve Segment Pictures Context Menu OLE Object ActiveX Control WordArt Context Menu Rotate Mode Connector 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: 54: 55: 56: 57: 58: 59: 60:

Canvas Popup Organization Chart Popup Diagram Add Command Built-in Menus System Layout Select

53: 54: 55: 56: 57: 58: 59: 60: 61: 62:

Canvas Popup Organization Chart Popup Diagram Layout Select List Range Popup List Range Layout Popup XML Range Popup List Range Layout Popup 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 Shift键 2 Ctrl键 3 Shift和Ctrl键 4 Alt键 5 Alt和Shift键 6 Alt和Ctrl键 7 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. 4. 5. 6. 激活文件Chap13.xls,切换到VB编辑器窗口 使用属性窗口重新命名VBAProject(Chap13.xls)为Debugging 插入模块到Debugging (Chap13.xls)工程,并且更改名Name属性为Breaks 输入过程代码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. 2. 3. 4.

点击语句SpecialMsg myName内部 选择调试|运行到光标处。当到达特定行时,VB将停止执行 按下Shift+F8以逐过程地运行过程SpecialMsg 按下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

' print to the Immediate window 打印到立即窗口 ' Close the file -commenting out this instruction will cause
‘ error 52. 关闭文件 – 注释掉该指令(Close

#1)会导致错误52 Close #1 End If Exit Sub ErrorHandler: FileExists = False 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 关闭工作簿
Workbook WorkbookBeforeClose,

WindowDeactivate,
WorkbookDeactivate, WorkbookActivate, WindowActivate

4.工作表事件

工作表对象相应例如工作表激活和失活事件,计算工作表事件,工作表更改事件和双击或右键单 击事件。本节讨论一些工作表对 象能够响应的事件。 事件名称 激活 事件描述 示例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。 事件名称 失活 事件描述 示例2 Worksheet_Deactivate() 当用户激活不同的工作表时, MsgBox "You deactivated " & _ 发 shtName & "." & vbCrLf & _ 生该事件 "You switched to " & _ ActiveSheet.Name & "." End Sub Private 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的内置参数列 表。 事件名称 跟踪链接 事件描述 示例8 当用户点击Excel工作表中的 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明确 ByVal Target As PivotTable) MsgBox Target.Name & _ 所选地数据透视表。 pivTbl是 " report has been updated." & vbCrLf _ 个 & "The PivotReport is located in cells " & _ 变量,指向类模块里使 用 Target.DataBodyRange.Address WithEvents关键字声明的工作 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() 事件。当用户从其它应用程序切 MsgBox "This workbook contains " & _
换到Excel工作簿时,不会引发该
ThisWorkbook.Sheets.Count & "sheets."

End Sub 事件 当用户激活包含Workbook_Activate事件过程的工作簿时,示例程序显示该工作簿含有的工作表数 目 示例10 – 试验:在VB编辑器窗口,激活工程浏览器窗口并且打开Excel对象文件夹,双击 ThisWorkbook并且在其代码窗口里输 入示例过程。然后,切换到Excel窗口并且打开一个新工作 簿。切换到你输入了Workbook_Activate过程的工作簿,这时,Excel 将显示该工作簿里的工作 表总数。 事件名称 失活 事件描述 示例11 当用户激活Excel的其它工作簿 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创建的月历 想要引发示例17中示范的AddInUninstall事件过程的话,可以选择工具|加载宏,并清除 CalendarMaker加载宏前面的勾选标 志就可以了。 事件名称 卸载加载宏 事件描述 示例17 当用户卸载一个加载宏时,引发 该事件 13.
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 Case Case Case Case Case Case

1 2 3 4 5 6 7
= = = = = = = 1 1 1 1 1 1 1

Range("a3").Value Range("b3").Value Range("c3").Value Range("d3").Value Range("e3").Value Range("f3").Value Range("g3").Value

End Select

' Loop through range a3:g8 incrementing each cell after the "1" For Each cell In Range("a3:g8")

' cell. 在单元格区域A3:G8里面循环,在1之后,每个单元格增加1 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,

息框指明问题,提供新的输入框 MyErrorTrap:

错误导致信

' and resumes at the line that caused the error.

并恢复至导致错误的代码行

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 当用户激活当前工作簿里的任何工作表发
生该事件。SheetActivate事件 同样也可 以发生在应用软件级别,当你激活任意打 开的工作簿里的任意 工作表时,都会激活

SheetDeactivate
SheetSelectionChange

SheetChange SheetCalculate
SheetBeforeDoubleClick SheetBeforeRightClick

事件名称 事件描述 当用户激活任何显示该工作簿 的 Private Sub Workbook_WindowActivate(ByVal _ Wn As Window) 窗口时,引发该事件 Wn.GridlineColor = vbYellow End Sub

该事件 当用户激活工作簿里一个不同的工作表时,引发该事件 当用户选择不同单元格时,引发该事件。 该事件发生在该工作簿里的每 个工作表 上 本事件发生在用户更改单元格内容时 本事件发生在用户重新计算工作表的时候 当用户双击工作表上的单元格时,引发该事件 当用户在工作表单元格上单击右键时,引发该事件 激活窗口 示例19

当用户激活包含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 需要下述两个参数: 用 关键字WithEvents声明一个Application或者Workbook的type对 Sh – 被选择的工作表 象(参 见示例 9,需要使用关键字WithEvents设置事件处理的相关 Target – 被选择的数据透视表信息)。该 事件处理可以在附带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 当用户激活图表工作表时引发该事件 Private Sub Chart_Activate()
MsgBox "You've activated the chart sheet."

Deactivate

End Sub 当用户离开该图表时引发该事件(进入其它工作表)
Private Sub Chart_Deactivate() MsgBox "It looks like you want to leave the chart sheet."

Select

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 当用户更改图表数据点时,引发该事件。图表对象应该在类模块 里使用 关键字WithEvents进行声明。 Calculate 当用户选择新数据或者改变图表数据时,引发该事件。
Private Sub Chart_Calculate() MsgBox "The data in your spreadsheet has " & vbCrLf _ & "changed. Your chart has been updated."

Resize DragOver DragPlot BeforeDoubleClick BeforeRightClick

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

MsgBox "You pressed the right mouse button. "

Else
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 当用户创建一个新工作簿时引发该事件
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

WorkbookOpen

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

WorkbookActivate WorkbookDeactivate WorkbookNewSheet
WorkbookBeforeSave

End If End If End Sub 当用户将焦点移到一个开启的工作簿时引发该事件 当用户将焦点从一个开启的工作簿移开时引发该事件 当用户在一个打开的工作簿上新建一个工作表时引发该事件 该事件发生在以大开工作簿被保护之前

285

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 WorkbookBeforePrint End Sub 该事件发生在以大开工作簿被打印之前
Private Sub App_WorkbookBeforePrint(ByVal _ Wb As Workbook, Cancel As Boolean)

Wb.PrintOut Copies:=2 WorkbookBeforeClose 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 ErrorHandle: Cells(r, 1).Value = p.Name Resume Next End Sub 该事件发生于用户安装加载宏之时 该事件发生于用户卸载加载宏之时
当用户激活开启工作簿中某个工作表时引发该事件

WorkbookAddInInstall WorkbookAddInUninstall SheetActivate SheetDeactivate
SheetSelectionChange

当用户离开某个工作表时引发该事件 当用户改变选择工作表上的区域时引发该事件
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

当用户在一个打开的工作簿里改变单元格里的内容时引发该事 当用户重新计算某个开启的工作簿里的工作表时引发该事件 当用户双击工作表时引发该事件 当用户右键单击工作表单元格时引发该事件 当用户激活一个打开的窗口时引发该事件 Wn.DisplayFormulas = True

WindowActivate

Private Sub App_WindowActivate(ByVal _ Wb As Workbook, ByVal Wn As Window)

286

WindowDeactivate WindowResize WorkbookPivotTableCloseConnection (Excel 2002的新事件) WorkbookPivotTableOpenConnection (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

该事件发生在查询表被刷新数据之前
Private Sub qryTbl_BeforeRefresh(Cancel As Boolean) Response = MsgBox("Are you sure you " _ & " want to refresh now?", vbYesNoCancel) If Response = vbNo Then Cancel = True

AfterRefresh 数Success

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 10.接下来…… 在本章中,你获得了便利的事件经验和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

Set rst = Nothing Set con = Nothing End Sub

' Destroy object variables to reclaim the resources

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) With tbl

' Create fields and append them to the Fields collection .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过 程 在立即窗

口里在一行 输入下述语 句: Amount") □ 从一个子过程运行DisplayAccessReport2过程: 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) Call DisplayAccessReport2("C:\Program Files\Microsoft Office\Office\Samples\Northwind.mdb", "Sales Totals by

' Enter the following procedure in the Code window

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 Dim Dim Dim Dim

cat As ADOX.Catalog cmd As ADODB.Command rst As ADODB.Recordset i As Integer 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 Dim Dim Dim Dim Dim Dim

cat As ADOX.Catalog cmd As ADODB.Command rst As ADODB.Recordset i As Integer dbPath As String StartDate As String 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 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 Selection.CurrentRegion.Columns.AutoFit

If objAccess Is Nothing Then End If MsgBox "You will get " & _

Set objAccess = CreateObject("Access.Application")

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 Dim Dim Dim Dim Dim

i As Integer j As Integer strPath As String a As Variant countR As Long 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 Options:=adCmdTable)

Set rst = conn.Execute(CommandText:="Products", _ ' begin with the first record rst.MoveFirst

' transfer the data to Excel .CurrentRegion.Clear Next j

' get the names of fields first With Worksheets("Sheet3").Range("A1") For j = 0 To rst.Fields.Count - 1 .Offset(0, j) = rst.Fields(j).Name .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 5 8 8 acSpreadsheetTypeExcel5 acSpreadsheetTypeExcel7 acSpreadsheetTypeExcel8 acSpreadsheetTypeExcel9

2
3 7

acSpreadsheetTypeLotusWK1 acSpreadsheetTypeLotusWK3 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 Exit Sub ErrorHandler: End Sub
AccessTbl_From_ExcelDataExit:

MsgBox Err.Number & ": " & Err.Description Resume AccessTbl_From_ExcelDataExit

315

28.接下来…… 本章提供了多个例子将Excel数据放入Access,以及从Access获取数据到工作表。你学习了如 何从Excel VBA过程中 控制Access应用软件,执行一些任务,例如打开Access窗体和报告,创 建新窗体,运行选择和参数查询,以及调用Access内置 函数。另外,本章示范了一些创建文本 文件,查询表,和图表的技术。你也学习了如何使用链接,导入和动态Access表,将Excel 数据 放置到Access数据库里。在下章中,你将学习如何使用Excel创建,察看和分析因特网数据。

316…...

Similar Documents

Premium Essay

Study

...[pic] __________________________________________________________ UNIVERSITY COMPULSORY SUBJECT EXAMINATION FOR THE BACHELOR OF SCIENCE (HONS) ACCOUNTING AND FINANCE BACHELOR OF SCIENCE (HONS) BUSINESS MANAGEMENT BACHELOR OF SCIENCE (HONS) BUSINESS STUDIES YEAR OF STUDY: YEAR 1 UCS 1000: STUDY SKILLS FEBRUARY 2009 Reading time: 15 minutes Writing time : 2 Hours _________________________________________________________ INSTRUCTIONS TO CANDIDATES This paper contains three sections: Section A, Section B and Section C. Answer ALL questions in the answer booklet provided. This paper makes up 40% of the total assessment of this subject. This paper is printed on FOUR (4) pages Section A [20 marks] There are TEN questions for Section A. Answer ALL questions in complete sentences. |1. |State TWO reasons why it is important to set goals in learning. | | |(2 marks) | |2. |Why is it important for college students to do prior reading before lectures or tutorials? | | |(2 marks) | |3. ...

Words: 554 - Pages: 3

Free Essay

John Walkenbach - Excel Vba Programming

...com/cheatsheet/excelvbaprogramming Get Smart at Dummies.com Dummies.com makes your life easier with 1,000s of answers on everything from removing wallpaper to using the latest version of Windows. Check out our • Videos • Illustrated Articles • Step-by-Step Instructions Plus, each month you can win valuable prizes by entering our Dummies.com sweepstakes. * Want a weekly dose of Dummies? Sign up for Newsletters on • Digital Photography • Microsoft Windows & Office • Personal Finance & Investing • Health & Wellness • Computing, iPods & Cell Phones • eBay • Internet • Food, Home & Garden Find out “HOW” at Dummies.com *Sweepstakes not currently available in all countries; visit Dummies.com for official rules. Excel VBA Programming ® FOR DUMmIES 2ND ‰ EDITION by John Walkenbach Excel® VBA Programming For Dummies®, 2nd Edition Published by Wiley Publishing, Inc. 111 River Street Hoboken, NJ 07030-5774 www.wiley.com Copyright © 2010 by Wiley Publishing, Inc., Indianapolis, Indiana Published by Wiley Publishing, Inc., Indianapolis, Indiana Published simultaneously in Canada No part of this publication may be reproduced, stored in a retrieval system or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, scanning or otherwise, except as permitted under Sections 107 or 108 of the 1976 United States Copyright Act, without either the prior written permission of the Publisher, or authorization through payment of the......

Words: 98776 - Pages: 396

Free Essay

Study

...http://www.aksnewarkde.com/StudyHabits.html GOOD STUDY HABITS... ...are the key to keeping up your grades Remember: “If you improve your study habits, improving your grades will be easier…” 1. Make a list. It all starts with ‘planning’. Make a list of all the things you have to do. Break it down into smaller chunks. Then check off your accomplishments as you go. 2. Do the hard stuff first…then the easy. Put your energy into the harder assignments…when you have less energy or your brain is tired, you can then tackle the easier tasks. 3. Proper study space. Make a place to study. When you use the same place to study, day after day, your body becomes trained. You will focus your concentration more quickly there. 4. Snack. Don’t try to study on an empty stomach. If you’re hungry, you’ll be distracted. So grab an apple or some carrot sticks. Just avoid dairy products or turkey, which tend to be natural sedatives (make you sleepy). 5. No distractions. Turn off the stereo, TV or radio. Silence is the best environment to study in. 6. Study buddy. Don’t overlook the value of a study partner. Quiz each other, compare notes and practice testing. You can also help one another from becoming bored. But avoid distracting each other from what you are there to do. 7. Write it down. Experts agree that the more senses you use to absorb information, the better you’ll remember it. Don’t just read about what......

Words: 372 - Pages: 2

Free Essay

Study

...Ralph Stinebrickner1 please direct correspondence to Todd R. Stinebrickner Dept. of Economics The Social Science Centre The University of Western Ontario London Ontario Canada n6a 5c2 trstineb@julian.uwo.ca phone 519 679-2111 ext. 5293 fax 519 661-3666 Unique new data from a college with a mandatory work-study program are used to examine the relationship between working during school and academic performance. Particular attention is paid to the importance of biases that are potentially present because the number of hours that are worked is endogenously chosen by the individual. A “naive” OLS regression, which indicates that a positive and statistically significant relationship exists between hours-worked and grade performance, highlights the potential importance of endogeneity bias in this context. Although a fixed effects estimator suggests that working an additional hour has an effect on grades which is quantitatively very close to zero, we suggest that there are likely to exist causes of endogeneity which are not addressed by the fixed effects estimator. Indeed, an instrumental variables approach, which takes advantage of unique institutional details of the work-study program at this school, indicates that working an additional hour has a negative and quantitatively large effect on grade performance at this school. The results suggest that, even if results appear “reasonable,” a researcher should be cautious when drawing policy conclusions about the relationship......

Words: 3812 - Pages: 16

Premium Essay

Study

...An Analysis of Determinants of Profitability in Public and Private Sector Banks in India Mrs. Somanadevi Thiagarajan Ph.D. Scholar, Management Sciences, Anna University of Technology, Coimbatore, India Lecturer (on leave) Faculty of Management, University of Belize, Belize Dr. S. Ayyappan Associate Professor in MBA Sakthi Institute of Information and Management Studies Pollachi -642001, India Dr. A. Ramachandran Director, SNR Institute of Management Sciences, SNR Sons College, Coimbatore, India Mr. M. Sakthivadivel Anna University of Technology Coimbatore, India Abstract An analysis was carried out to empirically evaluate the determinants of profitability in the public and private sector commercial banks in India. A combination of statistical tools such as the correlation analysis, multiple regression analysis and factor analysis were used to estimate the contribution of select bank specific variables towards profitability which was measured by using Return on Assets (RoA). The study revealed that the cost of borrowing and NPA has a significant negative correlation with profitability for public sector banks. Return on investments, return on advances and operating profit had a significant positive correlation with profitability for both public and private sector banks. The multiple regression analysis highlighted that the return on investments and return on advances has a significant influence on the profitability of private sector banks. The factor analysis has also shown......

Words: 2934 - Pages: 12

Free Essay

Study

...and let them success. * Sport: educate people more about * Sports are importance to health * Prevent people from ills * Get rid of any drugs * Education: * People value in themselves and society * Educate people to get in touch in study and let them know how importance of learning * People become good citizen and good behavior * Make people respect to their parents and the elder * Educate people to act in good * Prevent people from crime * Health care: provide people more knowledge * People know how to maintain health, living, clean and well-being * Exercise to get rid of any ills * To eat without harm * Environments: give many advantages to people like: * People know how to preserve and protect the environments * People protect the forest * Make environment clean * Products: give importance to people like: * People know how to use product correctively and maintain to the quality and know exactly to the expires of products * Films: educate people in positive ways like: * Respect and gratitude parents * Help people act in good and not to be deal with crime * Educate people to determine their future and let them try to study * Educate people not to violent in their family or in society * Disadvantages: the effects of disadvantages of advertising on television consist of technology, products, news and films. * Technology: the way of this advertise do may give people......

Words: 516 - Pages: 3

Free Essay

Study

...ABSTRACT Objective: This qualitative descriptive study investigates how public health nurses working within the child welfare system view the organization and the organizationÕs effect on their case management practice. Design: Semistructured interviews were conducted utilizing the Bolman—Deal Organizational Model. This model identifies four frames of an organization: symbolic, human resources, political, and structural. Sample: A purposive sample of nine nurses and one social worker was selected to participate in comprehensive interviews. Results: Data analysis identified two main themes. The first theme was the presence of organizational structural barriers to providing case management. The second theme was the lack of political influence by the nurses to change the structure of the organization; hence, their skills could be more completely utilized. Conclusions: Public health nurses who work in child welfare will need to systematically analyze their role within the organization and understand how to work in Òhost settings.Ó Nursing educators need to prepare public health nurses to work in non-health care settings by teaching organizational analysis. Key words: child protective services, organization, public health nursing. The purpose of this exploratory qualitative descriptive study was to investigate how public health nurses working in the child welfare system in a large metropolitan county in California view the organization. The study utilized the Bolman—Deal......

Words: 3721 - Pages: 15

Free Essay

Vba Intro

...An Introduction to VBA in Excel Robert L. McDonald† First draft: November, 1995 November 3, 2000 ∗ Abstract This is a tutorial showing how to use the macro facility in Microsoft Office—Visual Basic for Applications—to simplify analytical tasks in Excel. Contents 1 Introduction 2 Calculations without VBA 3 How to Learn VBA 4 Calculations with VBA 4.1 Creating a simple function . . . . . . . . . . . . 4.2 A Simple Example of a Subroutine . . . . . . . 4.3 Creating a Button to Invoke a Subroutine . . . 4.4 Functions can call functions . . . . . . . . . . . 4.5 Illegal Function Names . . . . . . . . . . . . . . 4.6 Differences Between Functions and Subroutines 3 3 4 5 5 7 7 8 9 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ∗ Copyright c 1995-2000 Robert L. McDonald. Thanks to Jim Dana for asking stimulating questions about VBA. † Finance Dept, Kellogg School, Northwestern University, 2001 Sheridan Rd., Evanston, IL 60208, tel: 847-491-8344, fax: 847-491-5719, E-mail: r-mcdonald@northwestern.edu. CONTENTS 2 5 Storing and Retrieving Variables in a Worksheet 5.1 Using a named range to read and write numbers from spreadsheet . . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Reading and Writing to Cells Which are not Named. . . 5.3 Using the “Cells” Function to Read and Write to Cells. 10 the . . . . . . . . . 11 12 13 6 Using Excel Functions 13 6.1 Using VBA to compute the......

Words: 10883 - Pages: 44

Free Essay

Study

...ARS 100 STUDY GUIDE for QUIZ 2 & EXAM #1 (Chapters 1, 2, 3, 4) Questions on the exam are not limited to the content of this study guide. Questions are derived not only from the study guide, but also from lectures, and readings. You should know the definitions and also be able to identify whether they apply to an image listed in the image list. Quiz 2 Focus of Study: What is Art Lecture Visual elements (Chapter 2 & The Visual Elements of Art Lecture) Terms related to color (Chapter 2 & The Visual Elements of Art Lecture) Motion/implied motion (Chapter 2 & The Visual Elements of Art Lecture) Techniques for creating the illusion of three-dimension (Chapter 2 & The Visual Elements of Art Lecture) Techniques for creating the illusion of depth (Chapter 2 & The Visual Elements of Art Lecture) Design principles (Chapter 3 & The Principles of Design Lecture) Exam 1 Focus of Study KEY TERMS: Trompe l’oeil Nonobjective art Representational art Impasto Iconography Chiaroscuro Contrapposto value Linear perspective Emphasis Expressionistic art scale/hierarchical scale Unity & variety Design principles Visual elements Abstract art Hue Overlapping Saturation Realistic/Realism art Analogous colors Atmospheric perspective Complementary colors Visual elements Design principles Form/Content/ Style Volume Primary colors Local color Mass Shade Tint Kinetic art Texture Types of line (implied, contour, outline, spontaneous, gestural, psychological, actual) Function of line (create depth and texture,...

Words: 412 - Pages: 2

Premium Essay

Excel Vba for Dummies Outline

...Excel VBA Programming Dummies Outline and Notes Jason Xu II: How VBA Works with Excel Chapter 3: Working in the Visual Basic Editor * To continue a single line of code from one line to the next, end the first line with a space followed by an underscore (_) * Option Explicit, inserted at the beginning of a VBA Module, forces variables to be defined (Should always use). Chapter 4: Introducing the Excel Object Model * A collection is a group of objects of the same type. * Workbooks: A collection of all currently open Workbook objects * Worksheets: A collection of all Worksheet objects contained in a particular Workbook object * Sheets: A collection of all sheets (regardless of their type) contained in a particular Workbook object. Contains both Worksheets and Charts. * Etc…. (collections are plural) * Referring to Objects * To reference a single object from a collection, you put the object’s name or index number in parentheses after the name of the collection: * Worksheets(“Sheet1”) * Excel won’t be able to identify the object if the quotation marks are omitted. * If Sheet1 if the first (or only) worksheet in the collection, you can also use the following reference: * Worksheets(1) * If referring to an object by its index number, use a plain number without quotation marks. * Navigating through the hierarchy * Application is at top of the hierarchy * Move...

Words: 2094 - Pages: 9

Premium Essay

Study

...Q: What is the definition of "study habits"? A: The definition of study habits is the habitual practices one uses to help them study and learn. Good study habits can help students achieve and/or maintain good grades. Q: How do you study better? A: Effective studying requires thinking positively, studying in a quiet area, rewriting notes, playing memory games and remaining healthy. Students also need to take breaks and reward themselves periodically. To think in a positive manner, students must think about their talents and skills. Negative and defeatist attitudes must be dispelled from the mind. Students also need to avoid comparing their achievements and successes with other people. Getting away from distractions is imperative. For instance, using a computer when taking notes can lead to playing games or surfing the Internet. Using a notebook instead of a computer helps a person focus better. However, listening to a favorite song on an iPod can motivate students to study better. Libraries and quiet off-campus spots are suitable places to learn. Eating a healthy diet is important because the vitamins and nutrients ensure maximum energy when studying. Certain herbs, such as ginko and ginseng, may enhance memorization skills. Exercise and healthy eating are effective aids when studying. Studying for an hour and taking a five-minute break fosters a more productive study session. Breaking chapters into sections avoids studying for long periods. Rewards can come in......

Words: 268 - Pages: 2

Premium Essay

Study

...It is possible for students to achieve success in the examination if the correct strategies and study skills are used. Studying effectively does not depend so much on how long one spends with book and notes but how effectively one has been studying. One of the most important recipes for success in examination is to study regularly and consistently. After all ‘Rome was not built in a day’. It is necessary, therefore to draw up a timetable with time set aside for recreation, exercise or television. Equally important is the discipline to stick to the period timetable. Without a study plan or timetable, some subject me be inadequate focus and attention. Another factor that is crucial for examination success is the need to do homework and assignments. Homework and assignments reinforce learning and helps students to identify what they do not know so that remedial work can be done immediately. In addition, homework helps students to practice and reinforce classroom learning. Preparing mind maps is another study skills that can be used to make learning more effective and more meaningful. For subject with a lot of content matter such as History and Biology, mind maps are essential for various reasons. For one, mind maps helps students to focus on the important elements as well as to see relationship between the element more clearly. Next, mind maps are useful tools for revision because the mind can see and remember more easily if ideas and concepts are presented in pictures and......

Words: 251 - Pages: 2

Free Essay

Vba Research Paper

...How to access VBA in excel 2013: Excel option—quickaccess toolbar—customized ribbonr—developer or just alt+F11 How to access VBA in excel 2013: Excel option—quickaccess toolbar—customized ribbonr—developer or just alt+F11 VBA 1. VBA format: a. upper left corner: project: * Automatically created when create a new workbook * Only one project per workbook * Can contain on or more modules; modules are VBA codes, which is separated into macros or procedure; macro is a set of instructions for the computer written in VBA, which is text readable. 2. Reason why create macros is to manipulate data in workbook environment 3. Programming language: Sub myfirstmacro() Commentary will automatically be green Commentary will automatically be green ‘my first macro ‘This is commentary Sheets(”Sheet 1”). Select Range (“A3”).Select ActiveCell. Value = 5 Sheets(“Sheet 1”). Select Range (“B2). Select ActiveCell. Value=62 End Sub 4. Variables: Ex1: a=a+1, if a = 5, a now is 6. Previous value of a is not existed any more. Ex2: c= “hi all!”. Words must be included in quotation mark. Ex3: d=c&”Bye!” comes to hi all!bye! a. variable names: * Must begin with a letter * Only can be letters, numbers and underscores * <=255 characters * Cannot be special reserved word like print or save 5.programming language2 Sub mathfun() Sheets("sheet1").Select Range("B3").Select a = ActiveCell.Value Range("B4").Select Summy is variable of sum...

Words: 1906 - Pages: 8

Premium Essay

Case Studies on Performance Management

... CASE STUDIES IN PERFORMANCE MANAGEMENT A Guide from the Experts TONY ADKINS John Wiley & Sons, Inc. CASE STUDIES IN PERFORMANCE MANAGEMENT CASE STUDIES IN PERFORMANCE MANAGEMENT A Guide from the Experts TONY ADKINS John Wiley & Sons, Inc. This book is printed on acid-free paper. Copyright © 2006 by SAS Institute. All rights reserved. SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. ® indicates USA registration. Other brand and product names are trademarks of their respective companies. Published by John Wiley & Sons, Inc., Hoboken, New Jersey. Published simultaneously in Canada. No part of this publication may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, scanning, or otherwise, except as permitted under Section 107 or 108 of the 1976 United States Copyright Act, without either the prior written permission of the Publisher, or authorization through payment of the appropriate per-copy fee to the Copyright Clearance Center, Inc., 222 Rosewood Drive, Danvers, MA 01923, 978-750-8400, fax 978-646-8600, or on the web at www.copyright.com. Requests to the publisher for permission should be addressed to the Permissions Department, John Wiley & Sons, Inc., 111 River Street, Hoboken, NJ 07030, 201-748-6011, fax 201-748-6008, or online at......

Words: 77510 - Pages: 311

Free Essay

Studies

...................16 Mathematics, Reading, and Time Preferences ..................................................... 17 Musical Talent....................................................................................................... 17 Physiological Elements and Achievement............................................................. 18 Reading and Sound .............................................................................................. 19 Reading and Light ................................................................................................. 19 Reading Comprehension and Design (Study Area) .............................................. 20 Rural/Inner City Student Background....................................................................20 Science Achievement with Computer-Assisted Instruction ................................... 21 Social Studies ....................................................................................................... 21 Sociological Elements and Achievement .............................................................. 22 Time of Day Preferences and Achievement.......................................................... 23 Vocational Education/Industrial Arts ..................................................................... 25 Word-Pair Recognition and Mobility ...................................................................... 26 Word Recognition and Temperature ..................................................

Words: 3036 - Pages: 13

Best Match | Autostoeltjes | Bodysuits & One-Pieces