如何避免在Excel VBA中使用Select

我已经听说了很多可以理解的使用憎恨。在Excel VBA中select,但我不确定如何避免使用它。 我发现如果我能够使用variables而不是Select函数,我的代码将更加可重用。 但是,我不知道如何引用的东西(如ActiveCell等),如果不使用Select

我发现这篇文章的范围和这个例子的好处,不使用select,但不能find任何东西如何

一些如何避免select的例子

使用Dimvariables

 Dim rng as Range 

Setvariables设置为所需的范围。 有许多方法可以参考单细胞的范围

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

或者多电池范围

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

以上所有示例都涉及活动工作表上的单元格。 除非你只想使用活动工作表,那么最好还是Dim一个Worksheetvariables

 Dim ws As Worksheet Set ws = Worksheets("Sheet1") Set rng = ws.Cells(1,1) 

再一次,这是指活动工作簿 ,所以你可能也想在这里明确。

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

将范围传递给你的SubFunction作为范围variables

 Sub ClearRange(r as Range) r.ClearContents '.... End Sub Sub MyMacro() Dim rng as Range Set rng = [A1:B10] ClearRange rng End Sub 

您还应该将方法(如FindCopy )应用于variables

 Dim rng1 As Range Dim rng2 As Range Set rng1 = [A1:A10] Set rng2 = [B1:B10] rng1.Copy rng2 

如果循环遍历一系列单元格,通常先将范围值复制到一个变体数组中并更好地(循环)

 Dim dat As Variant Dim rng As Range Dim i As Long Set rng = [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 

这是一个可能的小品尝师。

两个主要原因, Selection /。 Activecell / Selection / Activecell / Activesheet / Activesheet等…应该避免

  1. 它减慢你的代码。
  2. 这通常是运行时错误的主要原因。

我们如何避免它?

1)直接与相关对象合作

考虑这个代码

 Sheets("Sheet1").Activate Range("A1").Select Selection.Value = "Blah" Selection.NumberFormat = "@" 

这段代码也可以写成

 With Sheets("Sheet1").Range("A1") .Value = "Blah" .NumberFormat = "@" End With 

2)如果需要声明你的variables。 上面的代码可以写成

 Dim ws as worksheet Set ws = Sheets("Sheet1") With ws.Range("A1") .Value = "Blah" .NumberFormat = "@" End With 

一个小的重点我会添加到上面给出的所有优秀的答案:

避免使用Select可以做的最重要的事情是尽可能地使用VBA代码中的命名范围(结合有意义的variables名称) 。 这一点在上面已经提到,但是略过了一点; 但值得特别关注。

这里有一些额外的理由,使自由使用命名的范围,虽然我相信我能想到更多。

命名范围使您的代码更易于阅读和理解。

例:

 Dim Months As Range Dim MonthlySales As Range Set Months = Range("Months") 'eg, "Months" might be a named range referring to A1:A12 Set MonthlySales = Range("MonthlySales") 'eg, "Monthly Sales" might be a named range referring to B1:B12 Dim Month As Range For Each Month in Months Debug.Print MonthlySales(Month.Row) Next Month 

很明显, MonthsMonthlySales包含的命名范围以及该过程正在做什么。

为什么这很重要? 部分原因是其他人更容易理解它,但即使你是唯一一个看到或使用你的代码的人,仍然应该使用命名的范围和好的variables名,因为你会忘记你的意思是做什么一年之后, 你会浪费 30分钟,只是搞清楚你的​​代码在做什么。

命名范围确保您的macros不会在电子表格的configuration发生变化(如果不是)!

考虑一下,如果上面的例子是这样写的:

 Dim rng1 As Range Dim rng2 As Range Set rng1 = Range("A1:A12") Set rng2 = Range("B1:B12") Dim rng3 As Range For Each rng3 in rng1 Debug.Print rng2(rng3.Row) Next rng3 

这个代码一开始就能正常工作 – 直到你或者将来的用户决定“gee wiz,我想我会在A列中增加一个新的列”,或者在月和销售列,或为每列添加标题。 现在,你的代码被破坏了。 而且,因为使用了可怕的variables名称,所以需要花费更多的时间来弄清楚如何解决这个问题。

如果您已经使用了命名范围,那么MonthsSales列可以随意移动,而您的代码将继续正常工作。

因为其他人都给出了很长的答案,所以我会给出简短的答案。

每当您录制macros并重用它们时,您都会得到.select和.activate。 当你select一个单元格或工作表时,它就会激活它。 Range.Value ,只要你使用像Range.Value这样的非限定引用,他们就使用活动的单元格和表单。 如果您不注意代码的放置位置或用户单击工作簿,这也可能会造成问题。

所以,你可以通过直接引用你的单元来消除这些问题。 哪个去:

 '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" 

这些方法有各种各样的组合,但对于像我这样不耐烦的人来说,这将是尽可能快地expression出来的一般想法。

“…我发现如果我能够使用variables而不是select函数,我的代码将更加可重用。”

虽然我不能想象任何一个孤立的情况下,select将是一个比直接单元格参考更好的select,我会起来捍卫Selection并指出它不应该被抛出出于同样的原因.Select应该避免。

有时候,通过轻敲几个键就可以将短的,节省时间的macros子程序分配给热键组合,节省了大量的时间。 在处理不符合工作表范围的数据格式的独占数据时,能够select一组单元来制定操作代码。 与您可能select一组单元格并应用格式更改的方式大致相同,select一组单元格以运行特殊的macros代码可能会大大节省时间。

基于select的子框架的例子:

 Public Sub Run_on_Selected() Dim rng As Range, rSEL As Range Set rSEL = Selection 'store the current selection in case it changes For Each rng In rSEL Debug.Print rng.Address(0, 0) 'cell-by-cell operational code here Next rng Set rSEL = Nothing End Sub Public Sub Run_on_Selected_Visible() 'this is better for selected ranges on filtered data or containing hidden rows/columns Dim rng As Range, rSEL As Range Set rSEL = Selection 'store the current selection in case it changes For Each rng In rSEL.SpecialCells(xlCellTypeVisible) Debug.Print rng.Address(0, 0) 'cell-by-cell operational code here Next rng Set rSEL = Nothing End Sub Public Sub Run_on_Discontiguous_Area() 'this is better for selected ranges of discontiguous areas Dim ara As Range, rng As Range, rSEL As Range Set rSEL = Selection 'store the current selection in case it changes For Each ara In rSEL.Areas Debug.Print ara.Address(0, 0) 'cell group operational code here For Each rng In ara.Areas Debug.Print rng.Address(0, 0) 'cell-by-cell operational code here Next rng Next ara Set rSEL = Nothing End Sub 

要处理的实际代码可以是从单一行到多个模块的任何代码。 我已经使用这种方法启动长时间运行的例程,对包含外部工作簿文件名的单元格进行粗选。

总之,不要因为与.SelectActiveCell密切关联而丢弃Selection 。 作为工作表属性,它有许多其他的用途。

(是的,我知道这个问题是关于。 Selection ,而不是Selection但我想删除新手VBA编码器可能推断的任何误解。

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

这实际上取决于你想要做什么。 无论如何一个简单的例子可能是有用的。 假设您要将活动单元格的值设置为“foo”。 使用ActiveCell你会写这样的东西:

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

如果您想将其用于非活动单元格,例如“B2”,则应先select它,如下所示:

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

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

 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 

希望这有助于澄清一些事情。

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

例如:

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

因为最终用户总是只是点击button,只要焦点离开工作簿的代码想要工作,那么事情就完全错了。

不要使用工作簿的索引。

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

您不知道在用户运行代码时将打开其他工作簿。

恕我直言,使用.select来自于人们,他们喜欢我通过录制macros开始学习VBA,然后修改代码而不知道。 .select和随后的selection只是一个不必要的中间人。

.select可以避免,因为已经发布了很多,直接使用已经存在的对象,这允许各种间接引用,如复杂的方式计算i和j,然后编辑单元格(i,j)等。

否则, .select本身并没有什么错误,你可以很容易地find它的用途,例如我有一个电子表格,我用date填充,激活macros,它做了一些魔术,并以可接受的格式导出它在一个单独的工作表,然而,这需要一些最终的手动(不可预知的)input到相邻的单元中。 所以这里是.select的时刻,这节省了我额外的鼠标移动和点击。

这是你可以避免在以下情况下Select

当你想在工作表之间复制范围时:

从:

 Sheets("Source").Select Columns("A:D").Select Selection.Copy Sheets("Target").Select Columns("A:D").Select ActiveSheet.Paste 

至:

 Worksheets("Source").Columns("A:D").Copy Destination:=Worksheets("Target").Range("a1") 

使用奇特的命名范围

你可以用[]来访问它们。 与其他方式相比,这真的很漂亮。 自行检查:

 Dim Months As Range Dim MonthlySales As Range Set Months = Range("Months") Set MonthlySales = Range("MonthlySales") Set Months =[Months] Set MonthlySales = [MonthlySales] 

上面的例子看起来像这样:

 Worksheets("Source").Columns("A:D").Copy Destination:=Worksheets("Target").[A1] 

不是复制值,而是采取它们

通常,如果你愿意select ,很可能你正在复制一些东西。 如果你只对值有兴趣,这是避免select的一个好的select:

Range("B1:B6").Value = Range("A1:A6").Value

快速回答:

为避免使用.Select方法,可以设置一个等于所需属性的variables。

►例如,如果您需要Cell A1的值,则可以设置一个等于该单元格的值属性的variables。

  • 示例valOne = Range("A1").Value

►例如,如果您想要“Sheet3”的代号,您可以设置一个等于该工作表的代号名称的variables。

  • 示例valTwo = Sheets("Sheet3").Codename

我希望有帮助。 如果您有任何问题,请告诉我。

这是一个例子,将清除单元格“A1”(或更多,如果selecttypes是xllastcell等)的内容。 全部完成,无需select单元格。

 Application.GoTo Reference:=Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1") Range(Selection,selection(selectiontype)).clearcontents 

我希望这可以帮助别人。