我听说过很多关于在Excel VBA中使用. select的可以理解的厌恶,但我不确定如何避免使用它。我发现,如果我能够使用变量而不是Select函数,我的代码将更具可重用性。然而,我不确定如果不使用选择如何引用事物(如ActiveCell等)。

我找到了这篇关于范围的文章和这个关于不使用select的好处的例子,但我找不到任何关于如何使用的东西。


当前回答

始终声明工作簿,工作表和单元格/范围。

例如:

Thisworkbook.Worksheets("fred").cells(1,1)
Workbooks("bob").Worksheets("fred").cells(1,1)

因为终端用户总是只会点击按钮,一旦焦点从代码想要使用的工作簿上移开,事情就会完全出错。

永远不要使用工作簿的索引。

Workbooks(1).Worksheets("fred").cells(1,1)

当用户运行您的代码时,您不知道还会打开哪些工作簿。

其他回答

一些如何避免选择的例子

使用Dim变量

Dim rng as Range

将变量设置为所需的范围。有很多种方法来指代单格范围:

Set rng = Range("A1")
Set rng = Cells(1, 1)
Set rng = Range("NamedRange")

或者一个多单元格范围:

Set rng = Range("A1:B10")
Set rng = Range("A1", "B10")
Set rng = Range(Cells(1, 1), Cells(10, 2))
Set rng = Range("AnotherNamedRange")
Set rng = Range("A1").Resize(10, 2)

您可以使用Evaluate方法的快捷方式,但这效率较低,通常应该在生产代码中避免使用。

Set rng = [A1]
Set rng = [A1:B10]

以上所有示例都引用活动工作表上的单元格。除非你特别想只使用活动表,否则最好将工作表变量也调暗:

Dim ws As Worksheet
Set ws = Worksheets("Sheet1")
Set rng = ws.Cells(1, 1)
With ws
    Set rng = .Range(.Cells(1, 1), .Cells(2, 10))
End With

如果你确实想要使用ActiveSheet,为了清晰起见,最好是显式的。但是要小心,因为有些工作表方法会更改活动表。

Set rng = ActiveSheet.Range("A1")

同样,这是指活动工作簿。除非你特别想只使用ActiveWorkbook或ThisWorkbook,否则最好也使用Dim Workbook变量。

Dim wb As Workbook
Set wb = Application.Workbooks("Book1")
Set rng = wb.Worksheets("Sheet1").Range("A1")

如果您确实想使用ActiveWorkbook,为了清晰起见,最好是显式的。但是要注意,因为许多WorkBook方法会更改活动簿。

Set rng = ActiveWorkbook.Worksheets("Sheet1").Range("A1")

您还可以使用ThisWorkbook对象来引用包含运行代码的书籍。

Set rng = ThisWorkbook.Worksheets("Sheet1").Range("A1")

一个常见的(糟糕的)代码片段是打开一本书,获得一些数据,然后再次关闭

这很糟糕:

Sub foo()
    Dim v as Variant
    Workbooks("Book1.xlsx").Sheets(1).Range("A1").Clear
    Workbooks.Open("C:\Path\To\SomeClosedBook.xlsx")
    v = ActiveWorkbook.Sheets(1).Range("A1").Value
    Workbooks("SomeAlreadyOpenBook.xlsx").Activate
    ActiveWorkbook.Sheets("SomeSheet").Range("A1").Value = v
    Workbooks(2).Activate
    ActiveWorkbook.Close()
End Sub

最好是这样:

Sub foo()
    Dim v as Variant
    Dim wb1 as Workbook
    Dim  wb2 as Workbook
    Set wb1 = Workbooks("SomeAlreadyOpenBook.xlsx")
    Set wb2 = Workbooks.Open("C:\Path\To\SomeClosedBook.xlsx")
    v = wb2.Sheets("SomeSheet").Range("A1").Value
    wb1.Sheets("SomeOtherSheet").Range("A1").Value = v
    wb2.Close()
End Sub

将范围传递给子函数和函数作为范围变量:

Sub ClearRange(r as Range)
    r.ClearContents
    '....
End Sub

Sub MyMacro()
    Dim rng as Range
    Set rng = ThisWorkbook.Worksheets("SomeSheet").Range("A1:B10")
    ClearRange rng
End Sub

你还应该对变量应用方法(比如Find和Copy):

Dim rng1 As Range
Dim rng2 As Range
Set rng1 = ThisWorkbook.Worksheets("SomeSheet").Range("A1:A10")
Set rng2 = ThisWorkbook.Worksheets("SomeSheet").Range("B1:B10")
rng1.Copy rng2

如果你循环遍历一个单元格范围,通常更好(更快)复制范围值到一个变量数组,然后循环遍历它:

Dim dat As Variant
Dim rng As Range
Dim i As Long

Set rng = ThisWorkbook.Worksheets("SomeSheet").Range("A1:A10000")
dat = rng.Value  ' dat is now array (1 to 10000, 1 to 1)
for i = LBound(dat, 1) to UBound(dat, 1)
    dat(i,1) = dat(i, 1) * 10 ' Or whatever operation you need to perform
next
rng.Value = dat ' put new values back on sheet

这是对可能性的小小尝试。

我要给出简短的答案,因为其他人都给出了长答案。

当您记录宏并重用它们时,您将得到.select和.activate。当你选择一个单元格或工作表时,它会使它激活。从那时起,当你使用像Range这样的非限定引用时。值他们只是使用活动单元格和表。如果您没有观察代码放置的位置或用户单击工作簿,这也会产生问题。

因此,您可以通过直接引用单元格来消除这些问题。这是:

'create and set a range
Dim Rng As Excel.Range
Set Rng = Workbooks("Book1").Worksheets("Sheet1").Range("A1")
'OR
Set Rng = Workbooks(1).Worksheets(1).Cells(1, 1)

或者你可以

'Just deal with the cell directly rather than creating a range
'I want to put the string "Hello" in Range A1 of sheet 1
Workbooks("Book1").Worksheets("Sheet1").Range("A1").value = "Hello"
'OR
Workbooks(1).Worksheets(1).Cells(1, 1).value = "Hello"

这些方法有多种组合,但对于像我这样没有耐心的人来说,这将是尽可能简短地表达的总体思想。

如何避免复制粘贴?

让我们面对现实吧:这个在记录宏时经常出现:

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

而这个人唯一想要的是:

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

因此,与其在VBA宏中使用复制粘贴,我建议使用以下简单的方法:

Destination_Range.Value = Source_Range.Value

从不使用选择或活动表的主要原因是,大多数人在运行宏时至少会打开另外几个工作簿(有时是几十个),如果他们在宏运行时从工作表中单击离开,并单击他们已经打开的其他工作簿,那么“活动表”会发生变化,而不满足“选择”命令的目标工作簿也会发生变化。

在最好的情况下,你的宏会崩溃,在最坏的情况下,你可能会在错误的工作簿中写入值或更改单元格,而无法“撤消”它们。

我有一个我遵循的简单的黄金法则:为工作簿对象和工作表对象添加名为“wb”和“ws”的变量,并始终使用它们来引用我的宏书。如果我需要引用多本书或多张表格,我会添加更多变量。

例如,

Dim wb as Workbook
Dim ws as Worksheet
Set wb = ThisWorkBook
Set ws = wb.sheets("Output")

“Set wb = ThisWorkbook”命令绝对是关键。“ThisWorkbook”是Excel中的一个特殊值,它意味着您的VBA代码当前正在运行的工作簿。一个非常有用的快捷方式来设置你的工作簿变量。

当你在你的Sub顶部做了这些之后,使用它们再简单不过了,只要在你使用“Selection”的地方使用它们:

因此,要将“Output”中的单元格“A1”的值更改为“Hello”,而不是:

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

我们现在可以这样做:

ws.Range("A1").Value = "Hello"

如果用户使用多个电子表格,它不仅更可靠,而且不太可能崩溃;它也更短,更快,更容易写。

作为额外的奖励,如果你总是将变量命名为“wb”和“ws”,你可以将代码从一本书复制粘贴到另一本书,通常只需要进行最小的更改就可以工作。

请注意,在下面我将比较Select方法(OP想要避免的方法)和Range方法(这是问题的答案)。所以当你看到第一个选择时不要停止阅读。

这真的取决于你想要做什么。总之,举个简单的例子会很有用。让我们假设您想要将活动单元格的值设置为“foo”。使用ActiveCell,你可以这样写:

Sub Macro1()
    ActiveCell.Value = "foo"
End Sub

如果你想将它用于一个非活动单元格,例如“B2”,你应该首先选择它,如下所示:

Sub Macro2()
    Range("B2").Select
    Macro1
End Sub

使用Ranges,你可以编写一个更通用的宏,可以用来设置任何你想要的单元格的值:

Sub SetValue(cellAddress As String, aVal As Variant)
    Range(cellAddress).Value = aVal
End Sub

然后你可以重写Macro2为:

Sub Macro2()
    SetCellValue "B2", "foo"
End Sub

而Macro1为:

Sub Macro1()
    SetValue ActiveCell.Address, "foo"
End Sub