如何避免在Excel VBA中使用Select

638

我听说使用.Select在Excel VBA中会被人们所反感,但我不知道该如何避免使用它。如果能够使用变量而不是Select函数,那么我的代码将更加可重用。然而,如果不使用Select,我不确定如何引用诸如ActiveCell等内容。

我找到了这篇关于范围的文章这个避免使用select的例子,但我找不到任何有关“如何”的信息。


23
需要注意的是,在某些情况下,完全无法避免使用 Select 和/或 ActiveSheet 等等。这里有一个我找到的例子:https://dev59.com/6mEh5IYBdhLWcg3wHAND - Rick
18
有时候会需要进行编辑幻灯片中基于 Excel 文件的图表数据,这时可能需要使用“激活”或“选择”的功能。 - brettdj
@brettdj - 这里有一个最近的例子。要将工作簿中所有工作表设置为相同的值,似乎需要使用.Select / .Selection - BruceWayne
4
似乎不是这样。 - chris neilsen
15个回答

11

在我看来,使用.select的人通常是像我一样通过录制宏并修改代码来学习VBA的人,他们没有意识到.select和随后的selection只是一个不必要的中间代理。

可以通过直接使用已经存在的对象来避免使用.select,就像许多人已经发布的那样,这允许各种间接引用,比如以复杂的方式计算i和j,然后编辑cell(i,j)等。

否则,.select本身并没有什么明显的问题,你很容易找到这个的用途。例如,我有一个电子表格,我在里面填写日期,激活执行某些神奇操作的宏,然后将其以可接受的格式导出到另一个工作表,但是这需要对相邻单元格进行一些最终的手动(不可预测)输入。这时,就需要使用.select来节省鼠标移动和点击的时间了。


2
虽然你是对的,但 select 至少有一件隐含的问题:它很慢。与宏中发生的其他所有事情相比,它确实非常慢。 - vacip

10

如何避免复制粘贴?

面对现实吧:录制宏时,这种情况经常发生:

Range("X1").Select
Selection.Copy
Range("Y9").Select
Selection.Paste

虽然这个人只想要的东西是:

Range("Y9").Value = Range("X1").Value

因此,我建议使用以下简单的方法来代替在VBA宏中使用复制粘贴:
Destination_Range.Value = Source_Range.Value

2
认为这与@Vityata的好答案相对应,部分内容为“不复制值,而是取值”:Range("B1:B6").Value = Range("A1:A6").Value。当然,上面的一些答案并没有解决OP的核心问题 :-) - T.M.
这也在接受的答案中得到了处理,在标题为“如果您正在循环遍历一系列单元格,则通常最好(更快)先将范围值复制到变体数组中,然后再对其进行循环遍历” 的部分中。 - chris neilsen

6
我注意到这些答案中没有提到 .Offset 属性。它也可以用来避免在操作某些单元格时使用 Select 操作,特别是与所选单元格有关的情况(如 OP 在 ActiveCell 中提到的)。
以下是几个示例:
我还假设 ActiveCellJ4ActiveCell.Offset(2,0).Value = 12 - 这将把距离 activecell (即 J6)两行下方的单元格的值更改为 12。 - 如果是 -2,则会将值 12 放在两行上面的 J2 中。 ActiveCell.Offset(0,1).Copy ActiveCell.Offset(,2) - 这将把右侧一列的单元格(k4)复制到距离 activecell 两列的单元格(L4)中。 - 注意,偏移参数中可以省略 0。 - 因此:ActiveCell.Offset(,2)ActiveCell.Offset(0,2) 是相同的。 - 与前面的示例类似,-1 将使单元格向左移动一列(i4)。
这并不意味着它们比上面的选项更好,但肯定比使用 select 好。请注意,在工作表中应避免使用 EXCEL 函数 Offset,因为它是一个挥发性函数。

6
永远不要使用Select或Activesheet的主要原因是因为大多数人在运行您的宏时至少会有另外几个工作簿打开(有时可能有数十个)。如果他们在您的表格上单击并在您的宏运行时单击其他打开的某些书,则“Activesheet”更改,未经限定的“Select”命令的目标工作簿也会更改。
最好的情况是,您的宏将崩溃,最坏的情况是您可能会在错误的工作簿中写入值或更改单元格,而无法进行“撤消”。
我有一个简单的黄金法则:添加名为“ wb”和“ ws”的变量,用于工作簿对象和工作表对象,并始终使用它们来引用我的宏簿。如果我需要引用多个工作簿或多个工作表,则添加更多变量。
例如:
Dim wb as Workbook
Dim ws as Worksheet
Set wb = ThisWorkBook
Set ws = wb.sheets("Output")

"Set wb = ThisWorkbook"命令是非常重要的。"ThisWorkbook"在Excel中是一个特殊的值,它表示你的VBA代码当前正在运行的工作簿。这是设置工作簿变量的一个非常有用的快捷方式。

在你的子程序顶部完成这个操作之后,使用它们非常简单,只需要在任何你会使用"Selection"的地方使用它们即可:

所以,要将"Output"工作表中的单元格"A1"的值更改为"Hello",不需要写成:

Sheets("Output").Activate
ActiveSheet.Range("A1").Select
Selection.Value = "Hello"

我们现在可以这样做:
ws.Range("A1").Value = "Hello"

新的代码不仅更可靠,如果用户在处理多个电子表格时还不太可能崩溃;而且编写起来更短、更快、更容易。

另外一个好处是,如果你总是将变量命名为"wb"和"ws",那么你可以从一个工作簿复制和粘贴代码到另一个工作簿,通常只需要进行最少的修改就可以运行。


1
不是我点的踩,但我不确定这是否为现有答案提供了任何新的内容。 - BigBen
1
是的,我的答案有些冗余,但其他答案太长,包含了太多多余的东西,而且没有人提到使用ThisWorkbook来预先设置工作表变量。如果有人在我第一次尝试VBA时向我展示这个东西,我会觉得无比有用。其他人提到了使用工作表变量,但并没有非常好地解释为什么要这样做,并且没有提供使用工作表和工作簿变量进行编码的示例。 - Geoff Griswald
但是被接受的答案肯定讨论了 ThisWorkbook... 我不确定你的评论是否准确。 - BigBen
没错,它确实可以。但是在我所建议的上下文中,不是用它来设置工作簿变量并在以后使用该工作簿变量,或者使用该工作簿变量来设置工作表变量。我的答案比被接受的答案更简短、更简单、更适合初学者。 - Geoff Griswald

5
通过使用.Parent功能,此示例演示如何仅设置一个myRng引用即可动态访问整个环境而无需使用.Select、.Activate、.Activecell、.ActiveWorkbook、.ActiveSheet等方法。(没有通用的.Child功能。)
Sub ShowParents()
    Dim myRng As Range
    Set myRng = ActiveCell
    Debug.Print myRng.Address                    ' An address of the selected cell
    Debug.Print myRng.Parent.name                ' The name of sheet, where MyRng is in
    Debug.Print myRng.Parent.Parent.name         ' The name of workbook, where MyRng is in
    Debug.Print myRng.Parent.Parent.Parent.name  ' The name of application, where MyRng is in

    ' You may use this feature to set reference to these objects
    Dim mySh  As Worksheet
    Dim myWbk As Workbook
    Dim myApp As Application

    Set mySh = myRng.Parent
    Set myWbk = myRng.Parent.Parent
    Set myApp = myRng.Parent.Parent.Parent
    Debug.Print mySh.name, mySh.Cells(10, 1).Value
    Debug.Print myWbk.name, myWbk.Sheets.Count
    Debug.Print myApp.name, myApp.Workbooks.Count

    ' You may use dynamically addressing
    With myRng
        .Copy

        ' Pastes in D1 on sheet 2 in the same workbook, where the copied cell is
        .Parent.Parent.Sheets(2).Range("D1").PasteSpecial xlValues

        ' Or myWbk.Sheets(2).Range("D1").PasteSpecial xlValues

        ' We may dynamically call active application too
        .Parent.Parent.Parent.CutCopyMode = False

        ' Or myApp.CutCopyMode = False
    End With
End Sub

3
非常好,但不确定这与OP的问题有什么关系。在VBA中,您无需使用Select或ActiveSheet就可以工作,根本不需要“Parent”。 - Geoff Griswald

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接