1、序言 speedfirst 2008-12-4 原版载于 http:/ 目录 目录 0. 序言 . 1 1. 基本编程 元素 . 6 2. 对象 . 12 3. 开始编写 完整的 程序 . 19 4. 操作文件 . 24 5. 事件 . 34 6. 用户窗体 . 38 7. 根据 VBA 制 作展示 PPT . 42 8. 提取 Word 中未 样式化 的 标题 51 9. 操作数据 库 . 55 10. 操作 Windows API . 61 序言 1 0. 序言 某圣贤说过, 人和动物的最大之不同在于人知道怎么利用工具。VBA 就是一种工 具, 一种可以创造工具的工具。VBA 提供了给你
2、充分的自由, 做几乎任何其他编 程语言或者环境能做的事情, 避免重复的手动劳动。 当然, 有太多人对编程充满 了敬畏, 认为编程是类似周小川做金融决策, 或者爱因斯坦在思考问题那样很玄 幻、 难以理解的事情, 更不用说运用了。 但其实, 这更多的是误解 (我觉得这些 误解源自于国内的编程教科书上那些高深莫测的概念和诘屈聱牙的讲解) 。 事实 上,20 多年来编程正朝 着越来越人性化, 越来 越容易学的方向发展。VBA,确切 的说是VB(我在下面会解释这个微妙的差别)是众多语言中最容易学的,正如 其名字一样,又”Visual”,又”Basic”。所以大可不必担心诸如“我没有编 程基础”之类的心理
3、障碍。 VBA 的全称是 Visual Basic for Applications 的简称。其意思是开发环境被整 合到了某个应用程序的Visual Basic 语言。在本教程中特指整合在MS Office 中的VBA。(其余的还有比如VBA for AutoCAD, VBA for CorelDraw )我简单的 将一门编程语言分为两个大的部分, 第一部分是其语法。 语法规定了编程指令执 行的顺序和内存的使用方式。 尽管如此, 语法本身不能解决任何实际的问题, 这 就好比你知道了英语语法, 但是不懂单词和短语, 就不能正确沟通一样; 另一个 部分我称之为“库”, 就是其他人写好了的, 可以完
4、成一定功能的东西。 你可以 调用库的某一个部分来完成自己想做的事情。 语法和库在一起合作, 最终可以完 成复杂的任务。对于VBA 来讲,使用的是VB 的语法(确切的说是VB6 的语法, 与目前的VB.Net 完全不同, 如果你不知道我在说什么, 那么就忽略这句话好了) , 采用的是COM 组件这种库, 库的功能是让你能操作Office 的各种功能 (VB 也包 含一个基本的库, 以完成一些常见的操作, 比如访问文件, 字符串操作, 数学运 算等。这个库的形式是Func 和Sub,而不是COM)。比如代码: For i = 1 To 100 ActiveWindow.Captain = i Ne
5、xt 完成的工作是让当前Office 组件的窗口标题在一瞬间从1 变成2,3,100。 FORNEXT 是VB 规定的语法,用于循环。这些词语被称为“关键字”,你不能 将关键字用于其他用途。 而ActiveWindow 是指当前活动的Windows 窗口, Captain 指窗口标题,这些就是提供Office 功能的COM 组件。关于COM 组件到底是怎么 工作的我会在第1 章进行更详细的介绍。你可以类似的写出如下的代码: sum = 0 For i = 1 To 100 sum = sum + i Next 序言 2 就变成了求1+2+3+100 的和。而: For i = 1 To 100
6、 MsgBox i Next 可以显示100 次对话框,依次显示1,2,100。可见语法并不关心你到底用了 哪些库在做什么, 而仅仅是完成其任务而已 (在这里是循环) 。 在今后的教程中, 我会将重点放在介绍Office 库提供的各种功能上, 而不会花很多时间介绍语法。 仅仅是在第一次用某种语法举例子时, 我会说明一下。 很快你就会发现这是很简 单的一件事情。 在正式介绍编程之间,我简单说一下VBA 的开发环境,也 就是你写代码的地方。 如果你用的是Office 2003,在 “工具”菜单找“宏”, 在其子菜单中找”Visual Basic”, 就可以打开一个新的窗口。 如果是在 Office
7、 2007 中, 若你没看到“开 发工具”选项卡的话,先到选项中打开它。 然后在“开发工具”选项卡的“代码”区域里按”Visual Basic”按键, 就可以 打开同样的界面。(我用Excel 举例) 序言 3 2003 和2007 的界面完全一样。因为Office2007 添加了若干新的功能,所以只 有库比起2003 来多了些东西,表面上看不出来。 在“工程”视图中,右键单 击”ThisWorkbook”,然后 在菜单中选“插入”-“模块”, 就会在主界面出现 一片空白。键入我在图中写的代码: Sub test() MsgBox “Hello, VBA“ End Sub 然后点击上面的绿色箭
8、头, 就可以运行它了。 看看出了什么?: ) 这就是你的第 一个VBA 程序了。 记住这个写代码的过程, 以后我们会反复用到它。 并且我不会 再重复。如果你有兴趣,在桌面上新建一个文本文件,将MsgBox “Hello, VBA“ 这句放到里面(不要第一句和第三句),然后保存为test.vbs。(注意不是 test.vbs.txt, 一定要把默认的扩展名去掉) 然后双击这个文件, 你又看到了什 么? (如果有杀毒软件或者防火墙问你是否执行脚本, 请确认允许。 这段代码不 会带来任何伤害)。 以下是对一些常见问题的回答: 1. 宏和VBA 是什么 关系 ? 广义上来讲, 宏就是VBA。我说“一段
9、宏代码”和 “一段VBA 代码”是等价的。Office 的自动录制功能可以帮你录制一个 宏, 实际上就是把你的动作用VBA 代码的形式记录下来。 狭义上, 宏是指 一个无参数的非Private 的Sub(我会在后边解释什么是Sub),如果手 写代码的话,只有这种Sub 才会出现在“宏”那个对话框的列表里。 序言 4 2. 既 然可 以录制 宏, 为 何还 要手 写代码 ? 首先, 宏录制的局限性很大, 很多 动作(比如鼠标行为)是录不下来的。另外宏只能 录 制 顺 序 的 多 个 指 令 , 如上面的比较复杂一点的循环就不可能录制。而且宏录制不能覆盖VBA 的强大功能。 例如VBA 可以直接读
10、写文件系统中的任何文件。 这些动作是 录不下来的。最后,录制宏得到的代码非常死板, 不 灵 活 , 而 且 很 冗 余 。 如果你知道了怎么写,就会立刻抛掉这个宏录制功能。事实上 PowerPoint2007 已经取消了宏录制。 3. VBA 能干什 么? 如果你 想避免重复劳动,或者需要多个文档协作,VBA 是 最好的选择。虽然默认下,VBA 只可以操作VB 自带的库和Office 的库, 但可以通过添加引用的方式来为其扩充功能。理论上VBA 可以操作任何 Windows API(代表你的Windows 能做的任意事情),和任意用COM 封装 的组件。比如像Acrobat,EndNote 等
11、都是这么做的。它们可以使自身的 功能和办公紧密结合。如果你高兴,大可以写一个Word 用的聊天插件:) 4. VBA 不能干 什么 ?什么时 候不 适合使 用 VBA?VBA 的功能取决于其库。如 果没有库提供一个功能 (比如让Excel 一个单元格显示一副图片) , 那么 VBA 也不能帮你。此外,你还可以用VBA 编写插件来完成普通Office 不 能完成的功能。 此外, 如果你开发的项目过于庞大, 则应该换一门更专业 的编程平台 (比如.Net ) , 以便更好的组织代 码和查错。VBA 比较适 合短 小的,与工作任务很直接的问题。 5. VBA 能访问 某个 文本文件 么? 当然可以。
12、 VBA 直接提供OPEN 关键字来访问 文件。 6. VBA 能模拟 键盘 输入么? 当然可以。 VBA 直接提供SendKeys 函数来做到这 一点。 7. VBA 会被保 存在 哪里?VBA 是保存在Office 文档中的, 具体可以是 某一个 文档, 或者某一个文档的模板中。 具体的位置需要在工程视图中选择 (如 上图) 。 如果你希望一批文档都能用到同一个功能, 最好先写个模板, 然 后将VBA 写到模板里。 然后利用此模板来生成多个文档。 当然,VBA 代码 也可以单独导出保存。 在工程视图中右键单击某一个模块然后点“导出文 件”即可。 8. 不同Office 组件 可以互 操作
13、么? 当然可以。VBA 的功能是由COM 库来添 加的。默认情况下某一个Office 组件只引用了当前这个组件的库。但是 你也可以手动添加其他库的引用。 在VBA 开发环境的“工具”菜单里点击 “引用”, 就可以添加Windows 上安装的任意COM 组件。 这样, 不光不同 Office 组件可以互操作,你可以操作比如Media Player,IE 等程序。 9. VBA 可以写 图形 界面么? 当然可以。 不然就说 不上”Visual”了。VB 一向 支持一种被称为UserForm 的模块。你可以在上面添加各种控件。 10.VBA 还提供 哪些 其他的功 能?VBA 可以捕捉Office
14、的事件, 于是可以编写 如“当打开一个文档, 要做某事”;“当Excel 计算发生了错误, 则做某 事”这样的程序。VBA 还有定时器,让你可以做周期性的工作。 11.除了VBA , 还有 哪些开发 平台 可以操 作 Office ? 实际上,任何可以 使用 COM 组件的语言都可以访问Office 的功能。在微软平台上的各种语言基 本上都满足这个条件。 不过有几个平台是专门针对Office 的。 如.Net 上 的VSTO(Visual Studio Tools for Office Developers)。这是一个利 用Visual Studio 进行Office 开发的集合环境,适合大型
15、项目。不过为序言 5 了使用它,你必须得安装 Visual Studio 2005 或者2008。这对于办公族 来说负担过重了。 只有开发复杂的插件, 或者开发企业办公管理系统的人 才应该使用它。 此外Apache 基金会有一个POI 项目, 可以让Java 程序访 问Office 文档。这样用Java 开发,同时又想自动生成Office 文档的人 (比如用Java 写实验,但想用Excel 文档统计实验结果)可以利用POI 来达到他们的目标。不过POI 目前不支持Office 2007 的文件格式。 基本编程元素 6 1. 基本编程元素 这一期就是要介绍在VBA 环境下你能使用什么东西。 一
16、般来讲, 新 手往往对到底 写什么会引起什么结果感到比较混乱。比如写MsgBox 可以出现对话框,但写 MessageBox 为什么就不可以。在VBA 中,直接写代码,而不是在Sub 里写就会 报错?为何是Sub,而不是Suc,不是Sud?等等。我会将最常见的做概要性的 讲解。 但是我不会深入去介绍每个细节的语法。 精简够用就好。 如果有必要, 你 可以查任何的关于VB 的参考手册。 Sub 与 Function 最基本的问题是:如果你写了很多模块,每个模块有很多代码,那么你的程序代 码是从哪一句开始执行?答案是,VB 将代码组成一段一段的,每段从头到尾执 行(当然,这是粗略的说,因为代码还可
17、能有跳转和循环)。每一段叫做一个 Sub 或着一个Function。Sub 是子过程“Subroutine”的简称。 比如像如下的代 码就定义了一个Sub。 Sub Test() MsgBox “Hello“ Show Hello with a messagebox End Sub 其中”Sub”这个关键字标志着Sub 的开始,End Sub 则表示其结束。 其中的Sub 和End Sub 是关键字。所谓关键字就是VB 定义好的,有特殊意义的词汇。在写 代码时不可以写错一个字母, 否则就不被程序解释器接受 (但是你可以不区分大 小写,VBA 开发环境会自动帮你调整) 。 如果 你在开发环境中写
18、了这段代码就会 发现关键字都被表为蓝色。其他的,如For,Next,If,Then,While 等都是关 键字。以上代码中的Test 是一个名称,即这个Sub 的名称。你可以随意定义这 个名称, 只要其不包含一些特别的符号, 并且不和关键字重名 (注意: 绝对不能 包含空白字符,如空格或制表符)。写完这段代码后,在Office 的“宏”对话 框列表里就能看到一项“Test”了。 代码中的括号是用于定义参数的。 如果想在 Sub/Function 上加输入参数,都是在括号中写一个列表,即 参数名 1 As 类型 1 , 参数名 2 As 类型 2 ,. 参数和类型在下面的“变量与类型”一节更详细
19、的介绍。 即使没有参数也要写个 空括号来占位置。这是VB 的要求。之后,当你将键盘光标放在这个Sub 里,并 点击上面的绿色箭头;或者到“宏”对话框执行Test,这个Sub 就被执行了。 另外值得注意的是单引号后边的文字,称为注释。VB 中所有写在单引号后边的 文字都会被解释器忽略掉, 它们仅仅是给人看的。 所以你可以在写代码时加上注 释,方便其他人看懂你的程序。 基本编程元素 7 Function 和Sub 是很类似的东西。比如: Function Add(a1 As Integer, a2 As Integer) As Integer sum = a1 + a2 Add = sum End
20、 Function 这个例子实现了一个Function,含义是将两个整数加起来。这次我添加了两个 参数a1 和a2 用于输入,其类型均为Integer。参数的语法Sub 和Function 是 一样的。形式上,Function 和Sub 仅仅有关键字的差别。Function 与Sub 最大 的不同就在于Function 需要返回一个值,而Sub 不需要。第一行最后那个“As Integer”是指返回值的类型。返回的语法是: Function 名称= 想要返回的值 那么什么叫“返回”呢?这是因为程序段之间是可以互相调用的。Sub 和 Function 可以调用其他的Sub 和Function。整
21、个程序的执行顺序大致如下图所 示: 图中的矩形相当于一个Sub 或者Function,箭头代表程序的执行顺序(注意矩 形的长短不代表代码的长短, 而仅仅是排版需要;并且调用层数不一定是3 层) 。 每个Sub/Function 都可以调用其他的Sub/Function, 这种形式造成了一种层数 无限定的树状结构。 在调用时, 上层可以以参数的形式给予下层输入, 而下层将 控制权返回给上层的时候,也可以给上层一个返回值(只有Function 可以)。基本编程元素 8 比如上面的Sub “Test” 调用了VB 已经定义好的MsgBox 这个Function 来完 成工作(是不是觉得MsgBox
22、应该是一个Sub?实际上MsgBox 会返回一个整数值 表示用户是按哪个键关闭对话框的, 比如“确定”,“取消”,“重试”等。 只 不过在最简单的情况下我们不关心这个返回值罢了) 。 另外, 我们也可以写一个 程序让Test 调用Add。 Sub Test() sum = Add(3,4) MsgBox sum End Sub 这样就能用对话框显示3 与4 的和了。 在实践当 中, Sub 一般用于执行某个动作; 而Function 则在Excel 自定义函数中特别有用。VB 为开发者提供了一个很大的 库,即你可以直接使用许许多多的Sub 和Function 来完成常见的任务(这和 Offic
23、e 库无关) , 比如字符串操作的Substr, Trim, Len; 文件操作的Dir, Write; 数学运算的Log,Sqr 等。完整的清单可以在任何一本VB 的参考手册上找到。 变量与类型 我们在算数的时候需要草稿纸, 将计算的中间结果记录下来。 计算机算数也不例 外, 也得需要在内存中开辟一些空间来记录这些内容。 定义一块空间的方法的代 码是: Dim var 或者 Dim var As Type 或者就像上文Test 中的sum 那样不经定义直接使用。其中Dim 和 As 是关键字, 而var 就是变量名。你可以随意定义变量名称,只要其不 包 含 一 些 特 别 的 符 号 , 并
24、且不和关键字重名。 既然是分配一块内存空间, 那么分配空间有多大呢, 又是 怎么使用的呢?这就由类型来定义了。 一个类型规定了要分配的空间的大小和内 部结构, 比如 Integer 整数就分配2 个Byte (所以其范围是-32768 32767); 而Long 长整型数分配4Byte; Double 双精浮点数 (小数) 就分配8 个Byte; String 字符串的长度就是其存储字符串长度+一块用于存放字符串的长度的额外空间。 以上这些类型由VB 定义, 被称为基本类型 ( 还有其它几个基本类型可以使用) 。 VB 会尽可能在你混用这些基本类型时进行自动转换。比如: Dim str As
25、String Dim n As Integer str = 12 now str is “12“ n = “34“ now n is 34 n = 23.45 now n is 23 基本编程元素 9 可见, 在类型转换中, 可能会有数据损失。 此外对于对象类型 (下文将介绍) 来 讲, 将一个类型的值赋给另一个类型的变量是会报错的。 因此建议大家尽量减少 混用的情况。 一种特别的类型是Variant。 这种变量可以放置任何类型的数据。 比如上文中的 Dim var 就等价于 Dim var As Variant。但是要注意的是Variant 并不意味着变 量内容的类型可以变化。 它仅仅是能放
26、置任何类型的数据而已, 同一时间只有一 个”实际类型”。 为此, 其内部必须维护当前到底是什么类型, 还需要在被访问 时进行内部的二次解释。所以它消耗的空间更大,并且性能更差。另外如果用 Variant 变量表示一个对象, 在写变量名后输入一个点, 就不会有自动提示出现。 因此,建议大家少用Variant 类型。 最后说一下作用域的问题。每一个变量都有一个作用域。如果在一个 Sub/Function 内定义变量,那么这个变量的作用域就仅仅限于这个 Sub/Function,称为局部变量。也就是说,在其他Sub/Function 中是访问不到 这个变量的;或者说,如果在其他Sub/Functio
27、n 中定义了同名的变量,二者完 全是两码事,互相不会有任何影响。 在Sub/Function 中的参数也是一种局部变量,其定义的方式就是比常规方法省 略了Dim。 其形式正如上文所写的那样。 当然, 你也可以在参数列表里不写类型, 这就意味着参数类型是Varaint。同样的,Function 第一行的最后的As Type 表明Function 返回值的类型。不写这个返回值就意味返回Variant 类型。 如果将Dim 语句写在任意Sub/Function 之外,就定义了全局变量。此时变量对 于当前模块文件的所有Sub/Function 都有效。 这时, 不同的Sub/Function 之间 就
28、能通过全局变量交流 (尽管这不是个好习惯, 因为这样就破坏了Sub/Function 的独立性, 使得复用难以进行) 。 当然, 还有 可以在不同模块之间都有效的变量 的定义方法,即将全局变量定义中的“Dim”改为“Public”。不过这种变量在 小型程序中非常少见。 也许你会问, 如果某个局部变量恰好和全局变量同名会怎么样?这时局部变量会 “遮蔽”全局变量,使你只能访问到局部变量。 下面是一个例子,大家可以来复习一下本次的内容。 Sub ShowSum() Range(“A1“).Value = CalSum(200) End Sub Function CalSum(n As Integer
29、) As Integer Dim sum As Integer sum = 0 For i = 1 To n Step 1 If i Mod 7 = 0 Then sum = sum + i 基本编程元素 10 End If Next CalSum = sum End Function 最近代码计算了1200 之间所有可以被7 整除的数字之和,并在A1 单元格内显 示结果 (所以, 请在Excel 的VBA 环境里运行此段代码) 。 里面展示了如何定义 和使用Sub,Function 以及如利用变量来存储数据。 当然, 我也用了一些没有讲 过的关键字, 如For,If 等。 但我觉得它们的 意
30、思都是非常容易懂的。 比如 For i=1 To n Step 1 就定义了一个循环,让i 从1 逐渐增长为n,每次步长是1。 所以i 依次变为1,2,3,n。 当运行到 Next 那句时就会返回到For 那一 行,将i 增量,重新执行循环体代码。而If 是判断语句,判断i 与7 取模是否 是0(整除),Mod 是关键字表示取模。而 Range(”A1)则是Excel 提供的一 个对象,意为得到名称为A1 的那个单元格区域。之后的代码就是将其值设为想 要的结果。关于对象将是下次的主题。最后值得注意的是 那 个 n 不要设的过大, 否则很容易造成sum 不足以存储过大的数据而越界(记得么,整数的
31、最大值是 32767)。 总结 本期我们讲了最基本的VB 构造元素。讲解了Sub、Function、关键字、变量定 义和类型等概念。 如果你能读到这里, 想想看你还能记得它们是怎么回事不?当 然关于这个主题,有很庞大的内容我没有讲。但往往就是这20%的内容在100% 代码里被用到,它们是最核心的内容。对于一些高级话题,如果你有兴趣的话, 可以看Programming Microsoft Visual Basic 6.0 by Francesco Balena (Microsoft Press)。下一期讲带大家进入对象的世界:) 问答 1.我是菜鸟,一般一个sub 连dim 都不用,想起什么变量
32、就上去用了。 这是不 是相当于dim as variant ? 比如 sub a() aa=1 msgbox aa end sub 真实感觉: 速度上, 损失似乎不大; 内存, 似乎一个sub 一次执行之后, 变量全 清空了,内存里的aa 也被释放了。那么对于一般的小程序来说,是不是就无所 谓了呢? 基本编程元素 11 是的, 小程序无所谓, 方便就好。 但是如果程序稍微大了些, 对象多了些, 类型 检查是一个很好的防止错误的功能。 那个时候, 写的对比写的少更重要。 其实我 觉得在目前的硬件平台上, 性能可以不做最优先考虑。 反倒是编程的便利性 (写 正确的类型后VBA 编辑器有自动提示),
33、可读性和类型错误检测让我不喜欢用 Variant。 2.能返回数值的function,以及带变量的sub。 感觉很好用,但是他们自己若 返回数值就不能单独执行了 ? 比较困扰 是的,一般情况下,Function 不能像Sub 直接被绑定到某个快捷键,某个按钮 那样执行。 必须被某个Sub 或者Function 调用 才行。 但是有一种情况, 就是Excel 的自定义函数,Function 的地位无可替代。写一个Function 后,可以像Excel 自带函数那样直接在单元格里使用。 3.VBA 的变量必须先声明再使用么? 当然不是,VBA 允许你直接使用变量而不经任何声明。 但是这样做可能会引
34、起难 以调试的错误。 比如你在第一行直接使用了变量abc, 结果到了后边由于笔误写 成了acb。 这一定会出逻辑错误, 但是解释器因为语法没问题而不会报错。 你自 己也很难在密密麻麻的代码中将这个错误挑出来。 为了强迫变量必须先声明, 再 使用,在程序的第一行加上这句: Option Explicit 这样, 如果不经声明直接使用变量就会报语法错误。 这对于比较大的程序十分必 要。 对象 12 2. 对象 当你叫一个人做什么事情的时候, 一般来讲你会如何做呢?你会说, 比如,“周 正龙,拍老虎去”。如果你想知道一个人的信息时,你大 概 会 这 么 问 ,“ 姚明, 你有多高”。 这是我们平时的
35、直观的交流方式。 对象就提供了这样一种机制, 使 得我们的编程更加符合人的思维习惯。 这样, 做基于对象的编程就很方便了。 比 如你可以用程序指令写Application.Workbooks.Add 的代码让Application 这个 对象新建一个Workbook。 对象在物理上是一段内存的区域, 维护着一组数据, 这些数据管理着实际的各种 看得见,摸得着的实体。比如应用程序Application 是一个对象,窗口Window 是一个对象,单元格是一个对象,段落是一个对象在Office 中,几乎任何 实体都可以找到对应的对象。 如果你希望操作某个实体, 那么你就操作它的对象 就好了。 这样,
36、 编程的任务被转变成了找到合适的对象, 并让这个对象做一些事 情,或者从对象上获得一些信息。 在Office 中使用的对 象模型是COM (不同于比 如CORBA, Java 或者.Net 的对象) 。 COM 是 Component Object Model 的简称, 是微软开发的组件 (Component) 标准。 如果你不理解什么是组件, 可以把软件看作是一个机器, 而组件就是机器的一个 个零件。 组件之间相互 协作, 共同完成任务。COM 组件的好处是可以跨语言。 也 就是说, 你可以用不同的编程语言来开发组件, 并用不同的编程语言来使用组件。 在Windows 下,你可以用C+,Vi
37、sual Basic,J+等语言开来发和使用组件。 当然, 我们在这里并不关心一个COM 组件是怎么开发的。 你仅仅要记住如果要使 用一个COM 组件的主要步骤是什么就可以。 首先要使用COM 组件, 就必须先注册 它。如果你足够细心就能留意到很多Windows 程序在安装的时候都有一步叫做 “注册组件”。 这就是为什么如果有的程序不安装就用不了的原因它们的组 件没安装,因此没法用,程序自然就运行不起来。Office 当然也不例外,它会 注册几个组件,这些组件分别对应不同的Office 程序,比如Word,Excel, PowerPoint,Outlook 等。“注册”的结果可以在你的注册表里
38、找到。打开 HKEY_CLASSES_ROOT,就能看到一长串的注册的组件,如下图所示。图中你能看 到Excel 的一些组件。 对象 13 每个组件定义了若干“对象类型”。 回想上一期讲解的类型就能知道任何一个变 量都有一个类型。 而对象也有类型。 每个类型可以生成不同的“对象实例”。这 就是说, 如果有一个Excel.Application 的对象类型, 我可以生成很多个此类型 的对象实例, 即开启多 个Excel (其实类似的 , 如果有Integer 这个 类型, 就可 以定义很多个不一样的Integer 变量) 。 在 下文中我会简称“对象类型”为“类 型”,而“对象实例”为“对象”。
39、 为了在VBA 中使用某个类型, 需要添加 对 包 含这 个类 型定义 的组 件的引 用 (有点 拗口是吧, 多念几遍) 。 在VBA 编辑器的工具-引用就能打开一个对话框来更改 引用,如下图所示: 对象 14 默认情况下,VBA 已经为我们自动添加了一些必要组件的引用, 所以大多是时候 我们可以完全忽略以上的步骤, 直接写代码。 但如果想用一些额外的组件, 就需 要手动添加了。 每个对象会有如下几种成员: 方法 : 表示一种动作。 这和上次讲的一般的Sub 是没什么区别的, 只不过 它代表针对当前对象的动作。比如上面提到的Workbooks.Add 中的 Workbooks 是一个对象(更确
40、切的说,是一个集合对象,下面再详述), 而Add 就是这个对象的 方法, 用于执行“新建一个Workbook”这个动作。 函数:表示一种具有返 回值的功能。这和上次讲的一般的Function 是没 什么区别的,只不过它代表针对当前对象的功能。 属性:表示对象所带有 的某种信息。比如Window 对象具有Captain 这样 的属性, 表示其标题栏的标题;Height 和Width 属性表示Window 对象的 宽和高等等。 通过改变这些值就可以改变对象所代表的实体, 比如改变其 外观。 每个属性即可能是一个对象, 也可能是一个基本类型的变量。 每个 属性都属于一个类型。 值得注意的是有两种特别
41、的属性。 第一种是“只读”的属性。 就是说你不能改变 它,而仅仅能读取它。比如单元格的Text 属性就是只读的。Excel 根据单元格 的Value 和应用于此单元格的Format 来决定Text 是什么。 所以如果非得要改的 话,需要改Value 或者Format。另一种是所谓“集合”的属性。表示当前对象 包含一组子对象。 直观上, 一个Application 包含多个Workbook,一个 Workbook 包含多个Worksheet,一个Worksheet 包含若干的Range。对象也正是这么嵌套 的。比如你想获得第一个Workbook 的第一个Worksheet 的A10 单元格需要这
42、么 写: Application.Workbooks(1).Worksheets(1).Range(“A10“) 对象 15 可以看到大多数集合对象比常规对象的名称多一个s, 表示复数 (这是一种习惯, 而不是语法要求的。 比如Range 就没有s) 。 另外取得集合中的某一个对象即可 以用对象索引值来获得 (记得索引值是从1 开 始算的, 而不是 0) , 也可以用过 对象的名称,比如如果你的第一个工作表又叫“MySheet”,那么上面的语句可 以等价的写为: Application.Workbooks(1).Worksheets(“MySheet“).Range(“A10“) 你可以注意到
43、访问对象成员的语法就是在对象名成后边加”.”。 确切的语法是: 对象名.成员名 下图是 Excel 对象结构的一个概览。 虽然总共有一百多个对象, 但是实际上经常 使用的仅仅只有 Application, Workbook, Worksheet, Range, Window, Chart, Shape 这么几个。(图是从帮助里截下来的,所以那个箭头点了不会有效果:) 对象 16 对象 17 我们可以定义一个变量来指向某个对象。 这称为“对象的引用” (注意要和组件 引用的概念区分开) 或者引用变量。 之所以用引用的方式访问对象的原因是对象 比起常规变量都大的多。 如果像普通变量那样每Dim 一
44、次就分配整个对象的内存, 非常浪费空间。 所以, 对象的引用只包含对象在内存地址而已。 当然, 我们通过 对象的引用还是能够直接操作对象。比如下面的代码: Dim a As Range, b As Range Set a = Range(“A10“) Set b = Range(“A10“) 就定义了两个对象引用a 和b, 它们指向同一个对象Range(“A10“)。 逻辑上如下 图所示: 所以,如果用 a.Value = xxx 改变了对象的值,b.Value 也能反映出变化。这和 定义两个整数变量,各自独立的语义是不一样的。 另外需要注意的是, 对对象引用进行赋值的时候需要加Set 关键字
45、, 表示传递的 是引用。 如果不加Set, 就等价于对对象的默认属性赋值。 如果a 指向Range(“A1“) 对象,那么写 a = “abc“ 等价于 a.Value = “abc“ 因为Value 是Range 对象的默认属性。 但是如果你写 a = Range(“A2“) 就会出错。所以不要忘记Set 关键字。 那么什么时候对象会释放掉内存呢?每个对象有一个内部的引用计数。 每添加一 个引用就能使引用计数增加1。如果你确认某个引用不再使用了,就写 a = Nothing 这会让引用计数减1。当对象的引用计数为0 的时候就会自动销毁释放内存。 对象 18 有人会问,100 多个对象怎么找啊
46、。 答案是帮助文档。Office 自带了庞大的开发 文档。 不过需要进入VBA 编辑器后打开的帮助才能看得到。 普通的帮助文档是找 不到这些对象的。 帮助中记录了每一个对象的每个成员的详细信息, 比如是做什 么的,是否是只读的,参数信息等。如下图所示: 所以, 如果想找一个对象, 完全没必要去录制宏那样去找。 你仅仅把需要的对象 的名字翻译成英文(工作簿-Workbook,工作表-Worksheet,)然后去查 文档。如果你对常用对象不熟悉的话,那么请等待下一期教程:) 开始编写完整的程序 19 3. 开始编写完整的程序 前几期介绍了一些基本概念。 是该拿真东西练练手的时候了。 本期的主题就是
47、拿 OfficeSoft 一个真实的问题作为例子,讲解如何开始编写一个程序。当然这个 例子也很简单: ) 这个 问题是: 如何将一组单 元格内红色的数字求和。Excel 自 己提供的函数Sum 不能对待求和数据进行条件判断, 而Sumif 只允许对待求和数 值进行数值比较上的判断, 也无法处理“字体是红色”这种格式条件。 这时正是 VBA 发挥作用的时候, 它可以将一些基本的功能组织到一起, 然后完成自定义的 任务。 开始编程之前, 应该先想清楚几个关键的问题, 也就是一个设计的过程。 编程可 不是上来就开写。对于本问题,首先想明白如何表达问题中的几个关键点: 如何表示一个单元格范围, 并以此
48、作为问题的输入?即到底要对哪些单元 格应用这个自定义的求和? 如何遍历一个个的单元格? 如何获取一个单元格的值? 对于一个单元格如何判断一个单元格的文字是红色的? 如何求和? 如何输出? 对于第一个问题。我们一开始可以用最简单的输入方法来 做 这 个 事 情 , 即 选 择 。 用户在单元格上拖动一下, 选中了一组单元格。 我们在VBA 中可以用Selection 对象来取得所有选中的单元格。 Selection 对象的类型是Range, 以后我会用“对 象名类型名”的方法来表示对象和其类型的关系,如SelectionRange。顾 名思意,Range 就表示一个单元格的范围。 值得注意的是,
49、 之前可以看到有这种 写法Range(“A1“)。这里的Range 是一个对象名。具体来讲,它等价于 ActiveSheet.Range(“A1“)(这里Excel 为我们提供了很好的快捷写法)。 也就是说类型名可以和对象名重名。 所以需要你来区分什么时候是类型, 什么时 候是对象。回到我们的问题上,SelectionRange可以表 示选中的所有单元格, 并为我所用。第一个问题解决。 第二个问题。 关于遍历一个像Selection 这样的集合,VBA 提供了一个很方便的 语法,叫做For Each Next。其语法为: For Each In Do Something with Next 使用的时候, 将和替换成实际的变量和对象即可。 每次 这个变量都会指向Collection 的一个元素。对于Selection,其类型是Range, 那么其包含的元素是什么类型呢?答案是, 也是 Range, 只是这些Range 只代表