如何避免在Excel VBA中使用选择


537

我已经听到很多有关.Select在Excel VBA 中使用可理解的厌恶的信息,但是不确定如何避免使用它。我发现如果我能够使用变量而不是Select函数,那么我的代码将更可重用。但是,ActiveCell如果不使用,我不确定如何引用事物(例如,等等)Select

我发现这篇文章的范围,并在不使用选择的好处这个例子中,但无法找到任何东西怎么样


14
重要的是要注意,在某些情况下,使用Select和/或ActiveSheet等等是完全不可避免的。这里有一个例子,我发现:stackoverflow.com/questions/22796286/...
里克支持莫妮卡

9
在某些情况下-在ppt中编辑图表数据(其中包含一个基础excel文件)-需要激活或选择。
brettdj

@brettdj- 这是最近的一个例子。要将工作簿中的所有工作表设置为相同的值,这似乎.Select / .Selection是必需的。
BruceWayne

3
来自同一质量检查人员的@bruce 似乎不是
chris neilsen

Answers:


564

如何避免选择的一些例子

使用Dim'd变量

Dim rng as Range

Set变量到所需范围。有很多方法可以引用单细胞范围

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]

以上所有示例均涉及活动工作表上的单元格。除非您特别想只使用活动工作表,否则最好也将Worksheet变量变暗

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,为了清楚起见,最好是明确的。但是请注意,因为某些Worksheet方法会更改活动工作表。

Set rng = ActiveSheet.Range("A1")

同样,这是指活动工作簿。除非您特别希望仅使用ActiveWorkbookThisWorkbook,否则也最好对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

将范围作为范围变量传递给Subs和Functions

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

您还应该将方法(例如FindCopy)应用于变量

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

这是一个可能的小尝试者。


7
除了这个精妙的答案外,为了在一个范围内工作,您只要知道左上角就不需要知道其实际大小 ...例如,rng1(12, 12)即使将rng1设置为[A1:A10]only也可以。
MikeD

3
@chrisneilsen Chris,我相信您也可以在速记单元格引用表示法之前使用工作表前缀,以免您Range像这样输入:ActiveSheet.[a1:a4]ws.[b6]
Logan Reed

3
@AndrewWillems ...或此帖子中有48次,但谁在指望。☺...但很重要的是,在处理包含对象的变量时,这很容易忘记。在为variant变量分配对象Set 之前,不需要变量。例如,Dim x: x = 1是好的,但Dim x: x = Sheets("Sheet1")会产生错误438.但是仅仅是用来迷惑/澄清Dim x: x = Range("A1")不会产生错误。为什么?...因为它是将对象的分配给变量,而不是对象本身的引用(因为它等效于Dim x: x = Range("A1").Value
ashleedawg

1
@ user3932000我不知道图纸名称会自动更改的情况。至于文件名,只有在文件夹中已经有该名称的文件时,它才会这样做。只需使用Save ...或对文件名进行硬编码即可将其保存为字符串。如果您不能解决此问题,则应提出一个单独的问题,而不要发表评论。
TylerH '18

1
@ user3932000会产生一个有趣的问题。我敢肯定,有很多方法可以解决它。.您已经走了这么长时间,才知道劫持旧问题上的评论线程是不行的方法
chris neilsen

212

主要有两个原因,为什么.Select/ .Activate/ Selection/ Activecell/ Activesheet/ Activeworkbook等..应避免

  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)如果需要,声明您的变量。上面的相同代码可以写成

Dim ws as worksheet

Set ws = Sheets("Sheet1")

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

17
这是一个很好的答案,但是在这个主题上我缺少的是何时我们真正需要激活。每个人都说这很不好,但是没有人解释使用它的任何情况。例如,我正在使用2个工作簿,并且没有先激活它就无法在其中一个工作簿上启动宏。您能否详细说明一下?同样,例如,如果我在将范围从一张纸复制到另一张纸时不激活纸,则在我执行程序时,似乎还是隐式地激活了相应的纸。
user3032689 '16

1
我发现,如果您需要在工作表上粘贴或过滤数据,有时可能需要先激活它。我会说最好避免尽可能多地激活,但是在某些情况下您需要这样做。因此,按照上面的答案,保持激活并选择最低限度的绝对值。
尼克,

7
我认为重点不是要完全避免使用它们,而应尽可能地避免使用它们。如果要保存工作簿,以便在有人打开它时选择了某个工作表中的某个单元格,则必须选择该工作表和单元格。复制/粘贴是一个不好的例子,至少在值的情况下,可以通过以下代码来更快地完成它:Sheets(2).[C10:D12].Value = Sheets(1).[A1:B3].Value
robotik

1
@Nick您无需激活工作表即可将其粘贴或过滤。在粘贴或过滤器命令中使用工作表对象。通过练习学习Excel对象模型变得更加容易。我相信唯一使用.Activate的时间是在创建新工作表时,但是我希望原始工作表在代码完成后出现。
phrebh

3
@phrebh您无需使用.Activate即可移至原始工作表,只需使用Application.Goto
GMalc

88

我将在上面给出的所有出色答案中添加一点重点:

要避免使用Select,最大的可能就是尽可能在VBA代码中使用命名范围(结合有意义的变量名称)。上面已经提到了这一点,但略有掩饰。但是,它值得特别注意。

尽管我确信我可以想到更多的原因,但还有另外两个原因可以自由使用命名范围。

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

例:

Dim Months As Range
Dim MonthlySales As Range

Set Months = Range("Months")
'e.g, "Months" might be a named range referring to A1:A12

Set MonthlySales = Range("MonthlySales")
'e.g, "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包含什么以及该过程在做什么。

为什么这很重要?部分原因是因为它使其他人更容易理解它,但是即使您是唯一会看到或使用您的代码的人,您仍应使用命名范围和良好的变量名,因为您会忘记使用它的意图一年后,您将花费 30分钟来弄清楚您的代码在做什么。

命名范围可确保您的宏在电子表格的配置更改时(如果没有!)不会中断。

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

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

这段代码起初会很好用-直到您或未来的用户决定“老兄,我想我要在Column中添加带有年份的新列 A!”,或在月份和销售列,或为每个列添加标题。现在,您的代码已损坏。而且由于使用了可怕的变量名,因此花了更多的时间弄清楚如何解决它,而不是应该花费的时间。

如果您首先使用命名范围,则可以根据需要随意移动MonthsSales列,并且代码将继续正常运行。


6
关于命名范围是好是坏的电子表格设计的争论仍在继续-我坚决反对。以我的经验,它们会增加错误(对于不需要代码的标准用户)。
brettdj


12
我同意您的发展理念;但是我认为这篇论文是胡说八道。它讨论范围名称如何使正在调试电子表格的新手感到困惑,但是使用新手查看复杂电子表格的任何人都会得到他们应得的!我曾经为一家审查过财务电子表格的公司工作,我可以告诉你,这不是您给新手提供的工作。
DeanOC

8
没有有意义的辩论。任何反对定义名称的人都没有花时间充分理解其含义。命名公式可能是所有Excel中最深刻和最有用的结构。
Excel英雄

10
@brettdj:您的引用是正确的,但是您忘了提到它后跟六个 “ Except ...”短语。其中之一是:“ 除了在宏编码中 替代单元格引用以外,在构造宏时始终使用Excel名称作为对单元格引用的替代。这是为了避免由于插入其他行或列而导致的错误,从而导致宏编码不再出现指向预期的源数据。”
Marcus Mangelsdorf

47

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

每当记录宏并重用它们时,您都会获得.select和.activate。当您选择一个单元格或工作表时,只会使其处于活动状态。从那时起,只要您使用不合格的引用(例如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"

这些方法有各种各样的组合,但是对于像我这样不耐烦的人,这将是尽可能短的表达的总体思路。


33

“ ...并且发现如果我能够使用变量而不是Select函数,我的代码将更可重用。”

虽然我只能想到少数几个.Select比直接单元格引用更好的选择,但是我会提出抗辩Selection并指出,不应出于.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

实际要处理的代码可以是从单行到多个模块的任何内容。我已经使用此方法在包含外部工作簿文件名的单元格参差不齐的单元格上启动长时间运行的例程。

简而言之,Selection由于其与.Select和紧密相关,因此请勿丢弃ActiveCell。作为工作表属性,它还有许多其他用途。

(是的,我知道这个问题与.Select无关,Selection但是我想消除新手VBA编码人员可能推断出的任何误解。)


13
Selection可以是工作表中的任何内容,因此最好先测试对象的类型,然后再将其分配给变量,因为您将其显式声明为Range
L42

29

请注意,下面我将比较Select方法(OP希望避免的一种方法)和Range方法(这是问题的答案)。因此,当您看到第一个“选择”时不要停止阅读。

这实际上取决于您要执行的操作。无论如何,一个简单的例子可能会有用。假设您要将活动单元格的值设置为“ foo”。使用ActiveCell可以编写如下代码:

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

如果要将其用于非活动单元格,例如“ B2”,则应首先选择它,如下所示:

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

使用范围,您可以编写一个更通用的宏,该宏可用于将所需的任何单元格的值设置为所需的任何值:

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

希望这有助于清除一些问题。


1
感谢您如此迅速的答复。因此,这是否意味着如果我通常将单元格添加到范围,命名范围并对其进行迭代,那么我应该直接跳至创建数组?
BiGXERO 2012年

我不确定我是否理解您的意思,但是您可以使用一条指令(例如Range(“ B5:C14”))创建一个Range,甚至可以一次设置其值(如果必须相同)范围内的每个单元格),例如Range(“ B5:C14”)。Value =“ abc”
Francesco Baruchelli 2012年

28

避免SelectActivate这样做会使您成为更好的VBA开发人员。一般情况下,SelectActivate当宏被记录的使用,因此,Parent工作表或范围始终被认为处于活动状态。

这是你可以如何避免SelectActivate以下情况:


添加一个新的工作表并在其上复制一个单元格:

发件人(使用宏记录器生成的代码):

Sub Makro2()
    Range("B2").Select
    Sheets.Add After:=ActiveSheet
    Sheets("Tabelle1").Select
    Sheets("Tabelle1").Name = "NewName"
    ActiveCell.FormulaR1C1 = "12"
    Range("B2").Select
    Selection.Copy
    Range("B3").Select
    ActiveSheet.Paste
    Application.CutCopyMode = False
End Sub

至:

Sub TestMe()
    Dim ws As Worksheet
    Set ws = Worksheets.Add
    With ws
        .Name = "NewName"
        .Range("B2") = 12
        .Range("B2").Copy Destination:=.Range("B3")
    End With
End Sub

当您要在工作表之间复制范围时:

从:

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,很可能是您要复制某些内容。如果您仅对值感兴趣,这是避免选择的好方法:

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


尽量总是参考工作表

这可能是最常见的错误 。每当您复制范围时,有时都不会引用工作表,因此VBA会将错误的工作表视为ActiveWorksheet。

'This will work only if the 2. Worksheet is selected!
Public Sub TestMe()
    Dim rng As Range
    Set rng = Worksheets(2).Range(Cells(1, 1), Cells(2, 2)).Copy
End Sub

'This works always!
Public Sub TestMe2()
    Dim rng As Range
    With Worksheets(2)
        .Range(.Cells(1, 1), .Cells(2, 2)).Copy
    End With
End Sub

我真的可以永远不使用.Select.Activate做任何事情吗?

  • 一个很好的例子,说明什么时候可以合理使用,.Activate以及.Select何时要确保出于视觉原因选择了特定的工作表。例如,您的Excel将始终以首先选择封面工作表的方式打开,而不考虑关闭文件时哪个是ActiveSheet。

因此,类似以下代码的代码绝对可以:

Private Sub Workbook_Open()
    Worksheets("Cover").Activate
End Sub

您可以使用Application.Goto代替Worksheets.Activate。风险较小。
Geoff Griswald

1
FYI的延迟回复-有关需要的一个很好的示例,.Select以及我的解决方法-可以在 如何在所有工作表上写入相同的信息 -@Vityata :)
TM

1
@TM-确实是一个有趣的示例,可能为100多个工作表节省了几毫秒,但是如果我在某处看到它,我可能会不赞成这样做。无论如何,“选择集”没有明确地写成,而是的结果.FillAcrossSheets,因此介于两者之间(至少在我关于VBA分类法的想法中)
Vityata

17

始终陈述工作簿,工作表和单元格/范围。

例如:

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

因为最终用户将总是只单击按钮,并且一旦焦点移到工作簿上,代码就想使用它,那么事情就完全错了。

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

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

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


7
您知道工作表的名称也可以更改。请改用代号。
里克

当然,工作表名称可以更改。但是我不同意您应该过度复杂化代码以尝试减轻这种情况。如果用户更改了工作表的名称,而他们的宏停止工作,那么它们就在上面。我通常只是假设工作表名称将相同。对于特别重要的宏,在启动宏之前,我会进行一些飞行前检查,这只是检查以确保它期望找到的所有工作表确实存在,如果有的话,它将通知用户。
Geoff Griswald

10

这些方法已被污名化,因此以@Vityata和@Jeeped为首,以便在沙子上画一条线:

为什么不叫.Activate.SelectSelectionActiveSomething方法/属性

基本上是因为调用它们主要是为了通过Application UI处理用户输入。由于它们是用户通过UI处理对象时调用的方法,因此它们是由宏记录器记录的方法,这就是为什么在大多数情况下调用它们既脆弱又多余的原因:您不必选择对象以便执行动作Selection随后。

但是,此定义解决了需要它们的情况:

当调用.Activate.Select.Selection.ActiveSomething方法/属性

基本上,当您期望最终用户时在执行中扮演角色时。

如果您正在开发并且希望用户为代码选择要处理的对象实例,则.Selection.ActiveObject合适的。

在另一方面,.Select并且.Activate在使用时,你可以推断出用户的下一个行动,你希望你的代码,以引导用户,可能为他节省了一些时间和鼠标点击。例如,如果您的代码只是创建了一个全新的图表实例或更新了一个图表实例,则用户可能想将其检出,您可以调用.Activate它或它的工作表以节省用户搜索它的时间;或者,如果您知道用户需要更新某些范围值,则可以以编程方式选择该范围。


6

对我的恕我直言.select是来自人们的,像我这样的人通过记录宏来根据需要开始学习VBA,然后在没有意识到这一点的情况下修改代码.select,因此selection这只是不必要的中间人。

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

否则,.select它本身就没有任何隐含的错误,您可以轻松地找到它的用途,例如,我有一个填充有日期的电子表格,激活了可对其进行魔术的宏并将其以可接受的格式导出到另一张纸上,但是,需要一些最终的手动(不可预测的)输入到相邻单元格中。因此,此刻为.select我节省了额外的鼠标移动和单击操作。


2
当您是对的时候,select至少存在一件事隐含的错误:它很慢。与宏中发生的所有其他事件相比,确实非常慢。
vacip '16

4

快速回答:

为了避免使用该.Select方法,可以将变量设置为等于所需的属性。

►例如,如果要输入的值,Cell A1可以将变量设置为等于该单元格的value属性。

  • valOne = Range("A1").Value

►例如,如果您想要'Sheet3'的代号,则可以设置一个等于该工作表代号属性的变量。

  • valTwo = Sheets("Sheet3").Codename

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


3

我注意到这些答案都没有提到.Offset属性。这也可以用来避免Select在处理某些单元格时使用操作,尤其是在引用所选单元格时(如OP中提到的那样)。ActiveCell)。

这是几个例子。

我还将假设“ ActiveCell”是J4

ActiveCell.Offset(2, 0).Value = 12

  • 这会将单元格J6的值更改为12
  • 负-2会引用J2

ActiveCell.Offset(0,1).Copy ActiveCell.Offset(,2)

  • 这会将单元格复制k4到中L4
  • 请注意,如果不需要,在offset参数中不需要“ 0”(,2)
  • 与前面的示例类似,负1将是 i4

ActiveCell.Offset(, -1).EntireColumn.ClearContents

  • 这将清除k列中所有单元格中的值。

这些并不是说它们比上述选项“更好”,而只是列出了替代方案。


0

使用.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 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

非常好,但不确定与OP问题有什么关系。您无需使用“选择”或ActiveSheet,就完全不需要“父母”来在VBA中工作
Geoff Griswald,

0

绝不使用Select或Activesheet的主要原因是,大多数人在运行宏时会打开至少另外几本工作簿(有时数十本),并且如果在宏运行时从工作表上移开并单击其他他们打开的书,然后更改“ Activesheet”,并且不合格的“选择”命令的目标工作簿也将更改。

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

我遵循一个简单的黄金法则:为工作簿对象和工作表对象添加名为“ wb”和“ ws”的变量,并始终使用这些变量引用我的宏书。如果需要参考多于一本书或多于一页纸,则添加更多变量。

例如

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

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

在Sub的顶部完成此操作之后,使用它们再简单不过了,只要在要使用“ Selection”的位置使用它们即可:

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

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

我们现在可以这样做:

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

如果用户使用多个电子表格,这不仅可靠性更高,而且崩溃的可能性更低,而且编写起来更短,更快,更容易。

另外,如果您始终将变量命名为“ wb”和“ ws”,则可以将代码从一本书复制并粘贴到另一本书,并且通常只需很少的更改即可工作。


1
不是我的不赞成,但是我不确定这会给现有答案中已经提出的内容增加新的内容。
BigBen

是的,我的答案有点多余,但是其他答案太长了,包含太多多余的内容,没有人提到使用ThisWorkbook预先设置工作表变量。如果有人在我第一次将脚趾浸入VBA中时向我展示了这一点,那我会发现非常有用。其他人提到使用Worksheet变量,但是并没有真正解释为什么很好,也没有提供使用和不使用Worksheet和Workbook变量的代码示例。
Geoff Griswald

但是接受的答案肯定是在讨论ThisWorkbook……我不确定您的评论是否正确。
BigBen

确实如此,您没看错。但是,正如我建议的那样,在使用它来设置工作簿变量并继续使用该工作簿变量的上下文中,或者在使用该工作簿变量设置工作表变量的上下文中,这并非如此。与已接受的答案相比,对于初学者来说,我的答案更短,更简单且更易于使用。
Geoff Griswald

-3

此示例将清除单元格“ A1”的内容(如果选择类型为xllastcell等,则清除更多内容)。无需选择单元格即可完成所有操作。

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

我希望这可以帮助别人。


1
不,对不起 那根本不是你在那做的。您实际上所做的是使用“ Application.GoTo”命令选择单元格“ A1”,这与真正使用“选择”没什么不同,然后在选择内容上使用了clearcontents。在不选择单元格的情况下执行此操作的方式将Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1").ClearContents是一行而不是两行,并且实际上无需选择单元格即可工作。
Geoff Griswald
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.