【问题标题】:How to Loop through Worksheets Except TOC如何遍历除 TOC 之外的工作表
【发布时间】:2020-04-29 12:20:35
【问题描述】:

我正在尝试循环一个将在所有工作表中运行的宏,“TOC”表除外。
- 每个工作表都将创建一个表格,格式化并放入公式。

但是,宏尝试从“TOC”表开始,这会导致错误。

我目前有:

Sub BrandRank_()

Dim wb As ThisWorkbook
Dim ws As Worksheet
Dim TableName As String
Dim LstObj As ListObjects
Dim LastRow As Long

For Each ws In ThisWorkbook.Worksheets
Select Case ws.Name
Case Is = "TOC"

Case Else
With ws

‘Insert Table
TableName = "MyTable"
Range("A3").CurrentRegion.Select
ActiveSheet.ListObjects.Add(xlSrcRange, Selection.CurrentRegion, , xlYes).Name = TableName

'Apply a filter to $ Share for all Brands (Largest to Smallest)
ws.AutoFilter.Sort.SortFields.Clear
ws.AutoFilter.Sort.SortFields.Add2 Key:=Range("C3"), SortOn:=xlSortOnValues, Order:=xlDescending
ws.AutoFilter.ApplyFilter
ActiveSheet.ListObjects("Table1").ShowAutoFilterDropDown = False

'More Table Formatting Code

End With
End Select
Next ws
End Sub`

以下更新以反映 Samuel Everson 的建议:

在我的场景中,Sheet1 (TOC) 是我想跳过的工作表。那么,我应该这样写代码吗..?

Sub LoopThroughWorkSheets_()

    Dim wb As ThisWorkbook
    Dim ws As Worksheet
    Dim TableName As String
    Dim LstObj As ListObjects

    Set ws = ThisWorkbook.Sheets("Sheet2")

For Each ws In ThisWorkbook.Worksheets
    Select Case ws.Names
    Case Is = "TOC"

    Case Else
        With ws

'Insert Table with the Data starting in Column A, Row 3
    TableName = "MyTable"
    .Range("A3").CurrentRegion.Select
    .ListObjects.Add(xlSrcRange, Selection.CurrentRegion, , xlYes).Name = TableName

'Apply a filter to C3 (Largest to Smallest)
    .AutoFilter.Sort.SortFields.Clear
    .AutoFilter.Sort.SortFields.Add2 Key:=Range("C3"), SortOn:=xlSortOnValues, Order:=xlDescending
    .AutoFilter.ApplyFilter
    .ListObjects("TableName").ShowAutoFilterDropDown = False

'Add a Formula to F4, and Copy Down to Last Row in F (Using Column A as the LastRowData Reference)
    .Range("F4").Formula = "=(E4/(E4-G4))-1"
    .Range("F4").NumberFormat = "0.0%"
    .Range("F4").AutoFill Destination:=Range("F4:F" & Range("A" & Rows.Count).End(xlUp).Row)

'Update Number Format in Column E & G
    .Columns("E").NumberFormat = "$#,##0"
    .Columns("G").NumberFormat = "$#,##0"


        End With
    End Select
Next ws

End Sub

【问题讨论】:

  • 问题是你实际上并没有使用With ws
  • 但是Range("A3").CurrentRegion.Select 是有问题的@ScottHoltzman,因为它将在ActiveSheet 上执行,这可能是ToC。
  • @BigBen - 100% 同意,但这不是 OP 询问的问题。他解决了第一个问题后,将是第二个问题! (哈哈哈)
  • 我的猜测是这是一个 X-Y 问题... OP 认为当前设置应该是执行 X 的方法(即跳过 TOC 表),但实际问题 (Y) 是隐含的和显式ActiveSheets.
  • @BigBen - 好点。我认为你是 100% 正确的。您可以将该代码重构为答案。我正在签字。我太累了,看不到明显的东西。

标签: excel vba


【解决方案1】:

正如 cmets 中所指出的,您没有使用 With ws 语句。我在本月早些时候在another one of your questions 中也看到了这个问题,所以我想我会解释如何正确使用该语句。

考虑这段代码:

Sub WithStatementExample()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")

With ws
    Range("A1").Value = "This cell is A1 and this sheet name is " & ws.Name
    ActiveSheet.Range("A2").Value = "This cell is A2 and this sheet name is " & ws.Name
End With

End Sub 

虽然我们的语句封装在With 语句中,但单元格A1A2 的输出实际上都将在执行代码时处于活动状态的任何工作表上。

例如,考虑这个包含 3 张工作表的新工作簿,所有工作簿都使用默认名称:

如果我们根据该屏幕截图中的工作簿执行代码,结果将出现在我们假设的位置 - Sheet1 单元格 A1A2。但是,如果我们在Sheet3 处于活动状态时运行代码,代码会将值输出到Sheet3 单元格A1A2

这是因为在我们的With 语句中,我们没有使用我们的ws 工作表作为Range 属性的对象限定符。

range 属性可以应用于ApplicationWorksheet 对象以及Application.Range documentation

当不使用对象限定符时,此属性是 ActiveSheet.Range 的快捷方式(它从活动工作表返回一个范围;如果活动工作表不是工作表,则该属性失败)。

要使用 object 获取 With 块中的语句,请在语句前面键入 .

Sub WithStatementExample()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")

With ws
    .Range("A1").Value = "This cell is A1 and this sheet name is " & ws.Name
    .Range("A2").Value = "This cell is A2 and this sheet name is " & ws.Name
End With

End Sub 

现在输出将只转到分配给ws 变量的工作表——在本例中为Sheet1

如果您需要将某些语句应用于其他工作表但在With 块中执行,则最好使用对工作簿的显式引用而不是ActiveSheet - This will help explain more - (How to avoid using select in vba)

因此,如果您的代码中的 ListObjects 实际上位于 Sheets("TOC") 而不是循环所指的 ws,我会将 ActiveSheet.ListObjects...ActiveSheet.ListObjects("Table1")... 更改为:

wb.Sheets("TOC").ListObjects...
wb.Sheets("TOC").ListObjects("Table1")...

在您的 For Each...Next 循环中,您的变量 ws 在这种情况下代表一个对象 - 特别是一个工作表。变量ws 在每次迭代时被分配下一个对象,因此您不需要将工作表对象设置为独立于循环的变量。

在上面的示例中,我有 3 张纸。下面的两个代码块将实现相同的结果,一个通过For Each...Next 循环,另一个没有。

对于每个...下一个方法:

Sub ForEachExample()
Dim ws As Worksheet

For Each ws In ThisWorkbook.Worksheets
    Debug.Print ws.Name
Next ws
End Sub

无循环方法:

Sub NoLoopExample()
Dim ws As Worksheet

Set ws = ThisWorkbook.Worksheets(1)     'The same as Sheets(1) or Sheets("Sheet1")
Debug.Print ws.Name

Set ws = ThisWorkbook.Sheets(2)         'The same as Worksheets(2) or Sheets("Sheet2")
Debug.Print ws.Name

Set ws = ThisWorkbook.Sheets("Sheet3")  'The same as Worksheets(3) or Sheets(3)
Debug.Print ws.Name

End Sub

它们都会在 VBE 的即时窗口中输出以下内容:

Sheet1
Sheet2
Sheet3

在您更新的代码中,您将ws 设置为ThisWorkbook.Sheets("Sheet2")

  1. 您的工作簿中需要有一个名为 "Sheet2" 的工作表,否则您将收到 Runtime Error 9: subscript out of range 错误。
  2. 代码中的下一行是For Each...Next 语句,它将ws 设置为Worksheets 集合中的第一个工作表,而与前一行代码无关。

我要指出的是,这行代码在您当前的过程中是多余的,并且根据您的工作表的名称,可能会导致运行时错误。

请参阅For Each...Next statement 文档。


使用您的Select Case 语句,您的第一行Select Case ws.Names 应该抛出一个Compile Error: Type mismatchws.Names 根据对象限定符(在左侧)返回工作表特定名称的集合 - 在本例中为 ws。因为我们正在专门寻找工作表名称,所以它应该是ws.Name,它返回对象限定符的名称。

因此考虑到这一点,它可能看起来像:

For Each ws In ThisWorkbook.Worksheets
    Select Case ws.Name
        Case "TOC"
            'Do nothing
        Case Else
            'Do your actions here
    End Select
Next ws

作为旁注,您也可以使用If...Else 语句实现相同的目的,如下所示:

For Each ws In ThisWorkbook.Worksheets
    If ws.Name = "TOC" Then    'Note by using = "TOC" must match exactly, including letter casing. 
        'Do nothing
    Else
        'Do your actions here
    End If
Next ws

【讨论】:

  • 非常感谢您花时间解释with ws 声明!在理解对象限定符和其他要求方面,我还有很长的路要走。我尽量不使用Select 或 ActiveSheet, but can’t seem to get the code to execute correctly. Which is probably the issue with the with ws` 语句。您的解释确实帮助我更好地理解with ws 语句......但是,如果我使用`Set ws = ThisWorkbook.Sheets(“Sheet2”),宏怎么知道循环到“Sheet3” - “Sheet6”?跨度>
  • 在我的场景中,Sheet1 (TOC) 是我想跳过的工作表。那么,我应该这样写代码吗..? Sub LoopThroughWorkSheets_() Dim wb As ThisWorkbook Dim ws As Worksheet Dim TableName As String Dim LstObj As ListObjects Set ws = ThisWorkbook.Sheets("Sheet2") For Each ws In ThisWorkbook.Worksheets Select Case ws.Names Case Is = "TOC" Case Else With ws
  • 在 OP 中更新。抱歉,我不知道如何使用新代码插入评论并使其“眼睛友好”
  • 这很有意义!是的 - TOC 是 Sheet1 (TOC)。使用 If...Else 语句,第一个操作 'Insert Table with the Data starting in Column A, Row 3 说明:无效或不合格的参考。我有:For Each ws In ThisWorkbook.Worksheets If ws.Name = "TOC" Then 'Do Nothing Else 'Insert Table with the Data starting in Column A, Row 3 TableName = "MyTable" .ListObjects.Add(xlSrcRange, Range("A3").CurrentRegion, , xlYes).Name = "TableName"
  • 我应该添加For Each ws In ThisWorkbook.Worksheets - If ws.Name = "TOC" Then 'Do Nothing - Else - With ws。这样我就不用担心所有.Range and .Autofilter (s) 显示为无效或不合格的参考?
猜你喜欢
  • 2021-01-17
  • 2020-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多