标签:VBA,Range对象
引言:本文学习整理自powerspreadsheets.com,讲解得很细致,一些知识点反复强调,对于熟悉VBA的朋友来说,感觉有点啰嗦,但是对于VBA初学者来说,对快速掌握Range对象的引用,却很有好处。
>>>上篇文章:啰哩啰嗦地讲透VBA中引用单元格区域的18个有用方法--Range属性
使用Cells属性引用Range对象
VBA中没有Cell对象,有Worksheet.Cells属性和Range.Cells属性。可以使用Cells属性返回表示单元格的Range对象。
两个Cells属性之间的主要区别在于属性应用的对象:
1.使用Worksheet.Cells属性时,应用该属性到Worksheet对象。
2.使用Range.Cells属性时,应用该属性到Range对象。
这一点很重要,因为根据上下文的不同,属性可能返回不同的单元格。更准确地说,将Cells属性应用于Range对象时,是引用与另一个区域相关的单元格。
Cells属性的语法
Cells属性的基本语法是:
表达式.Cells(行号,列号)
其中,表达式是代表VBA对象的变量。VBA对象可以是工作表(在Worksheet.Cells属性的情形)或可以是单元格区域(Range.Cells属性的情形)。
行号和列号是行和列的编号,通常使用数字。使用此语法时,还可以使用字母来引用列,用双引号(“”)将字母括起来。除了引号(“”)(围绕字母),不需要像使用Range属性时那样使用其他引号。
Range属性和Cells属性之间的主要不同在于Cells属性接受行号和列号作为参数。
Range对象有一个称为Range.Item的属性。之所以可以在Cells关键字之后立即指定行号和列号参数,是因为Range.Item属性是Range对象的默认属性。如上所述,这也是可以使用双引号(“”)中的字母来引用该列的原因。
使用Worksheet.Cells引用单个单元格
Cells属性最基本的使用情形是引用单个单元格。
Cells属性只能(通常)用于返回1个单元格的范围,这是区分Cells与Range属性的主要特征之一。
实际上,有一种方法可以使用Cells属性来引用更大的单元格范围。然而,这涉及到组合Range属性和Cells属性。下面会介绍这种方法。
使用Cells属性引用单个单元格相对简单。例如,如果要引用Book1.xlsm的Sheet1中的单元格A1,完全限定引用与上一节所示的基本语法非常相似:
Application.Workbooks(“Book1.xlsm”).Worksheets(“Sheet1”).Cells(1,1)
Application.Workbooks(“Book1.xlsm”).Worksheets(“Sheet1”).Cells(1,”A”)
工作表Worksheet.Cells属性引用单个单元格的替代语法
下面的替代方法与上面解释的语法基本相同,也以“表达式.Cells”开头,区别在于括号内的参数。
替代的语法是:
表达式.Cells(单元格索引)
在这种特殊情况下,只有一个参数:相关单元格的索引。
那么,VBA如何确定一个单元格的索引呢?为此,每个单元格都分配了一个编号,单元格从左到右、从上到下编号。换言之:
编号从第一行开始:
- 单元格A1被分配数字编号1。
- 单元格B1被分配数字编号2。
- 单元格C1被分配数字编号3。
- 该过程将继续处理每个子序列单元格,直到…
- 单元格XFD1(第一行中的最后一个单元)被分配数字编号16384。
一旦第一行中的所有单元格都分配了一个编号,该过程将继续进行第二行:
- 单元格A2被分配数字编号16385。
- 单元格B2被分配数字编号16386。
- 单元格C2被分配数字编号16387。
- 编号分配将持续到…
- 单元格XFD2(第二行中的最后一个单元)被分配数字编号32768。
该过程继续到第三行。
然后是第四行。
依此类推,直到…
到达第1048576行。
而且…
Excel工作表中的最后一个单元格(单元格XFD1048676)的编号编号为17179869184。
下面的屏幕截图让你更好了解数字编号分配的过程:
例如,如果想使用该语法引用单元格A2,则合适的引用是:
Cells(16385)
对工作簿Book.xlsm的工作表Sheet1中单元格A2的完全限定引用是:
Application.Workbooks(“Book1.xlsm”).Worksheets(“Sheet1”).Cells(16385)
这里关于Cells属性的两个示例演示了如何将其应用于Worksheet对象。然而,也可以使用此语法将该属性应用于Range对象,这将允许相对于另一个区域引用单元格。
使用Range.Cells属性相对于单元格区域引用单个单元格
假设当前选择区域覆盖了活动工作表的单元格C3至D5(单元格C3、C4、C5、D3、D4和D5)。你已经知道这是一个Range对象。
可以使用Selection属性创建以下引用:
Selection.Cells(1,1)
这个引用返回单元格C3本身。
上述示例中出现不同行为的原因是Range属性的行为相对于应用它的对象(工作表或单元格区域)。对于单元格区域(如上面的示例),Range属性的行为与单元格区域的左上角有关。其逻辑与解释将Range属性应用于Worksheet对象或Range对象时的不同行为的逻辑相同。
使用Range.Cells属性相对于单元格区域引用单个单元格的替代语法
回顾一下替代语法:
表达式.Cells(单元格索引)
此时,单元格区域内的每个单元格都被分配一个数字编号;分配编号时执行上面描述的相同的规则,即从左到右从上到下。
例如,假设要处理从单元格A1到单元格B10的单元格区域,想要选择单元格A5。
这种情况下,引用是:
Range(“A1:B10”).Cells(9)
下面的屏幕截图显示了将单元格索引编号分配给相关区域内单元格的方式:
如果处理工作簿Book1.xlsm的工作表Sheet1的单元格区域,则完全限定引用如下:
Application.Workbooks(“Book1.xlsm”).Worksheets(“Sheet1”).Range(“A1:B10”).Cells(9)
将Cells属性的这种语法应用于Range对象的一个有趣方面是Cells属性的参数不受引用区域中的单元格数量的限制。如果使用的数字大于相关区域内的单元格数量,VBA将继续计数,就好像该范围比实际范围更高(一直延伸到底部)。换句话说:Cells属性返回的Range对象不必位于原始/源单元格区域内。
在上例中描述的单元格区域(A1:B10)的情况下,单元格索引编号继续分配,如以下屏幕截图所示:
例如,从A1至B10的单元格区域包含了20个单元格,假设对Cells属性设置参数“21”,即:
Range(“A1:B10”).Cells(21)
将返回引用A11,超出了原始引用单元格区域(A1:B10)。
使用Range和Cells属性引用单元格区域
正如上面所提到的,可以组合Range和Cells属性来引用单元格区域,最简单的方法是使用Cells属性作为Range属性的参数。其语法为:
Range(Cells(第一个单元格的行号,第一个单元格的列号),Cells(最后一个单元格的行号,最后一个单元格的列号))
例如,如果要使用Cells属性引用Book1.xlsm的Sheet1中的单元格区域包含单元格A1至B5(A1、A2、A3、A4、A5、B1、B2、B3、B4和B5),可以输入:
Range(Cells(1,1),Cells(5,2))
相应的完全限定引用如下所示:
Application.Workbooks(“Book1.xlsm”).Worksheets(“Sheet1”).Range(Cells(1,1),Cells(5,2))
当使用变量指定Cells属性的参数时,此技术非常有用。
使用Worksheet.Cells属性引用工作表中的所有单元格
这可能是实现Cells属性的最简单但也是最受限制的方法。语句:
表达式.Cells
返回相关Excel工作表中的所有单元格。
例如,下面的语句返回Book1.xlsm的工作表Sheet1中的所有单元格:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Cells
为什么应该学会使用Cells属性引用Range对象
使用Cells属性引用Range对象似乎比使用Range属性要复杂一些。但是,在某些情况下,使用Cells属性可能更合适。
Cells属性显示其有用性的最重要场景可能是使用变量而不是实际数字作为Cells属性的参数。实际上,可能经常会发现要处理这种情况(使用变量而不是硬编码的数字作为Cells属性的参数)。
当使用VBA执行某些工作时,Cells属性和将变量用作参数的功能非常有用,常见的情形就是这些工作要使用循环时。
使用Range.Offset属性引用Range对象
Range.Offset属性也返回一个Range对象。
然而,在Range.Offset属性的情形下,返回的Range对象位于从指定区域起一定数量的行和列。
换句话说,返回的Range对象由以下因素决定:
1.基准区域,将作为偏移的基准。
2.要偏移基准区域的行数。
3.要偏移基准区域的列数。
其语法如下:
表达式.Offset(偏移行,偏称列)
其中,“表达式”是表示Range对象的变量。“偏移行”是要偏移基准区域的行数,该值可以是正值(向下偏移)或负值(向上偏移)。“偏移列”是要偏移区域的列数,如果为正值,则向右偏移,为负值将向左偏移。
偏移行和偏移列参数都基于“表达式”表示的Range对象左上角单元格偏移。
Offset属性的工作方式意味着它只能应用于Range对象。换句话说,没有Worksheet.Offset属性。这意味着该属性是一个很好的替代方法,可以引用与区域相关的特定单元格,而无需使用上面介绍的更复杂的方法,即将Range或Cells属性应用于Range对象。
使用Range.Offset属性引用单个单元格
这是一个简单的示例:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1").Offset(RowOffset:=1,ColumnOffset:=1)
可忽略关键字RowOffset和ColumnOffset来简化该语句,如下:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1").Offset(1,1)
这个例子,VBA返回一个单元格,该单元格位于单元格A1下方1行和右侧1列。换句话说,基准单元格是单元格A1,从A1向下移1行向右移1列,因此引用单元格B2。
在某些情况下,可能希望创建一个相对引用,Range.Offset属性只需要移动一定数量的行或列(但不能同时移动行或列)就可以找到它应该返回的单元格。在这些情况下,可以忽略不相关的参数。
例如,下面的语句引用单元格A1下方1行的单元格,换句话说,引用单元格A2:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1").Offset(RowOffset:=1)
或:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1").Offset(1)
类似地,以下语句引用单元格B1,它是单元格A1右侧的一列:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1").Offset(ColumnOffset:=1)
或:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1").Offset(,1)
下面看看基准区域的另一种表示方式。下面的语句也引用单个单元格。但是,注意偏移的基准单元格表达方式的差异。
ActiveCell.Offset(1,1)
本例中,没有使用Range,而是使用Application.ActiveCell属性,该属性返回代表当前活动单元格的Range对象。
因此,VBA将返回一个单元格,该单元格位于当前活动单元格的下方1行,右侧1列。例如,如果当前活动单元格为A1,则上面的语句返回单元格B2。
如果Range.Offset属性用于尝试返回不存在的单元格,则会生成错误。例如,如果当前活动单元格为A1且使用以下引用,则可能会发生这种情况:
ActiveCell.Offset(-1,-1)
该语句要求VBA返回单元格A1上方1行,左侧1列的单元格,由于这样的单元格不存在,因此产生错误。
使用Range.Offset属性引用单元格区域
如果想使用Range.Offset属性引用单元格区域,可以简单地使用冒号(:)运算符来定义作为偏移基准的单元格区域,但偏移的逻辑是相同的。
例如,下面的语句:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1:B5").Offset(3,3)
处理过程如下:
1.它设置为包含单元格A1到B5(A1、A2、A3、A4、A5、B1、B2、B3、B4和B5)的基准单元格区域。
2.移动该区域,向右3个单元格,向下3个单元格。
返回从单元格D4到E8的区域(D4、D5、D6、D7、D8、E4、E5、E6、E7和E8)。
为什么应该学会使用Range.Offset属性引用Range对象
Range.Offset属性在下列情形通常最有用:
1.使用变量而不是实际的数字作为参数
2.循环过程
录制宏时使用相对引用时,宏录制器也通常使用Range.Offset属性。因此,你可能会经常遇到这种引用结构类型的宏。
使用Range.Item属性引用Range对象
在日常Excel工作中,你可能最终不会使用Range.Item属性。然而,可能仍需要不时使用此属性。此外,对Range.Item有好的理解有助于更好地使用上文介绍的Cells属性并理解其语法。
Range.Item属性返回一个对象,该对象是一个单元格区域。
在这种特殊情况下,单元格区域由特定的指定区域确定。Range.Item属性基于使用的参数访问该区域内的特定单元格。
Range.Item属性的语法为:
表达式.Item(行索引,列索引)
其中,表达式是代表Range对象的变量。注意,括号内使用的是行索引和列索引,这与Cells属性相似。
让我们详细看看行索引参数。该参数的主要特征如下:
- 它是必需的。
- 它必须是一个数字。
- 它确定在相关区域内访问的单元格。为此,有两个选项:如果仅使用行索引参数,单元格被从左至右从上至下分配数字编号;如果行索引和列索引参数都使用,行索引确定在适用区域内访问的单元格行。
这两个参数都是相对偏移量。VBA使用这些参数来确定要从最初指定的区域移出多少行和列。
与Cells属性一样,Range.Item属性也不受指定区域单元格编号的限制。
下面的示例语句返回Book1.xlsm的工作表Sheet1中单元格A1:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1:D10").Cells.Item(1)
或:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1:D10").Cells.Item(1,1)
或:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1:D10").Cells.Item(1,"A")
然而,如果要引用单元格B8,则可以使用下列语句:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1:D10").Cells.Item(30)
或:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1:D10").Cells.Item(8,2)
或:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1:D10").Cells.Item(8,"B")
Range.Item属性重要的部分是允许引用与区域相关的特定单元格。更重要的是,Item属性是Range对象的默认属性,因此,可以忽略Item关键字而只指定行索引和列索引。这样,上例可简化为:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1:D10").Cells(30)
或:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1:D10").Cells(8,2)
或:
Application.Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1:D10").Cells(8,"B")
至此,这些引用中使用的结构可能看起来很熟悉。这是因为该结构与在讲解Cells属性时描述的结构基本相同。
主要由于这个原因,可能不会经常使用Range.Item,相反,可能会使用Cells属性。
然而,现在你应该知道,如果出于目的需要,可以使用Range.Item属性,用于引用Range对象。
小结
Range对象是最重要和最常用的ExcelVBA对象之一。
不幸的是,Range对象有时会让某些用户感到困惑。其中一个主要原因是,有多种不同的方式来引用Range对象。
本文简要介绍Range对象。也许更重要的是,本文讲解了一些最常用的方法,用于创建对Range对象的合适引用。
阅读这篇文章后,你可能已经对Range对象有了很好的理解,并且不会被可用于引用它的不同备选方法所迷惑。特别是,在使用以下任何属性创建对Range对象的引用时,你现在可能非常熟练了:
- Range
- Cells
- Offset
- Item
还有其他一些方法可以引用和操作Range对象,其中包括Application.Union方法和Range.Areas属性,我们会在以后介绍。