Хитрости »
24 Февраль 2012 61062 просмотров
Как отменить действия макроса
Многие из тех, кто программирует в VBA знают, что после действий макроса пропадает возможность отмены действий. И если с отменой тех действий, которые были совершены до выполнения макроса совершенно точно можно распрощаться(невозможно будет это сделать), то отменить действия макроса возможно. И рано или поздно каждый программирующий в VBA задается вопросом: как можно отменить действия, совершенные макросом? Для начала надо понять, в каких ситуациях это нам надо. Например был выполнен код, который испортил или удалил данные в файле, но эти данные еще нужны. Самое простое, что можно сделать это закрыть файл без сохранения и открыть заново. Все данные будут на месте(если, конечно, в коде не было строки, сохраняющей файл). Второй способ: это перед выполнением макроса делать резервную копию файла — тогда Ваши исходные данные всегда будут целы.
Но как же сделать отмену действий макроса через стандартную кнопку на панели или сочетанием клавиш
Ctrl
+
Z
и можно ли? Ответ — можно. Но сразу вопрос: а насколько это нужно? В каких ситуациях это может пригодиться? Я навскидку сразу не сказал бы, если бы не являлся разработчиком программ и надстроек в среде Microsoft Excel. Именно в надстройках отмена действий наиболее востребована, на мой взгляд. Например надстройка объединяет ячейки. Объединили случайно и…В стандартной ситуации после такого макроса нельзя отменить действия. Закрывать файл без сохранения? Как-то некрасиво получается, если продукт является коммерческим. И тогда приходится извращаться и пытаться сделать возможным отмену действий макроса. В моей надстройке MulTEx в некоторых командах отмена действий команд как раз и применяется. Наиболее распространенное решение по отмене действий макроса заключается в запоминании свойств изменяемых ячеек:
'Создаем свой пользовательский тип данных Type SaveRange vFormula As Variant sAddr As String lColor As Long lColorIndex As Long End Type 'Переменные для запоминания данных Public wbWBook As Excel.Workbook Public wsSh As Excel.Worksheet Public vOldVals() As SaveRange '--------------------------------------------------------------------------------------- ' Procedure : Fill_Numbers ' Purpose : Основная процедура. Это тот код, который вносит изменения на лист ' и действия которого нам необходимо отменить ' Процедура заполняет выделенные ячейки номерами ' и изменяет цвет заливки '--------------------------------------------------------------------------------------- Sub Fill_Numbers() Dim rCell As Range, li As Long ' Сначала запоминаем значения выделенных ячеек на листе ReDim vOldVals(1 To Selection.Count) 'Запоминаем активную книгу 'это на случай, если отмена действий будет производиться из другой книги Set wbWBook = ActiveWorkbook 'Запоминаем активный лист 'на случай, если отмена действий будет производиться из другого листа Set wsSh = ActiveSheet 'Запоминаем значения(заносим в массив) li = 1 For Each rCell In Selection 'запоминаем адрес ячейки vOldVals(li).sAddr = rCell.Address 'запоминаем формулу(если нет формулы - значение) vOldVals(li).vFormula = rCell.Formula 'запоминаем цвет заливки ячейки vOldVals(li).lColor = rCell.Interior.Color 'запоминаем индекс цвета заливки(чтобы на заливать бесцветные ячейки) vOldVals(li).lColorIndex = rCell.Interior.ColorIndex li = li + 1 Next rCell '====================================== 'Выполняем основные действия(собственно тот код, который надо будет отменить) li = 1 For Each rCell In Selection rCell = li rCell.Interior.ColorIndex = li li = li + 1 Next rCell '====================================== 'Назначаем стандартному вызову отмены действий выполнение нашего макроса возвращения значений Application.OnUndo "Отменить заполнение ячеек номерами", "Restore_Vals" End Sub '--------------------------------------------------------------------------------------- ' Procedure : Restore_Vals ' Purpose : Процедура отмены действия(возврат значений) '--------------------------------------------------------------------------------------- Sub Restore_Vals() Dim li As Long 'В случае непредвиденной ошибки переходим на метку 'и показываем сообщение об ошибке On Error GoTo Erreble 'Активируем книгу, в которой были сделаны изменения wbWBook.Activate 'Активируем лист, в котором были сделаны изменения wsSh.Activate 'Возвращаем значения For li = 1 To UBound(vOldVals) Range(vOldVals(li).sAddr).Formula = vOldVals(li).vFormula 'если заливка была безцветная, то ColorIndex = xlNone 'значит выставляем безцветность If Not vOldVals(li).lColorIndex = xlNone Then Range(vOldVals(li).sAddr).Interior.Color = vOldVals(li).lColor Else 'если цвет был - возвращаем его Range(vOldVals(li).sAddr).Interior.ColorIndex = xlNone End If Next li Exit Sub 'Показываем сообщение о невозможности отмены действия Erreble: MsgBox "Нельзя отменить действие!", vbCritical, "Error" End Sub
Комментарии к коду я старался сделать максимально подробными, поэтому думаю, что больше нечего разъяснять. К тому же по древней традиции я приложил к статье пример с данным кодом 
Скачать пример

Код, приведенный выше, несомненно хорош, но если кол-во изменяемых ячеек достаточно велико, то код будет очень замедлять работу. Поэтому если есть возможность добавлять/удалять листы в книгах, то можно схитрить: сделать резервную копию листа, лист сделать очень скрытым и как только потребуется отмена действия — вернуть этот лист, удалив исходный(с уже испорченными данными):
'Переменные для запоминания данных Public wbWBook As Workbook Public wsSh As Worksheet, wsActSh As Worksheet, sSh_Name As String, lShPoz As Long '--------------------------------------------------------------------------------------- ' Procedure : Fill_Numbers ' Purpose : Основная процедура. Это тот код, который вносит изменения на лист ' и действия которого нам необходимо отменить ' Процедура заполняет выделенные ячейки номерами ' и изменяет цвет заливки '--------------------------------------------------------------------------------------- Sub Fill_Numbers() Dim rCell As Range, li As Long 'Запоминаем активную книгу 'это на случай, если отмена действий будет производиться из другой книги Set wbWBook = ActiveWorkbook 'Запоминаем активный лист 'на случай, если отмена действий будет производиться из другого листа Set wsActSh = ActiveSheet lShPoz = wsActSh.Index sSh_Name = wsActSh.Name Application.ScreenUpdating = 0 wsActSh.Copy , wbWBook.Sheets(wbWBook.Sheets.Count) Set wsSh = wbWBook.Sheets(wbWBook.Sheets.Count) wsSh.Visible = xlVeryHidden wsActSh.Activate Application.ScreenUpdating = 1 '====================================== 'Выполняем основные действия(собственно тот код, который надо будет отменить) li = 1 For Each rCell In Selection rCell = li rCell.Interior.ColorIndex = li li = li + 1 Next rCell '====================================== 'Назначаем стандартному вызову отмены действий выполнение нашего макроса возвращения значений Application.OnUndo "Отменить заполнение ячеек номерами", "Restore_Vals" End Sub '--------------------------------------------------------------------------------------- ' Procedure : Restore_Vals ' Purpose : Процедура отмены действия(возврат значений) '--------------------------------------------------------------------------------------- Sub Restore_Vals() 'В случае непредвиденной ошибки переходим на метку 'и показываем сообщение об ошибке On Error GoTo Erreble Application.ScreenUpdating = 0 'Активируем книгу, в которой были сделаны изменения wbWBook.Activate 'делаем видимым резервный лист wsSh.Visible = -1 'Удаляем исходный лист, данные в котором уже изменены Application.DisplayAlerts = 0 wsActSh.Delete Application.DisplayAlerts = 1 'назначаем резервному листу имя исходного wsSh.Name = sSh_Name wsSh.Move wbWBook.Sheets(lShPoz) 'Активируем резервный лист wsSh.Activate Application.ScreenUpdating = 0 Exit Sub 'Показываем сообщение о невозможности отмены действия Erreble: MsgBox "Нельзя отменить действие!", vbCritical, "www.excel-vba.ru" End Sub
Скачать пример

Конечно, в этом приеме тоже есть недостаток — если на этот лист ссылаются формулы из других листов есть большой шанс получить в этих формулах ошибку
#ССЫЛКА!(#REF!)
, т.к. исходный лист удаляется.
В этом случае можно из резервного листа копировать все ячейки и вставлять на рабочий лист. Да и вообще можно много чего придумать — вплоть до сохранения и последующего извлечения резервных копий файлов. Все как всегда зависит от задач и ситуации.
И самая большая ложка дегтя к обоим кодам: VBA не позволяет таким образом делать МНОГОКРАТНЫЕ отмены действий(многократное Ctrl+Z), что делает бесполезными попытки запомнить пошагово несколько разных изменений макросами. Отменить можно будет все равно только последнее запомненное действие.
Статья помогла? Поделись ссылкой с друзьями!
Видеоуроки
Поиск по меткам
Access
apple watch
Multex
Power Query и Power BI
VBA управление кодами
Бесплатные надстройки
Дата и время
Записки
ИП
Надстройки
Печать
Политика Конфиденциальности
Почта
Программы
Работа с приложениями
Разработка приложений
Росстат
Тренинги и вебинары
Финансовые
Форматирование
Функции Excel
акции MulTEx
ссылки
статистика
|
Jack Famous Пользователь Сообщений: 10852 OS: Win 8.1 Корп. x64 | Excel 2016 x64: | Browser: Chrome |
#1 24.03.2022 14:21:59 Приветствую! Чтобы окно появилось, нужно во время работы макроса (несколько секунд) нажать Esc
Опытным путём было установлено, что в самой функции перехватить дебаговое окно нельзя (или я что-то не так делал), зато можно перехватить в основной процедуре и правильно его обработать sokol92, ещё раз большое спасибо! Изменено: Jack Famous — 24.03.2022 14:51:56 Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄ |
||
|
sokol92 Пользователь Сообщений: 4445 |
2. Application.EnableCancelKey В частности, при значении xlErrorHandler при нажатии пользователем Ctrl+Break возникнет ошибочная ситуация с кодом ошибки 18, которую можно обработать в макросе по On Error. Изменено: sokol92 — 24.03.2022 14:48:24 |
|
Jack Famous Пользователь Сообщений: 10852 OS: Win 8.1 Корп. x64 | Excel 2016 x64: | Browser: Chrome |
#3 24.03.2022 14:34:13 sokol92, спасибо! Смотрю…
то, что нужно! Пробую… Изменено: Jack Famous — 24.03.2022 14:35:55 Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄ |
||
|
sokol92 Пользователь Сообщений: 4445 |
#4 24.03.2022 14:49:28 Для остановки работы макроса нужно нажимать Ctrl+Break (поправил).
Изменено: sokol92 — 24.03.2022 14:53:23 Владимир |
||
|
Jack Famous Пользователь Сообщений: 10852 OS: Win 8.1 Корп. x64 | Excel 2016 x64: | Browser: Chrome |
#5 24.03.2022 14:53:31
при DoEvents (нужно для корректного отображения прогресса в статус-баре) достаточно Esc Изменено: Jack Famous — 24.03.2022 14:53:44 Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄ |
||
|
RAN Пользователь Сообщений: 7091 |
#6 24.03.2022 17:39:35
А меня давно интересует вопрос, как быть, если в клаве ноута клавиша «Break» отсутствует в принципе? |
||
|
sokol92 Пользователь Сообщений: 4445 |
Это к медвежатникам. |
|
Jack Famous Пользователь Сообщений: 10852 OS: Win 8.1 Корп. x64 | Excel 2016 x64: | Browser: Chrome |
RAN, в отдельной теме лучше обсудить — к моей отношения не имеет Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄ |
|
RAN Пользователь Сообщений: 7091 |
Jack Famous, пытался…. Изменено: RAN — 24.03.2022 18:09:39 |
|
Jack Famous Пользователь Сообщений: 10852 OS: Win 8.1 Корп. x64 | Excel 2016 x64: | Browser: Chrome |
RAN, оставь тут ссылку, друже-котяра Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄ |
|
RAN Пользователь Сообщений: 7091 |
|
|
БМВ Модератор Сообщений: 21383 Excel 2013, 2016 |
#12 24.03.2022 19:30:45
тогда надо взять инструкцию и посомтреть нет ли сочетания с FN. что обычно делается. Часто она совмещается с PAUSE но при нажатой FN . По вопросам из тем форума, личку не читаю. |
||
|
Jack Famous, Программисты — это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете! |
|
|
Jack Famous Пользователь Сообщений: 10852 OS: Win 8.1 Корп. x64 | Excel 2016 x64: | Browser: Chrome |
#14 25.03.2022 08:44:04
о, экспертное мнение подъехало
Изменено: Jack Famous — 25.03.2022 09:04:25 Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄ |
|||
|
Отмена действий макроса |
||||||||
Ответить |
||||||||
Ответить |
||||||||
Ответить |
||||||||
Ответить |
||||||||
Ответить |
||||||||
Ответить |
||||||||
Ответить |
The short answer is you can’t Undo (I assume this is what you mean by revoke) the actions of a macro. The decisions that take place inside the programming language could be troublesome for Excel to reverse, so there is a blanket «Can’t Undo» state induced by kicking off the macro.
The long answer is that if you anticipate Undo being required, your code should accommodate that request by storing the previous state of the data before execution. This is a very broad assumption that you are working with data in VBA; direct file creation and manipulation could be trickier depending on what’s executed.
John Walkenbach gives a good example of storing data to provide a future Undo facility:
Computer users are accustomed to the ability to «undo» an operation. Almost every operation you perform in Excel can be undone. If you program in VBA, you may have wondered if it’s possible to undo the effects of a subroutine. The answer is yes. The qualified answer is it’s not always easy.
John Walkenbach, Spreadsheetpage.com
I think his opening statement is a little misleading though: for non-coding users the answer is «no, you can’t undo a macro».
|
Заблокирован |
|
|
1 |
|
Отмена действий, выполненных макросом17.11.2009, 20:32. Показов 19322. Ответов 31
Ситуация: применил макрос. Макрос содержит 5 команд. Затем я понял, что применил макрос по ошибке.
0 |
|
Профессор 7 / 7 / 0 Регистрация: 30.10.2009 Сообщений: 28 |
||||||||||||||||
|
17.11.2009, 21:05 |
2 |
|||||||||||||||
|
Можно немного модифицировать сам макрос допустим он у нас такой (здесь и далее для Word).
Откатить (сделать Undo) можно на сколько угодно действий, но нам нужно знать сколько действий сделал макрос. Модифицируем наш макрос
и добавляем еще один для отката действий произведенных нашим макросом.
и еще добавляем глобальную переменную
Запуск макроса DoUndo откатывает все действия произведенные нашим макросом
1 |
|
Заблокирован |
|
|
17.11.2009, 21:08 [ТС] |
3 |
|
Спасибо.
0 |
|
3895 / 898 / 122 Регистрация: 16.04.2009 Сообщений: 1,824 |
|
|
18.11.2009, 11:01 |
4 |
|
А я пред запуском таких макросов просто сохраняюсь и если понимаю что чего-то не срослось то закрываю файл без сохранения и открываю опять т.к. не все действия макроса могут быть отменены (Undo).
1 |
|
Заблокирован |
|
|
18.11.2009, 19:33 [ТС] |
5 |
|
Профессор,
0 |
|
БурундукЪ 10027 / 2616 / 84 Регистрация: 17.02.2009 Сообщений: 10,364 |
||||
|
18.11.2009, 19:49 |
6 |
|||
|
так я ж случайно запускаю его. поставь в начале каждого макроса
0 |
|
Заблокирован |
|
|
18.11.2009, 19:57 [ТС] |
7 |
|
CyberБурундукЪ,
0 |
|
10027 / 2616 / 84 Регистрация: 17.02.2009 Сообщений: 10,364 |
|
|
18.11.2009, 19:59 |
8 |
|
выскакивания диалогового окна при каждом использовании макроса — это плохо. я знаю, что плохо. зато надежно. особенно от
так я ж случайно запускаю его. Не по теме: :sorry: если ни чем не помог
0 |
|
Заблокирован |
|
|
18.11.2009, 20:03 [ТС] |
9 |
|
CyberБурундукЪ,
0 |
|
7 / 7 / 0 Регистрация: 30.10.2009 Сообщений: 28 |
|
|
18.11.2009, 22:50 |
10 |
|
Профессор, Вписывать код можно тоже автоматизированно в форму вставляем textbox и запускаем простую программу которая сама код впишет.
0 |
|
|
|
19.11.2009, 07:30 [ТС] |
|
Не по теме: Профессор,
0 |
|
БурундукЪ |
|
19.11.2009, 10:30
|
|
Не по теме: Long (8 байт) на порядок лучше чем Integer (4 байта), точнее в 2 раза длиннее
0 |
|
Заблокирован |
|
|
19.11.2009, 22:11 [ТС] |
13 |
|
CyberБурундукЪ,
0 |
|
10027 / 2616 / 84 Регистрация: 17.02.2009 Сообщений: 10,364 |
|
|
19.11.2009, 22:25 |
14 |
|
Busine2009, нет. просто, когда много памяти используется (например, массивы огромные) то нужно все учитывать и применять не Long, а Integer, там где этого хватает.
0 |
|
0 / 0 / 0 Регистрация: 28.11.2009 Сообщений: 9 |
|
|
28.11.2009, 14:09 |
15 |
|
Профессор, что-то совсем ничего сделать не могу
0 |
|
Заблокирован |
|
|
28.11.2009, 14:16 [ТС] |
16 |
|
leshuk,
0 |
|
0 / 0 / 0 Регистрация: 28.11.2009 Сообщений: 9 |
|
|
28.11.2009, 14:20 |
17 |
|
Busine2009, у меня есть выборка данных. Те данные которые мне не нужны я удаляю построчно из таблицы с помощью макроса.
0 |
|
Заблокирован |
|
|
28.11.2009, 14:23 [ТС] |
18 |
|
Т.е. ты хочешь применить макрос, приведённый в этой теме, и у тебя не получается?
0 |
|
0 / 0 / 0 Регистрация: 28.11.2009 Сообщений: 9 |
|
|
28.11.2009, 14:25 |
19 |
|
ага. я вообще взял этот пример из двух макросов и ничего не получилось
0 |
|
Заблокирован |
|
|
28.11.2009, 14:33 [ТС] |
20 |
|
Вот непосредственно сам макрос, который отменяет действия: Код Sub DoUndo() While uc > 0 ActiveDocument.Undo uc = uc - 1 Wend End Sub Сверху него вставь вот этот код (объявление переменной, видимой во всём проекте): В начало макроса, который нужно отменить вставь это: А для отмены команд вставь вот наподобие этого: Код .InsertAfter ("cyberforum ")[B][COLOR="Red"]: uc = uc + 1[/COLOR][/B]
Красным помечено то, что будет отменять, слева просто команда для примера.
1 |
|
IT_Exp Эксперт 87844 / 49110 / 22898 Регистрация: 17.06.2006 Сообщений: 92,604 |
28.11.2009, 14:33 |
|
20 |



