Запись макроса и чтение кода
Записываем макрос мышкой, открываем и построчно читаем полученный код — и переписываем его так, как написал бы человек.
Макрорекордер — встроенный «диктофон» 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 = 10 | ws.Range("A1").Value = 10 |
Sheets("Отчёт").Activate затем Range("B2").Font.Bold = True | ws.Range("B2").Font.Bold = True |
Columns("D:D").Select затем Selection.ColumnWidth = 12 | ws.Columns("D").ColumnWidth = 12 |
ActiveCell.Value | ws.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.