平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“VBA 编程基础”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
11.3.1 了解 Visual Basic 语法本节解释最常用的语法元素。
11.3.1.1 Activate 方法的语法
语法:object.Activate
理解这一步时,在 Activate 方法的语法里,object 是一个所提供信息的占位符,在此例中的代码会得到一个对象。比如,下面的过程会在活动的文档中激活第二个窗口。
Sub MakeActive()
Windows(2).Activate
End Sub
11.3.1.2 MsgBox 函数的语法
语法:MsgBox(prompt[, buttons] [, title] [, helpfile, context])
理解这一步时,在 MsgBox 函数的语法里,括号内的参数是此函数的命名参数。方括号所包含的参数是选择性的(在Visual Basic 中不用键入方括号)。在 MsgBox 函数中,唯一必须提供的参数(prompt)是做为提示的文本。
落到代码里,在代码中能够借助位置或名称来指定函数与方法的参数。若借助位置来指定参数,则必须根据语法中的顺序,借助逗号来分隔每一个参数,比如:
MsgBox "Your answer is correct!",0,"Answer Box"
从实现思路看,若以名称来指定参数,则须采用参数名称或跟着冒号与等号(:=),最后再加上参数值。能够任何的顺序来指定命名参数,比如:
MsgBox Title:="Answer Box", Prompt:="Your answer is correct!"
在这个场景下,函数以及某些方法的语法会借助圆括号将参数封闭起来。这些函数和方法会得到值,所以必须用圆括号将参数封闭起来,才能够赋值给变量。如果忽略得到值或是没有传递所有的参数,则能够不用圆括号。方法若不得到值,则不用将参数用圆括号封闭起来。上述准则不管是采用命名参数或位置参数都适用。
实际处理时,在下面的示例里,MsgBox 函数的得到值是一个号码,它被存储在变量 myVar 中,以用来指示选择的按钮。因为需用到得到值,所以调用时必须采用圆括号。而另一个消息框则是用来显示变量的值。
Sub Question()
myVar = MsgBox(Prompt:="I enjoy my job.", _
Title:="Answer Box", Buttons:="4")
MsgBox myVar
End Sub
11.3.1.3 选项语句的语法
语法:Option Compare {Binary | Text | Database}
在这个场景下,在 Option Compare 语句的语法里,大括号和垂直线指示三项中的强制性选择(在Visual Basic 的语句中不用键入大括号)。比如,下列的语句指出在模块中,字符串的比较是根据文本的排序顺序而不区分大小写。
Option Compare Text
11.3.1.4 Dim 语句的语法
语法: Dim varname[ ([subscripts]) ] [As type] [, varname[([subscripts])] [As type]] . . .
从实现思路看,在 Dim 语句的语法里,Dim 是必备的关键字。而唯一必备的元素是 varname(变量名)。比如,下列的语句新建三个变量:myVar、 nextVar 和 thirdVar。它们会自动被声明成 Variant 变量。
Dim myVar, nextVar, thirdVar
结合项目来看,下面的示例声明了一个String 型的变量。它包含了数据类型,如此能够节省内存同时且可帮助从代码中找出错误。
Dim myAnswer As String
理解这一步时,若在一个语句中声明好几个变量,则必须包含每一个变量的数据类型。变量在声明时若少了数据类型,则会自动地声明为Variant。
Dim x As Integer, y As Integer, z As Integer
在这个场景下,在下列的语句里,x 与 y 都被指定成 Variant 数据类型,只有 z 被指定成 String 数据类型。
Dim x, y As Integer,Z As String
实际处理时,若声明一个数组变量,则必须包含圆括号,但下标则是可选的。下列的语句中定义了一个动态数组 myArray。
Dim myArray()
11.3.2 有效地借助数据语法
表11-1 列出VBA 所兼容的数据类型,以及存储空间的大小与范围。
注意:
落到代码里,任何数据类型的数组都需 20 个字节的内存空间,加上每一数组维数占 4 个字节,再加上数据本身所占用的空间。数据所占用的内存空间能够用数据元数目乘上每个元素的大小加以计算。比如,以 4 个 2 字节的Integer 数据元所组成的一维数组中的数据,占 8个字节。这 8 个字节加上额外的 24 个字节,使得这个数组所需总内存空间为 32 个字节。包含一数组的 Variant 比单独的一个数组需多 12 个字节。
注意:
采用 StrConv 函数把字符串数据从一种类型转换为另一种类型。
从实现思路看,除非有其他的指定,否则未声明变量会被指定成 Variant 数据类型。这个数据类型可使写程序变得较容易,但它同时不总是采用中最有效率的数据类型。
若有下列情形时必须考虑采用其他的数据类型:
程序很大并且采用很多变量。
程序的执行速度必须尽可能得快。
将数据直接写到随机存储文件中。
实际处理时,除了 Variant 之外,兼容的数据类型包括 Byte、Boolean、 Integer、Long、Single、Double、Currency、Decimal、Date、Object 以及 String。可采用 Dim 语句去声明一个指定类型的变量,比如:
Dim X As Integer
在这个场景下,上述语句声明变量 X 是一个整型,其范围介于-32,768~32,767 之间。如果试着去设置超出此范围的数值给 X,则会有错误发生。如果试着去指定一个分数给 X,则此分数会自动四舍五入。比如:
从实现思路看,X = 32768 ' 产生错误。 X = 5.9 ' 设置 x = 6。
11.3.3 Visual Basic 的命名规则
实际处理时,当在 Visual Basic 的模块中为过程、常数、变量以及参数命名时,能够采用下列的规则:
第一个字符必须采用英文字母。
在这个场景下,不能在名称中采用空格、句点(.)、惊叹号(!)、或 @、&、 $,# 等字符。
名称的长度不能够超过 255 个字符。
实际处理时,通常,采用的名称不能与 Visual Basic 本身的 Function 过程、语句以及方法的名称相同。必须退出采用与程序语言的关键字相同的名称。若所采用的内在语言函数,语句或方法与所指定的名称相冲突,则必须显示地识别它。常规会将内建函数,语句或方法的名称之前加上关联的类型库的名称。比如,如果有一个名为 Left 的变量,则只能用 VBA.Left 来调用 Left 函数。
从实现思路看,不能在范围的相同层次中采用重复的名称。比如,不能在同一过程中声明两个命名为 age 的变量。然而,能够在同一模块中声明一个命名为 age 的私有变量和命名为 age 的过程级别的变量。
注意:
在这个场景下,Visual Basic 不区分大小写,但它会在名称被声明的语句处保留大写。
11.3.4 声明变量、常数和数组
11.3.4.1 声明变量
理解这一步时,通常会采用 Dim 语句来声明变量。一个声明语句能够放到过程中以新建属于过程的级别的变量。或在声明部分可将它放到模块顶部,以新建属于模块级别的变量。
理解这一步时,下面的示例新建了变量 strName 同时且指定为 String 数据类型。 Dim strName As String
理解这一步时,若该语句出现在过程中,则变量 strName 只能够在此过程中被采用。如果该语句出现在模块的声明部分,则变量 strName 能够被此模块中所有的过程所采用,但是不能被同一工程中不同的模块所含过程来采用。为了使变量可被工程中所有的过程所采用,则在变量前加上 Public 语句,如以下的示例:
Public strName As String
在这个场景下,变量能够声明成下列数据类型中的一种:Boolean、Byte、 Integer、Long、Currency、Single、Double、Date、String(变长字符串)、String * length (定长字符串)、Object 或 Variant。如果未指定数据类型,则 Variant 数据类型被赋予默认。也能够采用 Type 语句来新建用户自定义类型。
在这个场景下,能够在一个语句中声明几个变量。而为了指定数据类型,必须将每一个变量的数据类型包含进来。在下面的语句中,变量 intX、 intY、与 intZ 被声明为 Integer 类型。
Dim intX As Integer, intY As Integer, intZ As Integer
在这个场景下,在下面的语句里,变量 intX 与 intY 被声明为 Variant 类型;只有intZ 被声明为 Integer 类型。
Dim intX, intY, intZ As Integer
实际处理时,在声明语句里,不一定要提供变量的数据类型。若省略了数据类型,则会将变量设成 Variant 类型。
从实现思路看,采用 Public 语句:能够采用 Public 语句去声明公共模块级别变量。
Public strName As String
从实现思路看,公有变量可用来工程中的任何过程。如果公有变量是声明于标准模块或是类模块中,则它也能够被任何引用到此公有变量所属工程的工程中采用。
落到代码里,采用 Private 语句:能够采用 Private 语句去声明私有的模块级别变量。
Private MyName As String
私有变量只可采用来同一模块中的过程。
注意:
结合项目来看,在模块级别中采用 Dim 语句与采用 Private 语句是相同的。不过采用 Private 语句能够更容易地读取和解释代码。
采用Static 语句:当采用 Static 语句取代 Dim 语句时,所声明的变量在调用时仍保留它原先的值。
实际处理时,采用Option Explicit 语句:在 Visual Basic 中能够轻松地借助一个赋值语句来隐含声明变量。所有隐含声明变量都为 Variant 类型,而Variant 类型变量比大多数其他类型的变量需更多的内存资源。如果显式地声明变量为指定的数据类型,则应用程序将更有效。显式声明所有变量减少了命名冲突以及拼写错误的发生率。
落到代码里,若不想使Visual Basic 生成隐含声明,能够将Option Explicit 语句放置于模块中所有的过程之前。这一个语句要求对模块中所有的变量做显式地声明。如果模块包含Option Explicit 语句,则当 Visual Basic 遇到一个先前未定义的变量或拼写错误,它会发生编译时间的错误。
落到代码里,能够设置 Visual Basic 程序环境中的某个选项,使得自动在所有新的模块中包含 Option Explicit 语句。
注意: 需显式地声明固定大小的数组与动态数组。
结合项目来看,为自动化声明一个对象变量:当采用一个应用程序去控制另外一个应用程序的对象时,应该设置一个对于其他应用程序的类型库的引用。若设置一个引用,则能够根据它们最常指定的类型来声明对象变量。比如,如果是在 Microsoft Word 中,当对 Microsoft Excel 类型库做一引用设置时,能够在 Microsoft Word 中声明 Worksheet 类型的变量来表示 Microsoft Excel 中的Worksheet 对象。
在这个场景下,如果采用其他的应用程序去控制 Microsoft Access 对象,在多数情况下,能够根据它们最常指定的类型来声明对象变量。也能够采用关键字 New 去自动生成一个对象的新实例。然而,可能要指示它是 Microsoft Access 对象。比如,当在 Microsoft Visual Basic 里面声明一个对象变量去表示 Microsoft Access form 时,必须区别它是 Microsoft Access Form 对象或是 Visual Basic Form 对象。所以在声明变量的语句中必须要包含类型库的名称,如下面示例所示:
Dim frmOrders As New Access.Form
结合项目来看,某些应用程序同时不能识别特别的 Microsoft Access 对象类型。既使已经在这些应用程序中设置了一个对 Microsoft Access 类型库的引用,必须声明所有 Microsoft Access 对象变量为 Object 类型。不能采用 New 关键字去新建这个对象的新实例。下面的示例显示了不能识别 Microsoft Access 对象类型的应用程序,如何去声明一个变量用来表示 Microsoft Access Application 对象。然后应用程序新建一个 Application 对象的实例。
Dim appAccess As Object
Set appAccess = CreateObject("Access.Application")
11.3.4.2 声明常数
理解这一步时,若要声明一个常数,能够采用 Const 语句去声明,同时且设置它的值;而在常数声明完之后,则不能加以更改或赋予新值。
在这个场景下,在声明部分,能够在过程中或者在模块顶部声明常数。模块级别中的常数默认为私有的。若要声明一个公共模块级别常数,则能够在 Const 语句前加上 Public 这个关键字。也能够借助在 Const 语句前加上 Public 关键字来明确地声明一个私有的常数,使得我们可更容易地去读取同时且解释代码。
从实现思路看,下面的示例,声明了一个 Public 常数 conAge 为 Integer 类型,同时且指定它的值 为34。
Public Const conAge As Integer = 34
落到代码里,常数能够声明成下列数据类型中的一种:Boolean、Byte、 Integer、Long、Currency、Single、Double、Date、String 或 Variant。因为已经知道常数的值,所以能够指定 Const 语句中的数据类型。关于数据类型的详细信息。
从实现思路看,能够在一个语句中声明数个常数。为了指定数据类型,必须将每一个常数的数据类型包含进来。在下面的语句中,常数 conAge 和 conWage 被声明为 Integer 类型。
Const conAge As Integer = 34, conWage As Currency = 35000
11.3.4.3 声明数组
从实现思路看,数组的声明方式和其他的变量是一样的,它能够采用 Dim、 Static、Private 或 Public 语句来声明。标量变量(非数组)与数组变量的不同在于通常必须指定数组的大小。若数组的大小被指定的话,则它是个固定大小数组。若程序运行时数组的大小能够被改变,则它是个动态数组。
理解这一步时,数组是否从 0 或 1 索引是根据 Option Base 语句的设置。如果 Option Base 没有指定为1,则数组索引从0 开始。
1.声明固定大小的数组
实际处理时,下面这行代码声明了一个固定大小的数组,它是个 11 行乘以 11 列的Integer 数组:
Dim MyArray(10, 10) As Integer
第一个参数代表的是行;而第二个参数代表的是列。
实际处理时,与其他变量的声明一样,除非指定一个数据类型给数组,否则声明数组中元素的数据类型为 Variant。数组中每个数组的数字型 Variant 元素占用 16 个字节。每个字符串型 Variant 元素占用 22 个字节。为了尽可能使写的代码简洁明了,则要明确声明的数组为某一种数据类型而非 Variant。下面的这几行代码比较了几个不同数组的大小:
' 整型数组采用 22 个字节(11 元素* 2 字节)。
ReDim MyIntegerArray(10) As Integer
' 双精度数组采用 88 个字节(11 元素 * 8 字节)。
ReDim MyDoubleArray(10) As Double
' 变体型数组至少采用 176 字节(11 元素 * 16 字节)。
ReDim MyVariantArray(10)
理解这一步时,' 整型数组采用 100 * 100 * 2 字节(20,000 字节)。
ReDim MyIntegerArray (99, 99) As Integer
结合项目来看,' 双精度数组采用 100 * 100 * 8 字节(80,000 字节)。
ReDim MyDoubleArray (99, 99) As Double
从实现思路看,' 变体型数组至少采用 160,000 字节(100 * 100 * 16 字节)。
ReDim MyVariantArray(99, 99)
理解这一步时,数组变量的最大值,是以所用的操作系统有多少可用内存为基础。若采用的数组大小超过了系统中可用内存总数的话,则速度会变慢,因为必须从磁盘中读写回数据。
2.声明动态数组
在这个场景下,若声明为动态数组,则能够在执行代码时改变数组大小。能够借助 Static、Dim、Private 或 Public 语句来声明数组,同时使括号内为空,如下所示示例所示。
Dim sngArray() As Single
注意:
理解这一步时,能够在过程中采用 ReDim 语句来做隐含性的数组声明。当采用 ReDim 语句时要小心点,不要拼错数组的名称。否则即使在模块中有包含 Option Explicit 语句,仍然会所以而生成第二个数组。
结合项目来看,对于过程中的数组范围,能够采用 ReDim 语句去改变它的维数,去定义元素的数目以及每个维数的底层绑定。每当需时,能够采用 ReDim 语句去更改动态数组。然而当做这个动作时,数组中存在的值会丢失。若要保存数组中原先的值,则能够采用 ReDim Preserve 语句来扩充数组。比如,下列的语句将 varArray 数组扩充了10 个元素,而原本数组中的当前值同时没有消失掉。
ReDim Preserve varArray(UBound(varArray) + 10)
注意:
理解这一步时,对动态数组采用 Preserve 关键字时,只能够改变最后维数的上层绑定,而不能改变维数的数目。
11.3.5 过程及其调用
11.3.5.1 Function 过程
实际处理时,Function 过程是一系列由 Function 和 End Function 语句所包含起来的 Visual Basic 语句。Function 过程和 Sub 过程很类似,但函数能够得到一个值。Function 过程可经由调用者过程借助传递参数,比如常数、变量、或是表达式等来调用它。如果一个 Function 过程没有参数,它的 Function 语句必须包含一个空的圆括号。函数会在过程的一个或多个语句中指定一个值给函数名称来得到值。
落到代码里,在下面的示例里,Celsius 函数会根据华氏温度来计算摄氏温度。当 Main 过程调用此函数时,会有一包含参数值的变量传递给此函数。而计算的结果会得到到调用的过程,同时且显示在一个消息框中。
Sub Main()
temp = Application.InputBox(Prompt:= _
"Please enter the temperature in degrees F.",
Type:=1)
MsgBox "The temperature is " & Celsius(temp) & "
degrees C."
End Sub
Function Celsius(fDegrees)
Celsius = (fDegrees - 32) * 5 / 9
End Function
11.3.5.2 Sub 过程
实际处理时,Sub 过程是一系列由 Sub 和 End Sub 语句所包含起来的 Visual Basic 语句,它们会执行动作却不能得到一个值。Sub 过程可有参数,比如常数、变量、或是表达式等来调用它。如果一个 Sub 过程没有参数,则它的 Sub 语句必须包含一个空的圆括号。
下面 Sub 过程中,每一行都有注释来解释它的作用:
'声明过程命名为 GetInfo
'该 Sub 过程没有参数
Sub GetInfo()
'声明字符串变量命名为 answer
Dim answer As String
'指定 InputBox 函数的得到值给 answer
answer = InputBox(Prompt:="What is your name?")
'条件 If...Then...Else 语句
If answer = Empty Then
'调用 MsgBox 函数
MsgBox Prompt:="You did not enter a name."
Else
'MsgBox 函数与 answer 变量连接。
MsgBox Prompt:="Your name is " & answer
'结束 If...Then...Else 语句
End If
'结束 Sub 过程
End Sub
11.3.5.3 属性过程
在这个场景下,属性过程是一系列的 Visual Basic 语句,它允许程序员去新建同时操作自定义的属性。属性过程能够用来为窗体,标准模块,以及类模块新建只读属性。可被用来在代码中代替 Public 变量,当设置属性值时上述动作应被执行。
理解这一步时,与 Public 变量不同,在对象浏览器中属性过程会有一些帮助字符串指定给自定义的属性。
从实现思路看,新建一个属性过程时,它会变成此过程所包含的模块的一个属性。Visual Basic 提供下列三种类型的 Property 过程:
Property Let 用来设置属性值的过程。
Property Get 用来得到属性值的过程。
Property Set 用来设置对对象引用的过程。
声明属性过程的语法如下所示所示:
[Public | Private] [Static] Property {Get | Let | Set}
propertyname_ [(arguments)] [As type]
statements
End Property
实际处理时,属性过程通常是成对采用的:Property Let 与 Property Get 一组,而 Property Set 与 Property Get 一组。单独声明一个 Property Get 过程就像声明只读属性。三个 Property 过程一起采用时,只有对 Variant 变量有用,因为只有 Variant 才能包含一个对象或其他数据类型的信息。Property Set 本意是采用在对象上;而 Property Let 则不是。
在 Property 过程声明中所需的参数如下所示:
Property Get Property Get propname(1, ..., n) As type
Property Let Property Let propname(1, ..., n, n+1)
Property Set Property Set propname(1, ..., n, n+1)
落到代码里,在具有相同名称属性过程里,从第一个到最后一个参数(1, ..., n)都必须共享相同的名称与数据类型。
在这个场景下,Property Get 过程声明时所需的参数比相关的 Property Let 以及 Property Set 声明少一个。Property Get 过程的数据类型必须与相关的 Property Let 以及 Property Set 声明中的最后 (n+1)个参数的类型相同。比如,如果声明下列的 Property Let 过程,则 Property Get 声明所采用参数的名称与数据类型必须同 Property Let 过程中所用的一样。
Property Let Names(intX As Integer, intY As Integer,
varZ As Variant)
' 执行语句.
End Property
Property Get Names(intX As Integer, intY As Integer)
As Variant
' 执行语句.
End Property
落到代码里,在 Property Set 声明里,最后一个参数的数据类型必须是对象类型或是 Variant。
11.3.5.4 调用 Sub 以及 Function 过程
在这个场景下,从其他过程调用一个 Sub 过程时,必须键入过程名称以及任何需的参数值。而 Call 语句同时不需,不过若采用它,则任何参数必须以括号括起来。
实际处理时,能够采用 Sub 过程去组织其他的过程,因此能够较容易地了解同时调试它们。在下面的示例中,Sub 过程 Main 传递参数值 56 去调用 Sub 过程 MultiBeep。运行 MultiBeep 后,控件得到 Main,然后 Main 调用 Sub 过程 Message。Message 显示一个信息框;当按“确定”键时,控件会得到 Main,接着 Main 退出执行。
Sub Main()
MultiBeep 56
Message
End Sub
Sub MultiBeep(numbeeps)
For counter = 1 To numbeeps
Beep
Next counter
End Sub
Sub Message()
MsgBox "Time to take a break!"
End Sub
1.调用具有多个参数的 Sub 过程
从实现思路看,下面的示例展示了调用具有多个参数的 Sub 过程的两种不同方法。当第二次调用 HouseCalc 时,因为采用 Call 语句所以需借助括号将参数括起来。
Sub Main()
HouseCalc 99800, 43100
Call HouseCalc(380950, 49500)
End Sub
Sub HouseCalc(price As Single, wage As Single)
If 2.5 * wage <= 0.8 * price Then
MsgBox "You cannot afford this house."
Else
MsgBox "This house is affordable."
End If
End Sub
在调用 Function 过程时采用括号。
理解这一步时,为便于采用函数的得到值,必须指定函数给变量,并且用括号将参数封闭起来;如下所示示例所示:
Answer3 = MsgBox("Are you happy with your salary?", 4, "Question 3")
在这个场景下,若不在意函数的得到值,能够用调用 Sub 过程的方式来调用函数。如下面示例所示,能够省略括号,列出参数同时且不要将函数指定给变量:
MsgBox "Task Completed!", 0, "Task Box"
注意在上述例子中若包含括号,则语句会导致一个语法错误。
2.传递命名参数
结合项目来看,Sub 或 Function 过程中的语句能够借助命名参数来传递值给被调用的过程。参数有两种传递方式:按值来传递和按地址来传递。按值传递只是传递参数的一个副本,在函数内建立一个同参数类型及内容一样的变量,在过程体内部参数对参数所作的改变不会影响到实际参数;按地址传递则不然,它传递的是实际参数的地址,过程中所有对参数的改变都将影响到实际参数”。命名参数的组成是由参数名称紧接着冒号(:=)以及等号,随后指定一个值给参数。
下面的示例采用命名参数来调用不具得到值的 MsgBox 函数。
MsgBox Title:="Task Box", Prompt:="Task Completed!"
理解这一步时,下面的示例采用命名参数调用 MsgBox 函数。将得到值指定给变量 answer3。
answer3 = MsgBox(Title:="Question 3", _
Prompt:="Are you happy with your salary?", Buttons:=4)
11.3.5.5 调用属性过程
表11-2 列出了调用属性过程的语法。
理解这一步时,当调用一个 Property Let 或 Property Set 过程时,总是会有一个参数出现在等号(=)的右边。
实际处理时,当用多个参数声明一个 Property Let 或 Property Set 过程时, Visual Basic 传递调用的右边参数,给 Property Let 或 PropertySet 声明中的最后一个参数。比如,图11-18 显示了 Property 过程调用中的参数与 Property Let 声明中的参数的关系:
实际上,将属性过程与多个参数合用只有在新建属性的数组时才会采用。
- VBA工程加密PJ方式(两种)
- VBA数组用法案例详解
- VBA中的Timer函数用法
- 关于vba代码运行时错误1004 应用程序定义或对象定义错误问题