Запись макроса и чтение кода

Записываем макрос мышкой, открываем и построчно читаем полученный код — и переписываем его так, как написал бы человек.

Макрорекордер — встроенный «диктофон» Excel: он записывает ваши действия и переводит их в код VBA. Отличный переводчик — и очень плохой программист.

Зачем вообще записывать, если можно писать руками

Рекордер — это ваш словарь. Вы не знаете, как на языке VBA будет «покрасить шапку в голубой» или «поставить формат даты»? Включите запись, сделайте это мышкой, выключите запись — и посмотрите, что получилось. Excel сам напишет вам нужное свойство. Так VBA учат все: не зубрят справочник, а подсматривают у рекордера.

Но у рекордера есть слепая зона: он записывает движения, а не намерения. Он видит «пользователь выделил колонку D, потом задал формат», а не «нужен формат даты у колонки с датами». Отсюда весь мусор в записанном коде — и отсюда же наша работа по его чистке.

Шаг 1. Включаем вкладку «Разработчик»

По умолчанию она скрыта. Откройте Файл → Параметры → Настроить ленту и поставьте галочку Разработчик в правом списке. На вкладке появятся кнопки «Запись макроса», «Макросы» и «Visual Basic».

Ещё удобнее — кнопка записи в левом нижнем углу строки состояния Excel (маленький квадратик рядом со словом «Готово»).

Шаг 2. Записываем

Возьмём ту самую понедельничную выгрузку: сверху три служебные строки, в колонке D даты выглядят как текст, шапка серая и невыразительная.

Жмём Разработчик → Запись макроса. Появится диалог:

  • Имя макроса — латиницей, без пробелов. CleanExport лучше, чем Макрос1. Имя нельзя начинать с цифры.
  • Сочетание клавиш — можно оставить пустым. Осторожно: Ctrl+S или Ctrl+C вы перезапишете для всей книги, и коллеги вас не поймут.
  • Сохранить в — «Эта книга» (макрос уедет вместе с файлом) или «Личная книга макросов» (макрос будет доступен вам во всех файлах, но только на вашем компьютере).
  • Описание — одно предложение о том, что делает макрос. Напишите его. Правда.

Нажимаем ОК и делаем работу мышкой: выделяем строки 1–3, удаляем; выделяем колонку D, задаём формат даты; выделяем шапку A1:F1, делаем жирной и красим. Затем Разработчик → Остановить запись.

Шаг 3. Читаем, что получилось

Нажимаем Alt+F11 — открывается редактор VBA. Слева в дереве проекта находим Modules → Module1, двойной клик. Внутри примерно такое:

Sub CleanExport()
'
' CleanExport Макрос
' Чистка еженедельной выгрузки
'
    Rows("1:3").Select
    Selection.Delete Shift:=xlUp
    Columns("D:D").Select
    Selection.NumberFormat = "dd.mm.yyyy"
    Range("A1:F1").Select
    Selection.Font.Bold = True
    With Selection.Interior
        .Pattern = xlSolid
        .PatternColorIndex = xlAutomatic
        .Color = 16247773
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With
    Range("A1").Select
End Sub

Выглядит страшно, но читается легко, если знать три слова.

  • Rows("1:3").Select — «выделить строки 1–3». Это буквально ваш клик по заголовкам строк.
  • Selection — «то, что сейчас выделено». Рекордер всё делает через выделение, потому что вы так делали.
  • Selection.Delete Shift:=xlUp — «удалить выделенное, оставшиеся ячейки сдвинуть вверх».
  • With ... End With — «дальше речь про этот объект»: вместо того чтобы писать Selection.Interior пять раз, пишем один раз, а внутри — только точку и свойство.
  • .Color = 16247773 — цвет заливки числом. Читать это невозможно, но Excel так думает.

Заметьте: последняя строка Range("A1").Select появилась только потому, что вы в конце кликнули на A1. Макросу это не нужно совсем.

Почему записанный код почти всегда плохой

У него три родовые травмы.

1. Select и Activate — это лишние клики

Человеку нужно сначала выделить ячейку, чтобы что-то с ней сделать. Коду — не нужно. Он может обратиться к диапазону напрямую. Каждый .Select заставляет Excel реально перерисовать экран, прокрутить лист, обновить строку состояния. На тысяче операций это превращает секунду в минуту.

2. Он привязан к активному листу

Rows("1:3").Delete без указания листа означает «на том листе, который сейчас открыт». Запустили макрос, случайно стоя на листе «Итоги года», — и он снёс три верхние строки не там. Это самая частая катастрофа новичков.

3. В нём много шума

Рекордер записывает все свойства диалога, даже те, которые вы не трогали: PatternColorIndex, TintAndShade, PatternTintAndShade. Они ничего не меняют — просто засоряют код.

Чиним: тот же макрос, но по-человечески

Sub CleanExport()
    ' Чистка еженедельной выгрузки: убрать шапку системы,
    ' привести даты к нормальному виду, оформить заголовки.

    Dim ws As Worksheet
    Set ws = ThisWorkbook.Worksheets("Выгрузка")

    ws.Rows("1:3").Delete Shift:=xlUp
    ws.Columns("D").NumberFormat = "dd.mm.yyyy"

    With ws.Range("A1:F1")
        .Font.Bold = True
        .Interior.Color = RGB(221, 235, 247)
    End With
End Sub

Что изменилось:

  • Dim ws As Worksheet — «заведи ярлычок ws, в нём будет лист». Dim — от dimension, объявление переменной.
  • Set ws = ThisWorkbook.Worksheets("Выгрузка") — «пусть ws означает лист «Выгрузка» из этой книги». Теперь макрос работает с нужным листом, даже если открыт другой. Set нужен именно для объектов (лист, диапазон, книга), для чисел и текста он не пишется.
  • Все обращения начинаются с ws. — то есть «на этом листе». Никаких «на том, что сейчас открыт».
  • Select исчез полностью. Мы не выделяем — мы сразу действуем.
  • RGB(221, 235, 247) вместо магического числа: три числа — красный, зелёный, синий, от 0 до 255. Это можно прочитать.

Код стал вдвое короче, в разы быстрее и, главное, безопаснее.

Шпаргалка перевода

Записал рекордерКак пишет человек
Range("A1").Select затем Selection.Value = 10ws.Range("A1").Value = 10
Sheets("Отчёт").Activate затем Range("B2").Font.Bold = Truews.Range("B2").Font.Bold = True
Columns("D:D").Select затем Selection.ColumnWidth = 12ws.Columns("D").ColumnWidth = 12
ActiveCell.Valuews.Range("A1").Value — конкретный адрес вместо «где курсор»

Правило-мантра: увидели .Select или .Activate — почти всегда их можно удалить, приклеив то, что было в следующей строке после Selection., прямо к объекту.

Как это работает

Рекордер слушает не мышь, а сам Excel. Каждый ваш клик интерфейс превращает в вызов объектной модели, и рекордер записывает эти вызовы в модуль. Он не знает контекста: не знает, что колонка D — «дата отгрузки», не знает, что три верхние строки — служебная шапка выгрузки. Поэтому он пишет буквально и с запасом.

Ещё одна важная кнопка в момент записи — «Относительные ссылки». По умолчанию рекордер пишет абсолютные адреса: вы кликнули A1 — он записал Range("A1"), и макрос всегда будет лезть в A1. Включите относительные ссылки — и он запишет действия относительно текущей ячейки (ActiveCell.Offset(1, 0) — «на строку ниже»). Это удобно, когда действие нужно повторять в разных местах листа.

И главное ограничение: рекордер не умеет думать. Он не запишет цикл («пройди по всем строкам») и условие («если сумма пустая — подсвети»). Это уже пишется руками — и выглядит так:

Sub HighlightEmpty()
    Dim ws As Worksheet
    Dim i As Long
    Dim lastRow As Long

    Set ws = ThisWorkbook.Worksheets("Выгрузка")
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

    For i = 2 To lastRow
        If ws.Cells(i, "C").Value = "" Then
            ws.Cells(i, "C").Interior.Color = RGB(255, 199, 206)
        End If
    Next i
End Sub

Здесь lastRow — номер последней заполненной строки (тот же приём, что Ctrl+↑ из самого низа таблицы), For i = 2 To lastRow — «повтори для каждой строки со второй по последнюю», а If ... Then — условие. Именно за этим и переходят от рекордера к настоящему коду.

Частые ошибки

  • Оставлять всё как записалось. Такой макрос медленный и однажды «почистит» не тот лист. Пятиминутная чистка кода окупается годами спокойствия.
  • Запускать макрос, стоя не на том листе. Прямое следствие отсутствия ws. перед каждым обращением.
  • Вешать макрос на Ctrl+C или Ctrl+V. Вы сломаете привычные сочетания для всех, кто откроет книгу.
  • Записывать один длинный макрос на всю рутину. Лучше записать 3–4 коротких куска: их проще прочитать, проверить и переиспользовать.
  • Забывать про ThisWorkbook. ActiveWorkbook — «книга, которая сейчас впереди», и она может оказаться чужой. ThisWorkbook — «книга, в которой лежит сам макрос», это почти всегда то, что вам нужно.

Итоги

  • Рекордер — это словарь: не знаете, как что-то называется в VBA, — запишите действие и посмотрите код.
  • Записанный код почти всегда плох: он полон Select/Activate, привязан к активному листу и забит лишними свойствами.
  • Чинится это тремя приёмами: завести Dim ws As Worksheet, привязать его через Set ws = ThisWorkbook.Worksheets("…"), и обращаться к диапазонам напрямую через ws., удалив все .Select.
  • Циклы (For … Next) и условия (If … Then) рекордер записать не может — с них начинается настоящее программирование.
  • ThisWorkbook надёжнее, чем ActiveWorkbook; конкретный адрес надёжнее, чем ActiveCell.
Проверьте себя
1. Рекордер записал две строки: Range("B2").Select и Selection.Font.Bold = True. Как это переписать правильно?
AActiveCell.Font.Bold = True
Bws.Range("B2").Font.Bold = True
CSelection.Range("B2").Font.Bold = True
DТак и оставить — Select нужен, иначе Excel не поймёт, к какой ячейке применить свойство
2. Что из перечисленного макрорекордер записать НЕ сможет?
AЗаливку шапки цветом и жирный шрифт
BУдаление строк и установку формата даты
CЦикл по всем строкам с условием «если ячейка пустая — подсветить»
DВключение автофильтра на диапазоне