Как L oop через рабочие листы, кроме TOC - PullRequest
0 голосов
/ 27 апреля 2020

Я пытаюсь создать макрокоманду oop, которая будет проходить по всем рабочим листам, за исключением листа «TO C».
- на каждом рабочем листе будет таблица, созданная, отформатированная и содержащая формулы .

Однако макрос пытается начать на листе «TO C», что приводит к ошибке.

В настоящее время у меня есть:

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`

Обновление ниже, чтобы отразить предложения Сэмюэла Эверсона:

В моем сценарии Sheet1 (TO C) - это рабочий лист, который я хочу пропустить. Итак, я должен написать код таким образом ..?

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

1 Ответ

2 голосов
/ 28 апреля 2020

Как указано в комментариях, вы не используете ваше заявление With ws. Я видел эту проблему в еще одном из ваших вопросов в начале месяца, поэтому я решил дать объяснение, как правильно использовать утверждение.

Рассмотрим этот код:

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, вывод в ячейки A1 и A2 фактически будет на любом листе активен во время выполнения кода.

В качестве примера рассмотрим эту новую книгу с 3 листами, все с именами по умолчанию:

New workbook with 3 blank sheets

Если мы выполняем код на основе В книге на этом скриншоте результаты будут go, где мы предполагаем, что они будут - Sheet1 клеток A1 и A2. Но если мы запускаем код, пока Sheet3 активен, код выводит значения в Sheet3 ячеек A1 и A2.

Example output of code with Sheet3 selected

Это связано с тем, что в нашем операторе With мы не использовали наш лист ws в качестве квалификатора объекта для свойства Range.

Свойство range может применяться как к Application, так и к Worksheet объекты и по документация Application.Range :

При использовании без квалификатора объекта это свойство является ярлыком для ActiveSheet.Range (оно возвращает диапазон из активного лист; если активный лист не является рабочим листом, свойство не выполняется).

Чтобы получить инструкции в блоке 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 

Теперь вывод будет только go на лист, присвоенный переменной ws - в данном случае Sheet1.

Gif showing all from sheet3 to sheet1, demonstrating where the output is

Если вам необходимо применить некоторые утверждения к другим листам но выполненный в вашем блоке With, вам лучше использовать явную ссылку на книгу, а не ActiveSheet - Это поможет объяснить больше - (Как избежать использования select в vba)

Так что, если бы ListObjects в вашем коде было на самом деле Sheets("TOC"), а не ws, на которое ссылается l oop, я бы изменил ActiveSheet.ListObjects... и ActiveSheet.ListObjects("Table1")... на:

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

С вашим For Each...Next l oop ваша переменная ws в этом случае представляет объект - в частности, рабочий лист. Переменной ws присваивается следующий объект на каждой итерации, поэтому вам не нужно независимо присваивать объекту таблицы переменную l oop.

В моем примере выше у меня было 3 листа. Два приведенных ниже кодовых блока достигнут одинакового результата: один через For Each...Next l oop, а другой без.

For Each ... Следующий подход:

Sub ForEachExample()
Dim ws As Worksheet

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

Нет l oop подход:

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 .


С вашим оператором Select Case ваша первая строка Select Case ws.Names должна выбросить Compile Error: Type mismatch. ws.Names возвращает коллекцию заданных c имен листа на основе квалификатора объекта (слева) - в данном случае 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...