学习笔记

2.Excel VBA 基本语法

1.VBA里的数据类型

面对不同类型的数据,你得告诉Excel,应该选择哪种类型的容器来保存他们。

面对不同类型的数据,在编程时,应先告诉程序按什么类型来保存或处理它

VBA里的数据类型有:字节型(Byte),整数型(Integer),长整数型(Long),单精度浮点型(Single),双精度浮点型(Double),货币型(Currency),小数型(Decimal),子字符串型(String),日期型(Date),布尔型(Boolean)等

数据类型存储空间(字节)范围描述
Byte1保存0~255的整数
Boolean2保存逻辑判断的结果:True或False
Integer2保存-2768~32767的整数
Long4保存 -2,147,483,648 到 2,147,483,647 的整数
Single4用于存储单精度浮点数范围: -3.402823E38 到 -1.401298E-45(负数),和 1.401298E-45 到 3.402823E38(正数)
Double8用于存储双精度浮点数范围: -1.79769313486232E308 到 -4.94065645841247E-324(负数),和 4.94065645841247E-324 到 1.79769313486232E308(正数)单精度浮点数与双精度浮点数的区别主要是存储空间和范围
Currency8用于存储货币值,具有固定的小数点-922,337,203,685,477.5808 到 922,337,203,685,477.5807
Decimal14在Excel VBA中,Decimal并不是一个独立的数据类型,而是通过 Variant 类型来实现的。Decimal 类型通常用于高精度计算,比如财务数据。虽然你不能直接声明一个 Decimal 类型的变量,但你可以使用 Variant 类型并将其设置为 Decimal。
Date8用于存储日期和时间范围: 1/1/100 到 12/31/9999
String(变长)10字节加字符串长度用于存储文本,范围:0到大约20亿个字符变长字符串是默认的String类型,可以根据需要自动调整长度,适合存储不确定长度的文本数据
String(定长)字符串长度定长字符串具有固定的长度,适合存储固定长度的文本数据在声明时,必须指定字符串的长度
Object4对象变量,用来引用对象
Variant(变体型)用于存储任何类型的数据,如果未明确声明数据类型,默认为Variant
用户自定义每个元素的范围与它本身的数据类型的范围相同

2.存储数据的容器,常量和变量

常量和变量

常量和变量是VBA存储数据的两种容器,无论存储什么类型的数据,变量都可以更换内容,重复使用,而常量不可以。

变量常量
值的可变性可以在程序执行过程中改变在程序执行过程中保持不变
声明使用Dim关键字声明Dim 变量名 As 数据类型使用Const关键字声明
赋值可以在声明后赋值和重新赋值必须在声明时赋值,且不能更改
用途用于存储在数据运行期间可能发生变化的数据用于存储在程序运行期间不会改变的重要值或配置

声明变量

声明变量就是指定变量的名称和可以存储的数据类型,可以用语句:Dim 变量名 As 数据类型

变量名必须以字母(或汉字)开头,不能包含空格、句号、感叹号、@、&、$和#,最长不能超过255个字符(1个汉字记2个字符)

声明为String(变长)的变量,最长可以存储约20亿个字符,如果要声明定长的String变量,就在声明时指定它可以存储的数据的长度,如【Dim str As String*10】String与10之间以*号连接,字符串的长度被固定为10个字符串(如果赋值的字符串长度小于10,VBA会用空格在末尾进行填充,如果多余10,会自动进行截断)

指定变量的数据类型后,该变量只能存储指定类型的数据

变量类型声明符

变量类型声明符是一种用于在变量名中明确指定变量类型的特殊字符,

这些字符可以使得你在声明变量时简洁地指定其类型,而无需使用Dim语句中的As关键字来确定其类型

声明符数据类型示例
%IntegerDim i%
&LongDim l&
!SingleDim s!
#DoubleDim d#
$StringDim str$
@CurrencyDim cur@
声明多个变量

声明多个变量,可以写在同一个Dim后面,变量名之间用逗号【,】隔开,每个变量都要指定数据类型,默认为Variant类型

Dim str As String,nu As Integer

如果不指定变量类型,默认将该变量声明为Variant类型

Dim Str

Variant是一种特殊的数据类型,可以存储任何类型的值,方便的同时,也可能导致性能和转换错误

强制声明所有变量

如果你担心编程时忘记声明变量,可以设置强制声明变量

方法一:在模块的第一句手动输入代码:【Option Explicit】

【Option Explicit】应该放在代码的首行,在任何过程(如Sub或Function)之前,这将强制要求在该模块中声明所有变量

设置了强制声明变量,如果执行的程序中有未声明的变量,按“运行”后程序不会运行,而且计算机会自动提醒你未声明变量

可以通过 菜单栏——工具——选项,勾选【要求变量声明】,这样VBA会在每个模块的第一行自动写下【Option Explicit】

其他变量声明类型

如果使用Public语句声明变量,变量将被声明为公共变量:

Public 变量名 As 数据类型

如果使用Private声明变量,变量将变为私有变量:

Private 变量名 As 数据类型

如果使用Static声明变量,变量将被声明为静态变量,在这个代码运行期间都会保留该变量的值

Static 变量名 As 数据类型

通过Dim声明的变量是局部变量,过程或函数结束后会销毁

Public声明的变量是全局变量,项目中所有模块都将可以访问,应用程序运行期间保持其值

Private声明的变量是模块级变量,只能在声明它的模块中访问,模块存在期间保持其值

Static声明的变量是局部变量,过程或函数结束后保持其值,应用程序运行期间不销毁

注意:公共变量必须在模块对象中声明,在工作表、窗体等对象中,即使使用Public语句声明变量,该变量也只是模块级变量

给变量赋值

把数据存储到变量里,称为给变量赋值

把日期、文本、数值等数据类型变量赋值,语句为:【Let 变量名称=数据】,[Let]可以省略

给变量赋值后,当使用这个数据时,可以直接使用变量名称代替对应的数据

Option Explicit
Dim a As String
Sub test()
    a = "变量"
    MsgBox a
    Range("A1").Value = a	'为A1单元格写入数据
End Sub

如果给对象变量(Object型,如单元格)赋值,语句为:【Set 变量名=对象】,“Set”必须写

Option Explicit
Dim a As String
Sub test()
    a = "变量"
    MsgBox a
    
    Dim rng As Range
    Set rng = Worksheets("Sheet1").Range("B1")	'设置变量为某个单元格对象
    rng.Value = a	#修改单元格对象的属性
End Sub
使用常量

常量通常用来存储一些固定的、不会被修改的值,如圆周率、个人所得税的税率等。

常量也需要声明,声明常量不但要指定常量的名称以及数据类型,还要在声明的同时给常量赋值,

并且赋值后的常量不能再重新赋值

【Const 变量名 As 数据类型 = 数值】

比如:Const p As Single = 3.14

常量也有作用域,同声明变量一样,在过程中使用Const语句声明的常量为本地变量,只可以在声明常量的过程里使用,如果在模块的第一个过程之前使用Const语句声明常量,该常量将被声明为模块级常量,该模块里所有的过程都可以使用它,如果想让声明的常量在所有模块中都能使用,应在模块里的第一个过程之前使用Public语句声明

模块:

模块是用来存放代码的容器。

类型:标准模块、工作表模块、工作簿模块、类模块。

过程:

过程是模块中的一段代码,用于执行特定任务。

类型:

子过程(Sub):执行任务,不返回值。

函数(Function):执行任务,返回值。

使用数组

数组也是变量,是同种类型的多个变量的集合

在VBA中,数组是一个包含多个元素的变量,这些元素可以用过索引号来访问,

数组可以是一维的(类似一个列表),也可以是多维的(类似一个表格或矩阵)

数组共享同一个名字,即数组名;

数组由多个同类型的变量组成;

数组中的元素按次序存储在数组中,通过索引号进行区分

数组也是变量

声明数组除了要指定数组名称及数据类型,还应指定数组的大小

Sub test()
    Dim numbers(5) As Integer  ' 声明一个包含6个整数的一维数组

    numbers(0) = 10
    numbers(1) = 20
    numbers(2) = 30
    numbers(3) = 40
    numbers(4) = 50
    numbers(5) = 60

MsgBox numbers(2)  ' 显示第三个元素的值,即30
End Sub

数组的索引默认从0开始,也可以指定索引,比如(1 To 5)

Dim numbers(1 To 5) As Integer  ' 声明一个包含5个元素的数组,索引为1到5

numbers(1) = 10
numbers(2) = 20
numbers(3) = 30
numbers(4) = 40
numbers(5) = 50

MsgBox numbers(1)  ' 显示第一个元素的值,即10

如果在模块的开头写【Option Base = 1】,数组的默认起始索引将从1开始,默认为0

Option Base 1  ' 设置数组的默认起始索引为 1

Sub Example()
    Dim numbers(5) As Integer  ' 声明一个包含5个元素的数组,索引为1到5

    numbers(1) = 10
    numbers(2) = 20
    numbers(3) = 30
    numbers(4) = 40
    numbers(5) = 50

    ' 显示数组元素
    Dim i As Integer
    For i = 1 To 5
        MsgBox "numbers(" & i & ") = " & numbers(i)
    Next i
End Sub

要给数组中的某个元素赋值,赋值的方法与给变量赋值相同,比如【arr(10)=”内容”】

二维数组

由多个数组组成的数组可以成为“二维数组”

声明多维数组:

' 声明一个二维数组,索引范围为(1 To 3, 1 To 4)
Dim matrix(1 To 3, 1 To 4) As Integer

' 声明一个三维数组,索引范围为(1 To 2, 1 To 3, 1 To 4)
Dim cube(1 To 2, 1 To 3, 1 To 4) As Integer

初始化和访问多维数组:

Sub Example()
    ' 初始化二维数组的元素
    Dim matrix(1 To 3, 1 To 4) As Integer
    matrix(1, 1) = 10
    matrix(1, 2) = 20
    matrix(1, 3) = 30
    matrix(1, 4) = 40
    matrix(2, 1) = 50
    matrix(2, 2) = 60
    matrix(2, 3) = 70
    matrix(2, 4) = 80
    matrix(3, 1) = 90
    matrix(3, 2) = 100
    matrix(3, 3) = 110
    matrix(3, 4) = 120

    ' 显示二维数组的元素
    Dim i As Integer
    Dim j As Integer
    For i = 1 To 3
        For j = 1 To 4
            MsgBox "matrix(" & i & "," & j & ") = " & matrix(i, j)
        Next j
    Next i
End Sub
声明动态数组

如果在声明数组时,不能确定会往这个数组里存储多少个元素,即不能预知数组的大小,可以在首次定义数组时括号内为空,写成:【Dim 数组名称()】

然后在程序中使用ReDim语句重新指定它的大小

已经定义大小的数组,同样可以使用Redim语句重新指定它的大小

A列有许多职工姓名,想把这些职工姓名存储在数组arr中,但预现并不知道A列的职工姓名有多少个,在定义数组时可以这样写:

Option Explicit
Dim a As String

Sub test()
    Dim dtsz()
    Dim arr() As String
    Dim n As Long
    '统计A列有多少个非空单元格
    n = Application.WorksheetFunction.CountA(Range("A:A"))
    ReDim arr(1 To n) As String

MsgBox n  
End Sub
其他创建数组的方式

1.使用Array函数创建数组:

使用Array函数创建数组,定义变量时,变量类型必须为Variant型

参数是一个英文逗号【,】隔开的数值列表,有几个值,数组就会有几个元素,如果没有参数,返回一个空数组

Option Explicit
Dim a As String

Sub test()
    Dim arr As Variant
    '将1到10 十个自然数赋值给数组arr
    arr = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
MsgBox "arr数组第一个元素为" & arr(0)
End Sub

2.使用Split函数创建数组

Split函数把一个文本字符串按照指定的分隔符分开,返回1个一维数组,数组的最小索引号是0

Split函数第一个参数是字符串,第二个参数是指定分隔符

无论你是否在模块开头写入Option Base 1,Split函数返回的数组的最小索引号为0

Option Explicit
Dim a As String

Sub test()
    Dim arr As Variant
   '利用split生成数组
   arr = Split("微软- 谷歌-OpenAI", "-")
MsgBox "arr数组第一个元素为" & arr(0)
End Sub

3.通过Range对象直接创建数组

如果想把一个单元格区域的值直接存储到数组里,可以直接把单元格区域的值赋给变量名

变量类型必须定义为Variant型

需要注意的是,将一个数组的值复制到另一个单元格区域,需要将单元格区域的形状设置的跟数组的形状一样大,如果指定的单元格区域只有2列或2行,那么也只会复制数组数据的前2列或前2行

Option Explicit

Sub RngArr()
Dim arr As Variant  '定义为Variant类型
arr = Range("A1:F11").Value '将一个单元格区域的值存储为数组
Range("H1:m11").Value = arr     '将变量的值赋值给另一个单元格区域
'需要注意的是,将一个数组的值复制到另一个单元格区域,需要将单元格区域的形状设置的跟数组的形状一样大,如果指定的单元格区域只有2列或2行,那么也只会复制数组数据的前2列或前2行
End Sub
数组相关函数

UBond和LBound函数

使用UBound和LBound函数可以计算数组的最大和最小索引

一个一维数组arr,想要知道它的最大索引号是多少,代码为【UBound(arr)】

想知道一维数组最小的索引号是多少,代码为【LBound(arr)】

想要知道数组有多少元素,可以使用代码【UBound(arr)-LBound(arr)+1】(数组的最大索引号-最小索引号+1)就是数组元素的个数

Option Explicit

Sub arrcount()
Dim arr(10 To 20)   '定义数组
MsgBox "数组的最大索引号是" & UBound(arr) & Chr(13) & "数组的最小索引号是" & LBound(arr) & Chr(13) & "数组元素的个数是" & UBound(arr) - LBound(arr) + 1
End Sub

如果是一个多维数组,求它的最大或最小索引号,还需指定数组的维数

Option Explicit

Sub dwsz()
Dim arr(1 To 10, 1 To 100)
MsgBox "第一维的最大索引号是" & UBound(arr, 1) & Chr(13) & "第二维的最大索引号是" & UBound(arr, 2)
End Sub

Join函数

Join函数将一个一维数组里的元素使用指定的分隔符连接成一个新的字符串

将一维数组里的元素通过指定分隔符连接成一个

Sub JoinTest()
Dim arr As Variant, txt As String
arr = Array(0, 1, 2)
txt = Join(arr, "@")
MsgBox txt  '
End Sub

将数组写入单元格区域

如想将一维数组arr里的第23个元素写入活动工作表中的A1单元格,代码为

Option Explicit

Sub JoinTest()
Dim arr As Variant
arr = Array("测试", 1, 2)
Range("A1").Value = arr(0)
End Sub

也可以将数组里的所有元素批量写入一个单元格区域:

‘将一维数组写入单元格区域,单元格区域必须在同一行,

‘如果想要按列的方式进行写入,需要使用Transpose函数进行转换

如果设置要写入的单元格区域大于数组,那多出的区域因为get不到数字会被写入#N/A

Sub ArrToRng()
Dim arr As Variant
arr = Array(110, 1, 2)
'将数组批量写入单元格
Range("A1:A9").Value = Application.WorksheetFunction.Transpose(arr)
'将一维数组写入单元格区域,单元格区域必须在同一行,
'如果想要按列的方式进行写入,需要使用Transpose函数进行转换
End Sub

3.集合、对象、属性和方法

对象是程序中用于表示和操作应用程序的特定部分的一个实体,对象可以是工作表、工作簿、单元格、范围、图表等,每个对象都有属性、方法和事件,允许你操纵和控制对象的行为和外观

对象里还可以包含其他对象

集合也是对象,是对多个同种类型对象的统称

集合(Collection)是一组相关对象的有序集合,集合提供了一种方便的方式来处理多个对象,而无需单独引用他们,集合可以包含任何类型的对象,例如工作簿、工作表、图表、形状等

一个打开的工作簿,里面有多张工作表,无论工作表的名称是什么,表里保存什么数据,它们都属于工作表集合,即:Worksheets。

VBA中怎样取到集合里的一个对象,有很多个工作簿,若干张工作表,数不清的单元格,怎么表示BOOK1工作簿中Sheet2工作表中的A2单元格

【Application.Workbooks(“Book1”.Worksheets(“Sheet2”).Range(“A2”))】

Application代表Excel程序

Workbooks代表工作簿集合,表示打开的所有工作簿

Book1表示工作簿名称

不同级别的对象之间用【.】连接

Worksheets:工作表集合,表示指定工作簿中的所有工作表

引用对象就像引用硬盘上的文件,要按从大到小的顺序逐层引用,但并不是每一次引用对象都必须严谨的从第一层开始。

如果Book1工作簿是活动工作簿(当前正在查看或操作的工作簿),前面的代码可以写为【Worksheets(“Sheet2”).Range(“A2”)】

如果Sheet2工作表是活动工作表,代码甚至还可以简写为【Range(“A2”)】

对象的属性

每个对象都有属性,对象的属性可以理解为该对象包含的内容或具有的特点

属性提供了访问和修改对象特征状态的方式,每个对象都有一组特定的属性,属性的值可以是多种类型,

某些单元格的属性返回的是另一个对象,如Sheet1工作表的Range属性,返回的是e对象(即单元格),但单元格本身也是一种对象

常见的VBA对象及其属性示例

Range对象:

Value:单元格或范围的值

Address:单元格或范围的地址

Font:单元格或范围的字体对象,该对象本身又包含多个属性,如Color、Size等

Worksheet对象:

Name:工作表名称

Cells:工作表中的所有单元格

Range:工作表中的特定范围

读取和设置Range对象的属性

Sub RangePropertiesExample()
    Dim ws As Worksheet
    Dim rng As Range
    
    ' 设置工作表对象
    Set ws = ThisWorkbook.Sheets("Sheet1")
    
    ' 设置范围对象
    Set rng = ws.Range("A1")
    
    ' 读取单元格的值
    MsgBox "The value of A1 is: " & rng.Value
    
    ' 设置单元格的值
    rng.Value = "Hello, VBA!"
    
    ' 设置单元格的字体颜色
    rng.Font.Color = RGB(255, 0, 0)  ' 红色
End Sub

对象的方法

方法是在对象上执行的某个操作

与属性不同,属性通常是个描述对象的特征或状态

方法的特点

执行操作:方法用于对对象执行某种操作或动作,例如,选择单元格、赋值内容、显示对话框等

参数传递:方法通常可以接受参数,这些参数可以影响方法的行为,例如,Range.Select方法没有参数,而Range.Copy方法可以接受一个目标范围作为参数

返回值:一些方法会返回一个值或对象,表示操作的结果

Option Explicit

' 这个子程序用于将Sheet1工作表中的A1:B1单元格范围复制到D1单元格开始的位置。
Sub CopyRange()
    ' 声明一个 Worksheet 类型的变量 ws
    Dim ws As Worksheet
    
    ' 设置 ws 为当前工作簿中的Sheet1工作表
    Set ws = ThisWorkbook.Sheets("Sheet1")
    
    ' 复制Sheet1工作表中的A1:B1单元格范围,并将其粘贴到D1单元格开始的位置
    ws.Range("A1:B1").Copy Destination:=ws.Range("D1")
End Sub

4.VBA中的运算符

程序执行的过程就是对数据进行运算的过程,不同的数据类型可以进行不同的运算,按数据运算类型的不同,VBA里的运算符主要分为:算数运算符/比较运算符/连接运算符和逻辑运算符。

算数运算符

算数运算符用于算数运算,返回值的类型为数值型

运算符名称示例代码说明
+加法运算符result = a + b将两个数值相加
减法运算符result = a – b将两个数值相减
*乘法运算符result = a * b将两个数值相乘
/浮点除法运算符result = a / b将两个数值相除,返回浮点结果
\整数除法运算符result = a \ b将两个数值相除,返回整数结果
^幂运算符result = a ^ b计算一个数值的幂
Mod求模运算符result = a Mod b计算两个数值相除的余数
+一元加法运算符result = +a表示一个正数
一元减法运算符result = -a表示一个负数或改变数值的符号

比较运算符

比较运算符用于比较运算,如比较两个数的大小,返回值为Boolean型,只能为True或False

运算符名称示例代码说明
=等于result = (a = b)判断两个值是否相等
<>不等于result = (a <> b)判断两个值是否不相等
>大于result = (a > b)判断左边的值是否大于右边的值
<小于result = (a < b)判断左边的值是否小于右边的值
>=大于等于result = (a >= b)判断左边的值是否大于或等于右边的值
<=小于等于result = (a <= b)判断左边的值是否小于或等于右边的值
Is对象比较result=(obj1 Is obj2)判断两个对象引用是否相同
Like模式匹配result=(str Like pattern)判断字符串是否与指定的模式匹配
Is运算符

Is运算符用于比较两个对象的引用,判断它们是否引用同一个对象

以下代码在比较obj1和obj3时返回False,因为Is运算符用于比较两个对象引用是否指向同一个内存位置,即是否是同一个对象,obj1和obj3是通过New Collection创建的两个不同的Collection对象,因此它们指向不同的内存位置,即使它们的内容相同,但仍然是不同的对象

Sub IsOperator()
    Dim obj1 As Object
    Dim obj2 As Object
    Dim obj3 As Object
    Dim result As Boolean
    
    Set obj1 = New Collection
    Set obj2 = obj1
    Set obj3 = New Collection
    
    ' 判断 obj1 和 obj2 是否引用同一个对象
    result = (obj1 Is obj2)
    Debug.Print "obj1 Is obj2: " & result ' 输出 True

    ' 判断 obj1 和 obj3 是否引用同一个对象
    result = (obj1 Is obj3)
    Debug.Print "obj1 Is obj3: " & result ' 输出 False
End Sub
Like运算符

Like运算符用于字符串模式匹配,判断一个字符串是否符合指定的模式。

模式可以包含通配符,例如【*】(任意字符序列)、【?】(单个任意字符)和【#】(单个数字)

Sub LikeOperator()
    Dim str As String
    Dim pattern As String
    Dim result As Boolean
    
    str = "HelloWorld"
    pattern = "H*W*"
    
    ' 判断字符串是否匹配模式
    result = (str Like pattern)
    Debug.Print "str Like pattern: " & result ' 输出 True

    pattern = "H?lloW?rld"
    result = (str Like pattern)
    Debug.Print "str Like pattern: " & result ' 输出 True

    pattern = "H#lloW#rld"
    result = (str Like pattern)
    Debug.Print "str Like pattern: " & result ' 输出 False
End Sub

判断B2单元格的考生是否姓李,代码为【Range(“B2”) Like “李*”】

案例,A列单元格都是人的姓名,检查A列单元格是否以“黄”开头,并在B列返回判断结果

Option Explicit
' 使用Option Explicit语句,要求所有变量在使用前必须显式声明,提高代码的可读性和可维护性

Sub CheckStartWithHuang()
' 定义一个名为CheckStartWithHuang的子过程

    Dim ws As Worksheet
    ' 声明一个Worksheet类型的变量ws,用于引用工作表

    Dim LastRow As Long
    ' 声明一个Long类型的变量LastRow,用于存储A列的最后一行行号

    Dim i As Long
    ' 声明一个Long类型的变量i,用于循环遍历A列的行号

    Dim cell As Range
    ' 声明一个Range类型的变量cell,用于引用A列的单元格

    '获取当前工作表
    Set ws = ThisWorkbook.Sheets("Sheet1")
    ' 将当前工作簿中名为"Sheet1"的工作表赋值给变量ws
    ' 使用Set关键字是因为ws是一个对象变量

    '获取A列的最后一行
    LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    ' 使用ws.Cells(ws.Rows.Count, "A")获取A列的最后一个单元格
    ' 使用End(xlUp)方法向上查找最后一个非空单元格
    ' 使用.Row属性获取最后一个非空单元格的行号,并将其赋值给LastRow变量

    '遍历A列的每一个单元格
    For i = 1 To LastRow
    ' 使用For循环遍历A列的每一行,从第1行到最后一行(LastRow)

        Set cell = ws.Cells(i, "A")
        ' 将当前行的A列单元格赋值给变量cell
        ' 使用Set关键字是因为cell是一个对象变量

        '判断单元格是否以黄开头
        If Left(cell.Value, 1) = "黄" Then
            ' 使用Left函数获取单元格值的第一个字符
            ' 判断第一个字符是否等于"黄"

            ws.Cells(i, "B").Value = "是"
            ' 如果以"黄"开头,在相应行的B列写入"是"

        Else
            ws.Cells(i, "B").Value = "-"
            ' 如果不以"黄"开头,在相应行的B列写入"-"

        End If
        ' 结束If语句

    Next i
    ' 结束For循环

End Sub
' 结束子过程

' 这段代码的目的是检查Sheet1工作表中A列的每个单元格是否以"黄"开头
' 如果以"黄"开头,则在相应行的B列写入"是",否则写入"-"
' 通过使用变量和循环,可以高效地遍历和判断每个单元格的值
' 代码使用了显式声明变量、对象变量赋值、字符串函数等VBA编程技术
' 整体结构清晰,易于理解和维护

连接运算符

连接运算符用来连接两个文本字符串,有【+】和【&】两种

【+】运算符可以勇总算数运算符的加运算,也可以用于文本连接运算,如果+号运算符两边的表达式都是文本字符串,则执行连接运算,如果+号运算符两边的表达式包含数值,则执行算数运算符

当使用【&】运算符时,无论运算符左右两边是何种类型 的数据,都执行连接运算

逻辑运算符

逻辑运算符用于判断逻辑运算式的真假,参与运算的数据为逻辑性数据,返回结果为Boolean型,只能作为True或False

运算符名称示例代码说明
And逻辑与result = (A And B)如果 A 和 B 都为 True,则结果为 True;否则为 False
Or逻辑或result = (A Or B)如果 A 或 B 其中之一为 True,则结果为 True;否则为 False
Not逻辑非result = Not A如果 A 为 True,则结果为 False;如果 A 为 False,则结果为 True
Xor逻辑异或result = (A Xor B)如果 A 和 B 其中之一为 True,但不同时为 True,则结果为 True;否则为 False
Eqv逻辑等价result = (A Eqv B)如果 A 和 B 的值相同(都为 True 或都为 False),则结果为 True;否则为 False
Imp逻辑蕴涵result = (A Imp B)如果 A 为 True,B 也为 True 或 A 为 False 则结果为 True;否则为 False

应该先进行什么运算

在VBA中,要先处理算数运算符,接着处理连接运算符,然后处理比较运算符,最后再处理逻辑运算符。

可以用括号来改变运算顺序。

运算符按运算的优先级由高到低的次序排列为:

括号>指数运算>(乘方)>求相反数>乘法和除法>整除(两数相除取商的整数)>求模运算(两数相除取余数)>加法和减法>字符串连接>比较运算>逻辑运算

优先级运算符名称说明
1()括号改变运算顺序,括号内的表达式优先计算
2^取幂指数运算
3– (单目)单目负号取负数
4*, /乘法, 除法乘法和除法运算
5\整除取整除
6Mod取模取余数
7+, –加法, 减法加法和减法运算
8&字符串连接字符串连接运算
9=等于等于比较
10<, <=小于, 小于等于小于和小于等于比较
11>, >=大于, 大于等于大于和大于等于比较
12<>不等于不等于比较
13Like模式匹配字符串模式匹配
14Is对象比较对象引用比较
15Not逻辑非布尔逻辑取反
16And逻辑与布尔逻辑与运算
17Or逻辑或布尔逻辑或运算
18Xor逻辑异或布尔逻辑异或运算
19Eqv逻辑等价布尔逻辑等价运算
20Imp逻辑蕴涵布尔逻辑蕴涵运算

5.内置函数

VBA中的函数

合理使用函数不但可以节省处理数据的时间,提高工作效率,还可以降低编程的难度,减少编写代码的工作量

在VBA中使用VBA内置函数与在工作表中使用工作表函数类似,比如想知道当前的系统时间就可以用Time()函数

Sub NowTime()
MsgBox Time()
End Sub

VBA中有哪些函数

VBA(Visual Basic for Applications)提供了大量内置函数,用于执行各种任务。以下是一些常用的 VBA 函数类别及其示例:

  1. 数学函数
  • Abs:返回数字的绝对值
  • Round:将数字四舍五入到指定的位数
  • Sqr:返回数字的平方根
  1. 字符串函数
  • Len:返回字符串的长度
  • Left:返回字符串的前 n 个字符
  • Right:返回字符串的后 n 个字符
  • Mid:返回字符串中指定位置的子字符串
  • Trim:删除字符串两端的空格
  1. 日期和时间函数
  • Now:返回当前日期和时间
  • Date:返回当前日期
  • Time:返回当前时间
  • DateAdd:在指定日期上添加或减去指定的时间间隔
  • DateDiff:返回两个日期之间的时间间隔
  1. 逻辑函数
  • If:根据条件返回一个值或另一个值
  • IIf:根据条件返回一个值或另一个值(内联形式)
  • Choose:根据索引值从值列表中返回一个值
  1. 文件和文件夹函数
  • Dir:返回指定目录中的文件名或文件夹名
  • FileLen:返回文件的大小(以字节为单位)
  • FileDateTime:返回文件的上次修改日期和时间
  1. 数组函数
  • LBound:返回数组的下界
  • UBound:返回数组的上界
  • Split:将字符串拆分为子字符串数组
  • Join:将数组的元素连接成一个字符串
  1. 转换函数
  • CStr:将值转换为字符串
  • CInt:将值转换为整数
  • CDbl:将值转换为双精度浮点数
  • CBool:将值转换为布尔值
  1. 工作表函数
  • Cells:返回指定单元格或单元格区域
  • Range:返回指定的单元格区域
  • Rows:返回指定行或行区域
  • Columns:返回指定列或列区域

这只是 VBA 提供的函数的一小部分。VBA 还有许多其他类别的函数,如财务函数、统计函数、文本函数等。你可以在 VBA 编辑器中使用”对象浏览器”(按 F2 键)来浏览所有可用的函数和对象,并查看它们的语法和说明。

根据你的具体需求,你可以使用这些函数来编写 VBA 代码,自动化任务,操作数据,与 Excel 交互等。

在编写代码时,忘记了某个函数的拼写,可以在代码窗口中敲入【VBA.】,系统会自动显示函数列表,更方便的方法是问AI

6.控制程序执行,VBA的基本语句结构

If…Then语句

在VBA中,If…Then语句用于根据指定的条件执行一个或多个语句,它允许你创建决策结构,根据条件的真假来执行不同的代码块

If…Then语句的基本语法如下

If 条件式 Then
  '如果条件为真,执行这里的语句

ElseIf 条件式2 Then
  '如果条件2为真,那就执行这里的语句
  'ElseIf非必须
  'ElseIf可以有多个
Else
  '如果条件为假,执行这里的语句
  '也可以不需要Else,直接End If
End If 
Sub test()
Dim score As Integer
score = 100

If score >= 90 Then
MsgBox "优秀"
ElseIf score >= 80 Then
MsgBox "良好"
ElseIf score >= 60 Then
MsgBox "及格"
Else
MsgBox "不及格"
End If
End Sub

Select Case语句

尽管使用If语句可以有效地解决多次判断的问题,当面对在3种或更多策略中做出选择时,使用Select Case语句会更适合

Select Case语句是VBA中另一种条件语句,用于根据表达式的值从多个可能的语句块中选择一个来执行。

它提供了简洁而高效的方式来处理多个条件,特别是当条件涉及相同变量或表达式的不同值时。

Select Case语句的基本语法如下:

Select Case expression
  Case value1
    '当表达式等于value1时执行的语句
  Case value2
    '当 表达式等于value2时最核心的语句
  Case Else
    '当表达式不等于任何指定值时执行的语句
End Select

其中,expression是要评估的表达式,可以是变量、常量或任何有效的VBA表达式

value1/value2等是与表达式进行比较的值,如果表达式的值与某个case语句中指定的值匹配,则执行该Case语句下的代码块,

如果表达式的值与任何指定的值都不匹配,则执行Case Else语句下的代码块(如果存在)

Sub test()

Dim grade As String
grade = "A"

Select Case grade
    Case "A"
        MsgBox "优秀"
    Case "B"
        MsgBox "良好"
    Case "C"
        MsgBox "一般"
    Case Else
    MsgBox "无法完成评估"
End Select

End Sub

Select Case语句一旦找到匹配的值后即跳出整个语句块,所以,为了减少判断的次数,在设置条件时,应尽量把最有可能发生的情况卸载前面

For…Next语句

For…Next语句是VBA中的一种循环结构,用于重复执行一组语句,直到满足特定条件为止。

For…Next语句的基本语法如下:

For counter = start to end [Step step]
  '要重复执行的语句
Next[counter]

其中:

counter是一个数值变量,用作循环计数器

start是计数器的初始值

end是计数器的最终值

step是可选的,表示每次迭代时计数器增加的值,如果省略,默认为1

步长可以是正整数们也可以是负整数,当为负整数时,循环变量的初始值必须大于终值,当为正整数时,循环变量的初始值必须小于终值,当步长为1时,Step 1 可以省略

Next语句标志着循环的结束,可以在Next后面指定计数器变量名,但这是可选的

以下是For…Next循环的工作原理:

1.将计数器变量设置为初始值start

2.检查计数器是否超过最终值end,如果是,循环结束

3.执行For…Next语句内的代码块

4.将计数器变量增加step的值(默认Wie1)

5.重复步骤2-4,直到计数器超过最终值

示例:

Sub test()

Dim i As Integer

For i = 1 To 10
    MsgBox "这是第" & i & "次迭代"
Next i
    

End Sub

你还可以使用Step关键字来指定不同的增量值,例如

Sub test()

Dim i As Integer

For i = 10 To 1 Step -1
    MsgBox "倒数" & i
Next i
    

End Sub

在这两个例子中,循环将从10开始,以-1的步长递减,直到达到1,

输出结构将是10个消息框,显示倒数10,倒数9…

Do While语句

Do While语句是VBA中的一种循环控制,用于根据条件重复执行一段代码,只要条件为真,循环就会继续执行,一旦条件为假,循环就会终止

有两种形式的Do While循环:前测试和后测试

前测试 Do While循环

在这种形式中,条件在循环体之前进行测试,如果条件为真,则执行循环体,如果条件为假,则跳过循环体

Do While condition
  '重复执行的语句
Loop

示例:

Sub test()

Dim i As Integer
i = 1

'前测试 Do While 循环
Do While i <= 5
    Debug.Print "i:" & i
    i = i + 1
Loop


End Sub
后测试 Do While循环

在这种形式中,条件杂循环体之后进行测试,无论条件是否为准,循环体至少会执行一次

Do 
  '要重复执行的语句
Loop While condition
Sub test()

Dim i As Integer
i = 1

'后测试 Do While 循环
Do
    Debug.Print "i:" & i
    i = i + 1
Loop While i <= 5

End Sub

Do Until语句

与Do While语句不同的是:Do While语句是当逻辑表达式的值为False时退出循环,而Do Until语句是当逻辑表达式的值为True时退出循环

Do Until语句在VBA中用于创建一个循环,这个循环会一直执行,直到某个条件为真时才会停止

基本语法:

Do Until condition
  '循环体代码
Loop

其中condition是一个布尔表达式,当其结果为True时,循环终止

循环体代码包含了在每次迭代中要执行的代码

示例:

Sub ExampleDoUntil()
    Dim counter As Integer
    counter = 0
    
    ' 这个循环会一直运行,直到 counter >= 10
    Do Until counter >= 10
        counter = counter + 1
        Debug.Print counter
    Loop
End Sub
Do Until另一种形式

Do Until语句也可以写成这种形式:

Do
  '循环一代码
Loop Until condition

这种语法上的差异在于,条件判断在循环体代码之后进行,这意味着不管条件是否为真,循环体代码至少会执行一次

For Each…Next语句

For Each…Next语句在VBA中用于遍历集合或数组中的每一个元素,它的基本语法如下:

For Each element In group
  '循环体代码
Next element

element是一个变量,用于表示每次迭代时的当前元素

group是一个集合或数组,包含了要遍历的所有元素

循环体代码包含了在每次迭代中要执行的代码

示例:

Sub ExampleForEach()
    Dim numbers As Variant
    numbers = Array(1, 2, 3, 4, 5)
    
    Dim number As Variant
    For Each number In numbers
        Debug.Print number
    Next number
End Sub
遍历集合

For Each…Next语句也可以用于遍历VBA中的集合对象,例如Collection或Scripting.Dictionary

Sub ExampleForEachCollection()
    Dim fruit As Collection
    Set fruit = New Collection
    
    fruit.Add "Apple"
    fruit.Add "Banana"
    fruit.Add "Cherry"
    
    Dim item As Variant
    For Each item In fruit
        Debug.Print item
    Next item
End Sub
在Excel中使用For Eacvh

在Excel VBA中,For Each…Next语句非常常用,特别是用于遍历工作表中的单元格范

Sub ExampleForEachRange()
    Dim cell As Range
    For Each cell In Range("A1:A5")
        cell.Value = cell.Value * 2
    Next cell
End Sub

当前活动工作簿中有许多工作表,但并不知道数量,如果要把所有工作表的名称按次序写入活动单元格的A列,For Each…Next是更适合的循环结构

当前活动工作簿中有许多工作表,但并不知道数量。如果要把所有工作表的名称按次序写入活动工作表的A列,For Each…Next是更适合的循环语句

Sub shtname()
    Dim sht As Worksheet, i As Integer
    '定义变量,因为是在工作表集合里循环,所以变量类型必须定义为Worksheet,即工作表类型
    i = 1
    '第一次待写入单元格在第1行,所以变量值定义为1
    For Each sht In Worksheets
        Cells(i, "A") = sht.Name
        '将工作表名称写入第i行A列的单元格
        i = i + 1
    Next sht
End Sub

使用For Each…Next循环语句时,不需要定义循环条件,如果要在一个集合或一个数组中循环时,同其他循环语句相比,For Each…Next要灵活的多(类似于Python中的for i in…,遍历一个数组,遍历结束后结束循环)

注意:当在一个数组里循环时,不能对数组元素进行赋值或重新赋值,对已经赋值的对象数组也只能修改元素的属性

将数字1至100写入A1到A100单元格

Sub test()
Dim c As Range, i As Integer
i = 1
For Each c In Range("A1:A100")
c.Value = i
i = i + 1
Next

End Sub

其他常用语句

GoTo语句,让程序转到另一条语句去执行

在VBA中,指定地点可以在目标代码所在行前加一个带冒号的字符串或不带冒号的数字座位标签,然后再GoTo后面写上标签名

Sub GoToExample()
    Dim x As Integer
    x = 5
    
    If x > 10 Then
        GoTo GreaterThanTen
    Else
        GoTo LessThanOrEqualTen
    End If

GreaterThanTen:
    MsgBox "x 大于 10"
    Exit Sub
    '如果这里没有Exit Sub,后面的LessThanOrEqualTen也会执行

LessThanOrEqualTen:
    MsgBox "x 小于或等于 10"
End Sub
Sub GoToExample()
    Dim mysum As Long, i As Integer
    i = 1
x:    mysum = mysum + i
'在VBA中,当声明一个数值类型的变量(如 Long)但不显式初始化时,它会自动被初始化为 0。
    i = i + 1
    If i <= 100 Then GoTo x
    MsgBox "1到100的自然数和是:" & mysum
End Sub

GoTo语句大多用于错误处理时,因为它会影响程序的结构,增加阅读和调试的难度,所以除非必须,应尽量避免

With语句,让代码更简单

With语句在VBA中是一个非常有用的结构,主要用于简化代码(提高可读性)并提高效率

With语句可以对同一个对象执行多个操作,而无需重复引用该对象

无法是:

With object
  .属性1=值1
  .属性2=值2
  .方法1
End With

当需要相同的对象进行多次操作时,往往会编写一些重复的代码,比如:

Sub FontSet()
Worksheets("Sheet1").Range("A1").Font.Name = "仿宋"
Worksheets("Sheet1").Range("A1").Font.Size = 20
Worksheets("Sheet1").Range("A1").Font.Bold = True
Worksheets("Sheet1").Range("A1").Font.ColorIndex = 3
End Sub

以上是一个设置A1单元格字体的程序,因为是对同一个对象的多个属性进行设置,所以4行代码的前半部分是相同的,如果你不想多次重复录入相同的代码,可以用With语句来简化输入:

Sub FontSet()
With Worksheets("Sheet1").Range("A2").Font
    .Name = "仿宋"
    .Size = 20
    .Bold = True
    .ColorIndex = 3
End With
End Sub

With语句可以嵌套使用

With Range("A1")
    .Value = "Hello"
    With .Font
        .Bold = True
        .Size = 14
    End With
    .Interior.Color = RGB(255, 0, 0)
End With

7.Sub过程,基本的程序单元

关于VBA过程

什么是VBA过程

打开工作簿,输入数据,保存工作簿,退出EXCEL程序,这是一个录入数据的过程,把这些操作写成VBA代码,按先后顺序组合起来就是一个VBA过程。

所以,VBA过程就是完成某个给定任务的代码的有序组合

VBA过程是一组按顺序执行的VBA语句,用于执行特定的任务,VBA中主要有两种类型的过程:Sub过程(子程序) 和 Function过程(函数)。

Sub过程(子程序):

  • 执行操作但不返回值
  • 可以接受参数,但不能直接返回结果
  • 使用Sub关键字定义

Function过程(函数):

  • 执行操作并返回一个值
  • 可以接受参数,必须返回一个结果
  • 使用Function关键字定义

编写Sub过程需要了解的内容

关于Sub过程

录制的宏就是SUb过程,录制宏业只能生成Sub过程

应该把过程写在哪里

和录制的宏一样,过程也是保存在模块里,所以编写过程,应先插入一个模块来保存它,插入模块后,双击激活它的【代码窗口】,就可以在代码窗口中编写过程了

并不是只有模块对象才能保存过程,Excel对象(或窗体对象)也能保存过程。

双击任意一个对象激活它的代码窗口,即可在里面编写过程

为了避免错误,建议将Sub过程和Function过程保存模块对象中,

和一个文件夹可以保存多个文件一样,一个模块也可以保存多个过程。如果需要,也可以像给文件分类那样,建不同的模块来保存功能不同的过程。

声明Sub过程,规范的语句

Private和Public用于声明过程的作用域名,同时只能选一个,如果省略,则默认为公共过程

如果选用Static,运行程序的过程中将保存该过程里声明的本地变量

Private Sub MyPrivateSubProcedure()
    ' 只能在当前模块中调用
End Sub

Public Sub MyPublicSubProcedure()
    ' 可以在任何地方调用
End Sub

当使用Static关键字时,过程中声明的本地变量在过程结束后不会释放,而是保留其值直到下次调用

Static Sub CountCalls()
    Static callCount As Integer
    callCount = callCount + 1
    Debug.Print "This procedure has been called " & callCount & " times."
End Sub
'在这个例子中,每次调用CountCalls过程,callCount都会增加,而不是每次都从0开始。

尽管一个Sub过程可以包含任意多的代码,但是为了更有效、更有条理地把任务完成,总是需要有明确的分工,当需要处理的任务比较复杂时,可以用多个小过程去完成,每个过程负责完成一个特定的、较为简单的目的,最后通过执行这些小过程来完成最终目的。

从另一个过程执行过程

方法1:输入过程名称以及参数,参数用逗号隔开

Sub 主过程()
    ' 调用不带参数的过程
    次要过程1
    
    ' 调用带参数的过程
    次要过程2 "Hello", 123
End Sub
'在VBA中,被调用的过程的代码确实可以放在调用它的过程之后。这是因为VBA使用了一种叫做"早期绑定"的机制。

Sub 次要过程1()
    MsgBox "这是次要过程1"
End Sub

Sub 次要过程2(文本 As String, 数字 As Integer)
    MsgBox "文本: " & 文本 & ", 数字: " & 数字
End Sub

方法2:在过程名称以及参数前使用Call关键字,参数用括号括起来,并用逗号隔开

Sub 主过程()
    Dim 姓名 As String
    Dim 年龄 As Integer
    
    姓名 = "张三"
    年龄 = 30
    
    ' 使用 Call 关键字调用不带参数的过程
    Call 问候()
    
    ' 使用 Call 关键字调用带参数的过程
    Call 显示信息(姓名, 年龄)
End Sub

Sub 问候()
    MsgBox "欢迎使用我们的VBA程序!"
End Sub

Sub 显示信息(名字 As String, 岁数 As Integer)
    MsgBox "姓名: " & 名字 & vbNewLine & "年龄: " & 岁数
End Sub

方法3:利用Application对象的Run方法

这种方法允许您动态地调用过程,特别适用于需要在运行时决定调用哪个过程的场景。

使用 Application.Run 的优点:

动态调用:可以在运行时决定调用哪个过程。

跨模块调用:可以轻松调用其他模块或甚至其他工作簿中的过程。

字符串参数:过程名可以作为字符串传递,允许更灵活的编程结构。

错误处理:可以轻松捕获不存在的过程调用错误。

注意事项:

性能:相比直接调用,Application.Run 稍慢一些,但在大多数情况下,这种差异是可以忽略的。

类型安全:由于是动态调用,编译器无法在编译时检查参数类型,可能导致运行时错误。

IntelliSense:在编写代码时,不会为过程名和参数提供 IntelliSense 支持。

Sub 主过程()
    ' 调用不带参数的过程
    Application.Run "问候"
    
    ' 调用带参数的过程
    Application.Run "显示信息", "李四", 25
    
    ' 动态决定调用哪个过程
    Dim 过程名称 As String
    过程名称 = IIf(Time < #12:00:00 PM#, "早上好", "下午好")
    Application.Run 过程名称
End Sub

Sub 问候()
    MsgBox "欢迎使用 Application.Run 方法!"
End Sub

Sub 显示信息(名字 As String, 岁数 As Integer)
    MsgBox "姓名: " & 名字 & vbNewLine & "年龄: " & 岁数
End Sub

Sub 早上好()
    MsgBox "早上好!"
End Sub

Sub 下午好()
    MsgBox "下午好!"
End Sub

过程的作用域

过程按作用域的不同分为公共过程和私有过程(作用域指的是一个过程(Sub或Function)可以被访问和使用的范围)

公共过程

公共过程是可以从任何其他模块或过程中访问的过程,声明时需要用到Public语句,其中Public也可以忽略

私有过程

声明私有过程必须使用Private语句,其他模块里的过程不能调用私有过程

私有过程只能在声明它的模块内部被访问和使用,它可以是一个Sub(子程序)或Function(函数)

如果想把模块中的所有过程全部声明为私有过程(包括已经声明为公共过程的的过程),只需在模块中第一个过程之前写上【Option Private Module】即可

私有过程不会在【宏】对话框中显示

8.自定义函数,Function过程

Function过程也称为函数过程,编写一个Function程序,就编写了一个函数

试写一个函数

Function过程同Sub过程一样,都是保存在模块里,所以在编写函数之前,应先插入一个模块来保存它

插入模块后,双击模块激活它的代码窗口,即可开始编写函数

可以通过 菜单栏的【插入】-【过程】-【函数】来插入函数

编写函数时,函数名不能带有数字,否则在excel单元格中调用好像会出错,函数名也不能是单个字母

过程结束前,必须将计算结果赋值给过程名称

如果想让函数生成一个1~10之间的随机整数,完整的程序为:

Public Function ttt() As Integer
ttt = Int(Rnd() * 10) + 1
End Function

使用自定义函数

自定义的函数可以在工作表中使用,也可以在VBA的过程中使用

在工作表中使用自定义函数 同 使用工作表自带的函数类似,另外在【公式】-【插入函数】中也能找到自定义函数

自定义函数也可以和其他函数嵌套使用

怎么统计指定颜色的单元格个数

在Excel里,可以通过RGB函数指定不同的颜色,如想将活动工作表中B1单元格的底纹设置为黄色,代码为

Sub SetYellowBackground()
    ' 将 B1 单元格的底纹颜色设置为黄色
    ActiveSheet.Range("B1").Interior.Color = RGB(255, 255, 0)
End Sub
'检测单元格A1的颜色是否为黄色
'如果是返回1
'否则返回0
Function CountColor()
If Range("A1").Interior.Color = RGB(255, 255, 0) Then
CountColor = 1
Else
CountColor = 0
End If
End Function

统计指定颜色的单元格个数:

要知道A1:A10里有多少个黄色单元格,可以让VBA替我们数一下,创建函数,然后在工作表中调用函数

Function CountColor()
Dim rng As Range
For Each rng In Range("A1:B10")
    If rng.Interior.Color = RGB(255, 255, 0) Then
        CountColor = CountColor + 1
    End If
Next rng
End Function

还可以通过颜色索引号来引用某个颜色,以下是索引号以及对应的颜色,属性的名字是ColorIndex:
1: 黑色

2: 白色

3: 红色

4: 绿色

5: 蓝色

6: 黄色

7: 粉红色

8: 青色

上一个案例的代码可以改成

Function CountColor()
Dim rng As Range
For Each rng In Range("A1:B10")
    If rng.Interior.ColorIndex = 6 Then
        CountColor = CountColor + 1
    End If
Next rng
End Function

ColorIndex属性引用的事某个索引号上的颜色,而Color返回的是真实颜色,因为颜色索引号可以更改,所以使用ColorIndex属性引用到的颜色不一定都相同,因此函数不一定能返回正确的结果

用参数计算指定区域

在工作表中使用函数时,可以通过函数参数指定计算统计的单元格区域,自定义函数也可以使用参数

如果需要统计的单元格区域不是固定的,额可以用变量代替程序里的A1:A10单元格区域,让用户在使用自定义函数时通过函数参数指定区域

'指定函数的参数为Range型
Function CountColor(arr As Range)
Dim rng As Range
For Each rng In arr
'设置要遍历的对象是参数设置的Range区域
    If rng.Interior.ColorIndex = 6 Then
        CountColor = CountColor + 1
    End If
Next rng
End Function

更进一步,还可以给函数设置第2个参数,通过第二个参数(或更多)指定要统计的颜色

'指定函数的参数为Range型
Function CountColor(arr As Range, c As Range)
Dim rng As Range
For Each rng In arr
'设置要遍历的对象是参数设置的Range区域
    If rng.Interior.Color = c.Interior.Color Then
        CountColor = CountColor + 1
    End If
Next rng
End Function
设置自定义函数为易失性函数

有时,当工作表重新计算之后,自定义函数并不会重新计算,但如果在函数开始添加一条语句后后,无论何时重新计算工作表,函数都会重新计算,得到新的结果

只需要在函数过程开始的第一句写上【Application.Volatile True】,就是将自定义函数声明为易失性函数,当工作表发生重算后,易失性函数会重新计算函数的值。

Function MyCustomFunction(input As Range) As Double
    Application.Volatile  ' 使函数成为波动性函数
    
    ' 函数的主要逻辑
    MyCustomFunction = WorksheetFunction.Sum(input) * 2
End Function

声明函数过程,规范的语句

[Public/Private] Function 函数名([参数列表]) [As 返回类型]
    [函数体]
    [函数名 = 返回值]
End Function

9.合理地组织程序,让给代码更美

代码排版,必不可少的习惯

缩进,让代码更优层次

缩进可以使程序更容易阅读和理解,在VBA中,过程的语句要比过程名缩进一定的字符,在IF/Select Case/For…Next/Do…Look/With语句之后也要缩进,一般缩进4个空格

但在缩进某行或某块代码时,并不用手动在代码前敲入4个空格,而是按Tab键即可

点击某行代码的任意位置(不用选中代码),按shift+tab可以取消缩进(凸出)

更改长行代码为短行代码

当一条语句过长时,可以在橘子的后面输入: 一个空格和下划线【_】,然后换行,把1行代码拆成2行(或多行)

多行代码合并为1行

在第一行代码后面加上英文冒号【:】,可以接着写第二行代码。

如非必要,并不提倡

注释,让代码的意图清晰明了

注释语句以英文单引号开头【’】,后面跟着注释的内容,可以放在代买的末尾。

当注释语句单独成一行时,还可以使用Rem代替单引号

Rem 这也是一个单行注释

在调试程序时,如果不想运行某行代码,可以在代码前加上单引号或Rem,使它成为注释语句,后期可以取消注释

如果想要批量注释多行代码,可以选中代码后,点击【视图-工具栏-编辑】,然后在抓手按钮的右边,找到一个【设置注释快】按钮,点击可将选中的代码转为注释(这个按钮的右边还有批量解除注释按钮)

1 thought on “2.Excel VBA 基本语法”

发表回复