Vba excel процесс выполнения

Хитрости »

26 Февраль 2016              56341 просмотров


Отобразить процесс выполнения

Часто при создании кодов в VBA используется обращение к ячейкам, листам, книгам и т.д. и их обработка в циклах. Пара примеров подобных циклов:

  • Просмотреть все файлы в папке — цикл по файлам в папке — Do While sFiles <> «» и For Each objFile In objFolder.Files
  • Массовая замена слов — цикл по ячейкам(массивам) — For lr = 1 To UBound(avArr, 1)
  • Не работают/пропали меню — цикл по всем панелям — For Each cmdBar In Application.CommandBars

Если операция в цикле выполняется за пару секунд — это вполне приемлемо и отражать графически подобные действия нет нужды. Но, если циклы «крутятся» по полчаса — вполне неплохо иметь возможность видеть на какой стадии цикл. Здесь есть один нюанс: циклы могут быть как с заранее известным кол-вом итераций, так и без этого понимания.
Цикл Do While из первого кода статьи Просмотреть все файлы в папке является циклом условия. Т.е. заранее неизвестно сколько файлов будет обработано и следовательно невозможно отразить прогресс выполнения задачи в процентах.
Циклы вроде For Each и For … Next как правило дают возможность определить общее кол-во элементов к обработке, т.к. применяются как правило к коллекциям и объектам, у которых есть свойство .Count. Углубляться в этой статье не стану — это лишь предисловие, чтобы было ясно, почему и зачем далее в статье продемонстрированы разные подходы отображения процесса выполнения.
Отобразить же процесс можно двумя способами:

  • Использование Application.StatusBar
  • Использование UserForm

Использование Application.StatusBar

Самый простой вариант отображения процесса выполнения кода. Он может быть без проблем использован на любом ПК.

Application.StatusBar

— это специальный элемент интерфейса, расположенный в левой нижней части окна Excel и который может показывать дополнительную информацию в зависимости от действий пользователя. Все не раз видели его в работе. Например, после того как мы скопировали ячейки StatusBar покажет нам доп.информацию:
StatusBar
И из VBA есть доступ к этому элементу. Чтобы написать слово привет в StatusBar надо выполнить всего одну строку кода:

Application.StatusBar = "Привет"

Чтобы сбросить значения StatusBar и передать управление им обратно самому Excel необходимо выполнить строку:

Application.StatusBar = False

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

Sub Get_All_File_from_Folder()
    Dim sFolder As String, sFiles As String
    'диалог запроса выбора папки с файлами
    With Application.FileDialog(msoFileDialogFolderPicker)
        If .Show = False Then Exit Sub
        sFolder = .SelectedItems(1)
    End With
    sFolder = sFolder & IIf(Right(sFolder, 1) = Application.PathSeparator, "", Application.PathSeparator)
    'отключаем обновление экрана, чтобы наши действия не мелькали
    Application.ScreenUpdating = False
    sFiles = Dir(sFolder & "*.xls*")
    Do While sFiles <> ""
        'показываем этап выполнения
        Application.StatusBar = "Обрабатывается файл '" & sFiles & "'"
        'открываем книгу
        Workbooks.Open sFolder & sFiles
        'действия с файлом
        'Запишем на первый лист книги в ячейку А1 - www.excel-vba.ru
        ActiveWorkbook.Sheets(1).Range("A1").Value = "www.excel-vba.ru"
        'Закрываем книгу с сохранением изменений
        ActiveWorkbook.Close True 'если поставить False - книга будет закрыта без сохранения
        sFiles = Dir
    Loop
    'возвращаем ранее отключенное обновление экрана
    Application.ScreenUpdating = True
    'сбрасываем значение статусной строки
    Application.StatusBar = False
End Sub

Если запустить код, то перед открытием каждой книги в строке StatusBar будет показано какой именно файл отрывается и обрабатывается. И так с каждым файлом:
Открытие файлов


В случае же с циклами, количество итераций которых есть возможность определить, можно показывать этап выполнения в процентах. Например, цикл по всем выделенным ячейкам:

Sub ShowProgressBar()
    Dim lAllCnt As Long, lr as Long
    Dim rc As Range
    'кол-во ячеек в выделенной области
    lAllCnt = Selection.Count
    'цикл по всем ячейкам в выделенной области
    For Each rc In Selection
        'прибавляем 1 при каждом шаге
        lr = lr + 1
        Application.StatusBar = "Выполнено: " & Int(100 * lr / lAllCnt) & "%"
        DoEvents 'чтобы форма перерисовывалась
    Next
    'сбрасываем значение статусной строки
    Application.StatusBar = False
End Sub

В строке статуса это будет выглядеть так:
Только проценты
Но можно показывать информацию и в чуть более изощренных формах:
Вариант отображения % и блоками-цифрами от 1 до 10(1 = 10% выполнения)
нумерация

Sub StatusBar1()
    Dim lr As Long, lrr As Long, lp As Double
    Dim lAllCnt As Long 'кол-во итераций
    Dim s As String
    lAllCnt = 10000
    'основной цикл
    For lr = 1 To lAllCnt
        lp = lr  100 'десятая часть всего массива
        s = ""
        'формируем строку символов(от 1 до 10)
        For lrr = 10102 To 10102 + lp  10
            s = s & ChrW(lrr)
        Next
        'выводим текущее состояние выполнения
        Application.StatusBar = "Выполнено: " & lp & "% " & s: DoEvents
        DoEvents
    Next
    'очищаем статус-бар от значений после выполнения
    Application.StatusBar = False
End Sub

Вариант отображения % и стрелками ->(1 стрелка = 10% выполнения)
Стрелки

Sub StatusBar2()
    Dim lr As Long, lp As Double
    Dim lAllCnt As Long 'кол-во итераций
    Dim s As String
    lAllCnt = 10000
    For lr = 1 To lAllCnt
        lp = lr  100 'десятая часть всего массива
        'формируем строку символов(от 1 до 10)
        s = String(lp  10, ChrW(10152)) & String(11 - lp  10, ChrW(8700))
        Application.StatusBar = "Выполнено: " & lp & "% " & s: DoEvents
        DoEvents
    Next
    'очищаем статус-бар от значений после выполнения
    Application.StatusBar = False
End Sub

Вариант отображения % и квадратами (кол-во квадратов можно изменять. Если lMaxQuad=20 — каждый квадрат одна 20-я часть всего массива)
Квадраты

Sub StatusBar3()
    Dim lr As Long
    Dim lAllCnt As Long 'кол-во итераций
    Const lMaxQuad As Long = 20 'сколько квадратов выводить
    lAllCnt = 10000
 
    For lr = 1 To lAllCnt
        Application.StatusBar = "Выполнено: " & Int(100 * lr / lAllCnt) & "%" & String(CLng(lMaxQuad * lr / lAllCnt), ChrW(9632)) & String(lMaxQuad - CLng(lMaxQuad * lr / lAllCnt), ChrW(9633))
        DoEvents
    Next
    'очищаем статус-бар от значений после выполнения
    Application.StatusBar = False
End Sub

Этот вариант мне нравится больше всего.
При желании можно сделать и иные варианты — главное найти символы, которые будут показываться и определить их числовой код.


Использование UserForm
Использование стандартного элемента ProgressBar

Для Userform можно использовать стандартный контрол ProgressBar, но я лично не люблю добавлять на формы элементы, которые надо подключать отдельно. Потому как впоследствии контрол может отказаться работать, т.к. нужной версии не окажется на конечном ПК пользователя. Например в моем офисе 2010 для 64-битных систем его нет.
Поэтому про него кратко и в файле примере его нет. Как его создать:

  • создаем UserForm (в меню VBE —InsertUserForm. Подробнее про вставку модулей и форм — Что такое модуль? Какие бывают модули?)
  • отображаем окно конструктора(если не отображено): ViewToolbox
  • далее в меню ToolsAdditional Controls
  • там ищем что-то имеющее в названии ProgressBar и отмечаем его. Жмем Ок.

Теперь в окне Toolbox появится элемент ProgressBar. Просто перетаскиваем его на форму. В свойствах можно задать цвет и стиль отображения полосы прогресса. Останется лишь при необходимости программно показывать форму и задавать для элемента ProgressBar значения минимума и максимума. Примерно это выглядеть будет так:
Практический код
Например, надо обработать все выделенные ячейки. Если форма называется UserForm1, а ProgressBar — ProgressBar1, то код будет примерно такой:

Sub ShowProgressBar()
    Dim lAllCnt As Long
    Dim rc As Range
    'кол-во ячеек в выделенной области
    lAllCnt = Selection.Count
    'показываем форму прогресс-бара
    UserForm1.Show
    UserForm1.ProgressBar1.Min = 1
    UserForm1.ProgressBar1.Max = lAllCnt
    'цикл по всем ячейкам в выделенной области
    For Each rc In Selection
        'прибавляем 1 при каждом шаге
        UserForm1.ProgressBar1.Value = UserForm1.ProgressBar1.Value + 1
        DoEvents 'чтобы форма перерисовывалась
    Next
    'закрываем форму
    Unload UserForm1
End Sub

Использование своего собственного прогресс-бара

Я использую в своих приложениях свой прогресс-бар с процентами. Для этого я использую стандартную UserForm, на которой располагаю два элемента Caption. Первый отвечает за визуальную составляющую в виде синей полосы заполнения, а так же за отображение процентов белыми цифрами на синем фоне. Второй Caption прозрачный и на нем в том же месте, что и у первого, отображаются проценты цифрами, но уже черным шрифтом. В результате в работе это выглядит так:
Мой прогресс-бар

Как использовать эту форму и коды
Первоначально надо скачать файл, приложенный к статье, и в свой проект перенести форму frmStatusBar и модуль mCustomProgressBarModule.
Далее просто внедряем нужные строки в свои коды с циклами:

  • До начала цикла необходимо вызывать процедуру инициализации формы:
    Call Show_PrBar_Or_No(lAllCnt, «Обрабатываю данные…»)
    первым аргументом задается общее кол-во обрабатываемых элементов, а вторым заголовок формы. Если второй аргумент не указан, то по умолчанию будет показан заголовок «Выполнение…». Так же внутри кодов есть кусок кода, отвечающий за минимальное кол-во элементов к обработке. По умолчанию задано 10. Это значит, что если обрабатывается менее 10 ячеек, то форма прогресс-бара показана не будет. Нужно для случаев, когда производятся разные действия над ячейками, но неизвестно сколько их будет. Но зато известно, что с ними будет делать код. Часто для кол-ва ячеек менее 100 нет смысла отображать прогресс выполнения, т.к. это и так секундное дело.
    Чтобы изменить минимальное кол-во достаточно в строке bShowBar = (lCnt > 10) заменить 10 на нужное число.
  • Далее в каждом проходе цикла вызвать перерисовку формы под новое значение цикла:
    If bShowBar Then Call MyProgresBar
  • и в конце не забыть закрыть форму, чтобы не висела:
    If bShowBar Then Unload frmStatusBar

Пример применения формы:

Sub Test_ProgressForm()
    Dim lr As Long
    Dim lAllCnt As Long 'кол-во итераций
    lAllCnt = 10000
    'инициализируем форму прогресс-бара
    Call Show_PrBar_Or_No(lAllCnt, "Обрабатываю данные...")
    'сам цикл
    For lr = 1 To lAllCnt
        If bShowBar Then Call MyProgresBar
    Next
 
    'закрываем форму, если она была показана
    If bShowBar Then Unload frmStatusBar
End Sub

Так же все описанные примеры и коды можно найти в приложенном файле:
Скачать пример:

  Tips_ShowProgressBar.xls (79,0 KiB, 6 153 скачиваний)


Статья помогла? Поделись ссылкой с друзьями!

  Плейлист   Видеоуроки


Поиск по меткам



Access
apple watch
Multex
Power Query и Power BI
VBA управление кодами
Бесплатные надстройки
Дата и время
Записки
ИП
Надстройки
Печать
Политика Конфиденциальности
Почта
Программы
Работа с приложениями
Разработка приложений
Росстат
Тренинги и вебинары
Финансовые
Форматирование
Функции Excel
акции MulTEx
ссылки
статистика

Прогресс-бар на VBA

Данный прогресс-бар позволяет отображать ход выполнения любого макроса.

Для использования этого индикатора перетащите из файла-примера в свой файл модуль класса ProgressIndicator и форму F_Progress

Использовать прогресс бар сравнительно просто — достаточно добавить в макрос несколько строк кода:

Sub ПростейшийПримерИспользованияПрогрессБара()
    Dim pi As New ProgressIndicator    ' создаём новый прогресс-бар
    pi.Show "Подождите, работает макрос"    ' отбражаем индикатор

    ' здесь код вашего макроса

    pi.Hide    ' закрываем индикатор
End Sub
Sub ПримерИспользованияПрогрессБара()
 
    КоличествоЗапусковВнешнегоМакроса = 3000
 
    Dim pi As New ProgressIndicator    ' создаём новый прогресс-бар
    pi.Show "Форматирование ячеек"    ' отбражаем индикатор

    ' первое действие (на шкале индикатора от 0 до 95 процентов) - это окраска ячеек
    pi.StartNewAction 0, 95, "Окраска ячеек", , , КоличествоЗапусковВнешнегоМакроса
 
 
 
    ' цикл с вызовом внешнего макроса "ФорматированиеЯчейки"
    For i = 1 To КоличествоЗапусковВнешнегоМакроса
 
        ' инициируем очередное действие в индикаторе
        pi.SubAction , "Обрабатывается ячейка $index из $count", "$time"
 
        ' собственно, код цикла
        ФорматированиеЯчейки i
    Next
 
    ' всё покрасили - теперь пора чистить ячейки )
    pi.StartNewAction 95, 100, "Очистка ячеек"
    Cells.Clear
 
    pi.Hide    ' закрываем индикатор
End Sub
 
Sub ФорматированиеЯчейки(ByVal n As Long)    ' вызываемый макрос
    Cells(n).Interior.ColorIndex = 15: Cells(n).BorderAround xlContinuous
End Sub

Давайте рассмотрим подробнее работу с индикатором.

Прогресс-бар выполнен в виде модуля класса, поэтому, для начала работы с ним,
надо прежде всего создать экземпляр этого класса
:

Dim pi As New ProgressIndicator    ' создаём новый прогресс-бар

Итак, прогресс-бар создан, и теперь надо его отобразить.

Для этого мы используем метод Show объекта типа ProgressIndicator:

pi.Show "Форматирование ячеек"    ' отбражаем индикатор

При использовании метода Show мы сразу задаём заголовок индикатора (можно здесь указать название вашего макроса)

Индикатор появился на экране — но полоса не отображается, ибо процент выполнения по-умолчанию равен нулю.

Для каждого действия мы будем задавать начальный и конечный процент выполнения задачи

К примеру, если первое действие вашего макроса занимает по времени примерно пятую часть от времени выполнения всего макроса,
то мы укажем интервал для индикатора от 0% до 20%:

Как вы заметили, для запуска очередного действия используется метод StartNewAction объекта ProgressIndicator.

При вызове этого метода можно сразу задать текст для каждой из 3 текстовых строк индикатора:

pi.StartNewAction 0, 20, "Текст первой строки", "Текст строки 2", "Текст строки 3"

Если действие состоит из нескольких отдельных «поддействий», то можно также сразу задать и количество этих «поддействий»

(например, основное действие — это форматирование ячеек (от 0% до 20% индикатора), а поддействия — это окраска отдельных ячеек (первая строка — от 0% до 1% индикатора, вторая строка — от 1% до 2%, и т.д.))

Чтобы нам не мучиться с расчётами этих процентов, мы просто задаём количество действий (например, количество форматируемых ячеек, равное 3000),
и индикатор сам разделит диапазон от 0% до 20% на 3000 равных частей, плавно увеличивая длину полосы индикатора по мере форматирования отдельных ячеек.

 pi.StartNewAction 0, 20, "Окраска ячеек", , , 3000

Чтобы уведомить индикатор об очередном «поддействии» внутри цикла, мы используем метод SubAction объекта ProgressIndicator

' инициируем очередное действие в индикаторе
        pi.SubAction , "Обрабатывается ячейка $index из $count", "$time"

Как вы могли заметить, мы задали только значение второй и третьей строки индикатора, не указав никакого текста для первой строки.

В этом случае (если значения некоторых из 3 строк индикатора не заданы), эти строки не изменяются
(в первой строке индикатора останется текст, заданный ранее при использовании метода StartNewAction)

Кроме того, в тексте для строк индикатора можно использовать следующие ключевые слова:

  • $index и $count — для вывода строк типа «Обрабатывается ячейка 515 из 3000«,
  • $time — для вывода ожидаемого времени до окончания макроса

(макрос анализирует текущий процент выполнения и затраченное время, и предсказывает, сколько времени осталось до окончания всех действий)

Если же необходимо просто увеличить длину полоски индикатора — можете использовать метод SubAction без параметров:

Вы можете выводить сколько угодно действий в индикаторе, причем совсем не обязательно, чтобы начальный процент очередного действия был равен конечному проценту предыдущего.

Вполне допустим следующий код:

    pi.StartNewAction 5, 20, "Действие 1"    ' начинаем не с нуля
    pi.StartNewAction 20, 50, "Действие 2"    ' начинается сразу после предыдущего
    pi.StartNewAction 35, 60, "Действие 3"    ' начинается раньше предыдущего
    pi.StartNewAction 85, 90, "Действие 4"    ' начинается через время после предыдущего
    pi.StartNewAction 10, 100, "Действие 5"    ' начинаем почти всё сначала

По окончании макроса желательно закрыть прогресс бар:

pi.Hide    ' закрываем индикатор

У объекта ProgressIndicator имеется много различных свойств и методов.

Вкратце расскажу о некоторых свойствах:

  • свойство Caption позволяет задать новый заголовок индикатора
  • свойство FP позволяет получить доступ к отображаемой форме (и всем её элементам управления)
    (например, код pi.FP.PrintForm выведет индикатор на печать)
  • свойства Line1Line2 и Line3 позволяют в любом месте кода задать текст конкретной строки индикатора
  • свойства ShowPercents и ShowTime включают или выключают отображение процента выполнения и времени в заголовке индикатора
    (по умолчанию оба свойства имеют значение TRUE, т.е. в заголовке отображается и время, и процент выполнения макроса)

Из функций объекта мы рассмотрим только одну: AddChildIndicator

Эта функция создаёт дочерний прогресс бар, и отображает его выше или ниже родительского:

' создаём дочерний индикатор, и выводим его ниже основного
    Dim pi2 As ProgressIndicator
    Set pi2 = pi.AddChildIndicator("Раскрашивание ячеек", 1)

При изменении процента выполнения в дочернем индикаторе пропорционально меняется и процент выполнения главного (родительского) прогресс-бара.

В прикреплённом файле, помимо модуля класса и формы индикатора,
присутствует также стандартный модуль с  несколькими примерами использования прогресс-бара.

На индикаторе присутствует кнопка «Отмена» — её нажатие вызывает останов всех запущенных макросов
(выполняется команда End, останавливающая все макросы, и обнуляющая все переменные)

Поскольку у этой кнопки свойство Cancel установлено в TRUE, нажатие на клавиатуре клавиши ESC равносильно нажатию кнопки «Отмена»
(при нажатии Esc макрос останавливается)


добавлено 23.02.2012

Новая версия прогресс-бара — с поддержкой отображения лога на индикаторе, и возможностью отображения лога в виде текстового файла.

новая версия прогресс-бара

Высоту текстового поля с логом можно изменять:

вывод лога макроса на индикаторе выполнения

Новая  версия индикатора, и примеры его использования — во втором прикреплённом файле.


PS: Идеальный прогресс-бар должен работать так  :)

  • 115744 просмотра

Не получается применить макрос? Не удаётся изменить код под свои нужды?

Оформите заказ у нас на сайте, не забыв прикрепить примеры файлов, и описать, что и как должно работать.

Ранее я рассмотрел методы создания пользовательских форм и основы работы с ними (если вы никогда не работали с пользовательскими формами, рекомендую для начала прочитать указанную заметку). В настоящей заметке показано использование индикатора текущего состояния – графического «измерителя», который отображает текущее состояние выполняемой задачи, например, долго работающего макроса.[1]

Рис. 1. В окне UserForm отображается ход выполнения макроса

Скачать заметку в формате Word или pdf, примеры в архиве

Мы рассмотрим три метода создания индикаторов текущего состояния:

  • Макрос, который запускается за пределами диалогового окна UserForm (отдельный индикатор текущего состояния).
  • Макрос, который запускается из диалогового окна UserForm. При этом в диалоговом окне UserForm используется элемент управления MultiPage для отображения индикатора текущего состояния, пока выполняется другой макрос.
  • Макрос, который запускается из диалогового окна UserForm. При этом высота диалогового окна UserForm увеличивается, а индикатор текущего состояния отображается в нижней части окна.

При использовании индикатора текущего состояния необходимо знать, насколько завершено текущее задание. Способы получения этой информации различаются в зависимости от типа выполняемого макроса. Например, если макрос записывает данные в ячейки (и количество таких ячеек известно), то остается создать код, который будет подсчитывать процентное отношение количества ячеек, содержащих данные. Даже если невозможно точно оценить, насколько далеко «зашел» макрос, пользователю небезынтересно будет узнать, что макрос еще выполняется и Excel не завис.

Отображение индикатора текущего состояния в строке состояния окна

Простой способ отображения хода выполнения макроса — использование строки состояния Excel. Его преимущество — простота реализации. Недостатком же является то, что большинство пользователей не привыкли отслеживать информацию, которая отображается в строке состояния окна, поскольку предпочитают просматривать ее в отдельном окне.

Для отображения сообщения в строке состояния используется следующий оператор:

Application.StatusBar = "Пожалуйста, подождите…"

Можно обновлять строку состояния в процессе выполнения макроса. Например, если в макросе используется переменная Pet, которая представляет состояние задачи, можно создать код, который будет периодически выполнять следующий оператор:

Application.StatusBar = "Выполнение… "&Pet&"% завершено"

После завершения макроса нужно вернуть строку состояния к прежнему виду. Для этого используется следующий оператор:

Application.StatusBar = False

Если строка состояния не возвращена к прежнему виду, продолжает отображаться завершающее сообщение. Помните о том, что индикатор текущего состояния замедляет выполнение макроса, поскольку обновление индикатора требует дополнительного использования системных ресурсов. Если быстродействие макроса превыше всего, от использования индикатора текущего состояния лучше отказаться.

Создание отдельного индикатора текущего состояния

Такой индикатор не инициализируется путем отображения формы UserForm. Следующий макрос очищает рабочий лист, и записывает 20 тысяч случайных чисел в диапазон ячеек (см. также файл progress indicatorl.xlsm).

Sub GenerateRandomNumbers()

   Вставка случайных чисел на активный лист

    Const RowMax As Long = 500

    Const ColMax As Long = 40

    Dim r As Long, c As Long

    If TypeName(ActiveSheet) <> «Worksheet» Then Exit Sub

    Cells.Clear

    For r = 1 To RowMax

        For c = 1 To ColMax

            Cells(r, c) = Int(Rnd * 1000)

        Next c

    Next r

End Sub

После небольшого изменения макроса (описанного в следующем разделе) диалоговое окно UserForm отображает индикатор процесса выполнения макроса (рис. 1).

Создание диалогового окна UserForm, включающего индикатор текущего состояния

Выполните следующие шаги:

  1. Вставьте новое диалоговое окно UserForm и измените значение свойства Caption на Ход выполнения процесса.
  2. Добавьте элемент управления Frame и присвойте ему имя FrameProgress.
  3. Добавьте элемент управления Label в состав элемента управления Frame и назначьте ему имя LabelProgress. Удалите заголовок этого элемента управления, а также сделайте его фон красным (посредством свойства BackColor). На данный момент размеры и расположение этого элемента управления не важны.
  4. Добавьте еще один элемент управления Label над элементом управления Frame, с помощью которого вы будете описывать происходящее (необязательно). В нашем примере с помощью этого элемента управления добавляется надпись Ход выполнения процесса.
  5. Настройте диалоговое окно UserForm и элементы управления таким образом, чтобы они выглядели, как на рис. 2.

Рис. 2. Окно формы UserForm может играть роль индикатора хода выполнения процесса

Можно изменить тип форматирования элементов управления. Например, свойство SpecialEffect элемента Frame таким образом, чтобы последний стал «вдавленным».

Создание процедур обработки событий. Важно, чтобы процедура автоматически запускалась при отображении диалогового окна UserForm. Один из вариантов подразумевает использование события Initialize. Но это событие возникает еще до того, как диалоговое окно отображается на экране, поэтому такой вариант не подходит. С другой стороны, событие Activate возникает в тот момент, когда диалоговое окно UserForm отображается на экране, поэтому в данном случае можно остановиться на его использовании.

Вставьте следующую процедуру в модуль кода диалогового окна UserForm. Эта процедура всего лишь вызывает процедуру GenerateRandomNumbers, когда диалоговое окно UserForm отображается на экране. Процедура GenerateRandomNumbers, которая хранится в модуле кода VBA, является фактическим макросом, который будет работать, пока на экране отображается индикатор текущего состояния.

Private Sub UserForm_activate()

    Call GenerateRandomNumbers

End Sub

В процедуре GenerateRandomNumber имеется дополнительный модуль, который отслеживает текущее состояние, сохраняя соответствующие сведения в переменной PctDone.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

Sub GenerateRandomNumbers()

   Вставка случайных чисел на активный лист

    Dim Counter As Long

    Const RowMax As Long = 500

    Const ColMax As Long = 40

    Dim r As Long, c As Long

    Dim PctDone As Double

    If TypeName(ActiveSheet) <> «Worksheet» Then Exit Sub

    Cells.Clear

    Counter = 1

    For r = 1 To RowMax

        For c = 1 To ColMax

            Cells(r, c) = Int(Rnd * 1000)

            Counter = Counter + 1

        Next c

        PctDone = Counter / (RowMax * ColMax)

        Call UpdateProgress(PctDone)

    Next r

    Unload UserForm1

End Sub

Процедура GenerateRandomNumbers включает два цикла. Во внутреннем цикле вызывается процедура UpdateProgress, которая принимает только один аргумент (переменная PctDone, которая «ответственна» за отображение процесса выполнения макроса). Эта переменная может принимать значения от 0 до 100.

Sub UpdateProgress(Pct)

    With UserForm1

        .FrameProgress.Caption = Format(Pct, «0%»)

        .LabelProgress.Width = Pct * (.FrameProgress.Width 10)

        .Repaint

    End With

End Sub

Создание процедуры запуска. Для отображения диалогового окна UserForm введите следующий код в модуль VBA.

Sub ShowUserForm()

    With UserForm1

        ‘Использование цвета темы рабочей книги

        .LabelProgress.BackColor = ActiveWorkbook.Theme. _

            ThemeColorScheme.Colors(msoThemeAccent1)

        .LabelProgress.Width = 0

        .Show

    End With

End Sub

Можно сделать так, чтобы строка состояния соответствовала текущей теме рабочей книги. Для этого в процедуру ShowUserForm добавьте следующий оператор.

.LabelProgress.BackColor = ActiveWorkbook.Theme. _
ThemeColorScheme.Colors(msoThemeAccentl)

При выполнении процедуры ShowUserForm ширина объекта Label устанавливается равной 0. После этого вызывается метод Show объекта UserForm1, что приводит к отображению диалогового окна UserForm (которое играет роль индикатора текущего состояния). Когда диалоговое окно UserForm отображается на экране, вызывается событие Activate, которое приводит к выполнению процедуры GenerateRandomNurabers. Процедура GenerateRandomNumbers включает код, который вызывает процедуру UpdateProgress при каждом изменении переменной счетчика цикла r. Обратите внимание, что процедура UpdateProgress использует метод Repaint объекта UserForm. Если бы этого оператора не было, изображение на экране не обновлялось бы. Перед завершением процедуры GenerateRandomNumbers ее последний оператор выгружает диалоговое окно UserForm из памяти.

Для того чтобы модифицировать эту методику, необходимо разобраться, как определяется процент завершения выполняемой задачи. После этого значение состояния задачи можно будет присваивать переменной PctDone. Определение этой величины осуществляется различными способами в зависимости от приложения. Если код выполняется в цикле (как в данном примере), вычисление процента выполнения сравнительно несложное. Если же код выполняется не в цикле, определение состояния выполнения происходит в различных точках кода.

Отображение сведений о текущем состоянии с помощью элемента управления MultiPage

В предыдущем примере макрос запускался не из диалогового окна UserForm. Во многих случаях долго выполняющийся макрос можно вынудить «уйти со сцены», щелкнув мышью на кнопке ОК в диалоговом окне UserForm. В этом же разделе будет описан лучший способ, использование которого возможно при следующих допущениях:

  • проект завершен и отлажен;
  • в проекте используется диалоговое окно UserForm (без элемента управления MultiPage) для запуска долго выполняющегося макроса;
  • существует метод оценки степени завершения выполняемой задачи.

Как и в предыдущем примере, в рабочий лист вводятся случайные числа. Отличие заключается в том, что в приложении содержится диалоговое окно UserForm, в котором пользователем определяется количество строк и столбцов для ввода случайных чисел (рис. 3; см. также файл progress indicator2.xlsm).

Рис. 3. Пользователь определяет количество строк и столбцов, в которые вводятся случайные числа

Изменение диалогового окна. На данном этапе предполагается, что диалоговое окно UserForm уже настроено. Нам осталось добавить элемент управления MultiPage. Первая страница элемента управления MultiPage будет содержать все первоначальные элементы управления. На второй странице располагаются элементы управления, которые используются для отображения индикатора текущего состояния. Когда макрос начнет выполняться, в коде VBA значение свойства Value элемента управления MultiPage изменится. В результате будут скрыты исходные элементы управления и отображены элементы управления, которые используются для создания индикатора текущего состояния.

Первым шагом будет добавление элемента управления MultiPage в диалоговое окно UserForm. После этого необходимо переместить все существующие в диалоговом окне UserForm элементы управления на первую страницу элемента управления MultiPage. Затем следует активизировать вторую страницу элемента управления MultiPage и настроить ее так, чтобы она выглядела, как показано на рис. 4. В данном случае используется та же комбинация элементов управления, что и в предыдущем примере.

  1. Добавьте элемент управления Frame, присвоив ему имя FrameProgress.
  2. Добавьте элемент управления Label в состав элемента управления Frame, присвоив ему имя LabelProgress. Удалите заголовок этого элемента управления, а также сделайте его фоновый цвет красным.
  3. Добавьте еще один элемент управления Label, описывающий суть происходящего (необязательно).
  4. Активизируйте элемент управления MultiPage в целом (а не отдельную вкладку), а свойству Style присвойте значение 2 – fmTabStyleNone. (Это приведет к сокрытию всех вкладок.) Возможно, придется изменить размер элемента управления MultiPage, чтобы учесть скрытые вкладки.

Рис. 4. Вторая вкладка элемента управления MultiPage, которая применяется для отображения индикатора текущего состояния

Простейший способ выделения элемента управления MultiPage, когда вкладки скрыты, — выбор его в раскрывающемся списке, который находится в окне Properties (подчеркнут красной линией на рис. 4). Для выбора определенной страницы укажите величину свойства Value для элемента MultiPage: 0 — для Page1, 1 — для Page2 и т.д.

Вставка процедуры UpdateProgress. Вставьте следующую процедуру в модуль кода диалогового окна UserForm.

Sub UpdateProgress(Pct)

    With UserForm1

        .FrameProgress.Caption = Format(Pct, «0%»)

        .LabelProgress.Width = Pct * (.FrameProgress.Width 10)

    End With

    DoEvents

End Sub

Процедура UpdateProgress вызывается из макроса после щелчка пользователем на кнопке ОК и выполняет обновление индикатора текущего состояния.

Далее необходимо модифицировать процедуру, которая выполняется после щелчка на кнопке ОК. Данная процедура выступает обработчиком события Click и называется OKButton_Click. Для начала необходимо вставить оператор в начало процедуры:

MultiPage1.Value = 1

Этот оператор приводит к активизации второй вкладки элемента управления MultiРаgе (страницы, на которой отображается индикатор текущего состояния). На следующем шаге необходимо самостоятельно создать код, который будет вычислять степень выполнения задачи. Полученное значение следует присвоить переменной PctDone. Скорее всего, расчеты будут производиться внутри цикла. После этого нужно добавить приведенный ниже оператор, который будет обновлять индикатор текущего состояния:

Call UpdateProgress(PctDone)

Отображение индикатора текущего состояния без применения элемента управления MultiPage

Методика, рассмотренная в этом разделе, немного проще предыдущей, так как не требует применения элемента управления MultiPage. Вместо этого индикатор текущего состояния содержится в нижней части диалогового окна UserForm. Для того чтобы элементы управления, составляющие индикатор текущего состояния, изначально не были видны, высота диалогового окна UserForm была уменьшена до соответствующего размера. Как только нужно будет отобразить индикатор текущего состояния, высота диалогового окна UserForm увеличится, что сделает индикатор видимым на экране (см. файл progress indicator3.xlsm).

На рис. 5 показано диалоговое окно UserForm в редакторе VBE. Свойство Height диалогового окна UserForm имеет значение 172. Но перед тем как отобразить диалоговое окно UserForm, значение свойства Height устанавливается равным 124 (это приводит к тому, что элементы управления, представляющие индикатор текущего состояния, становятся невидимыми). Как только пользователь щелкнет на кнопке ОК, код VBA изменит значение свойства Height на 172. Для этого используется следующий оператор:

Me.Height = 172

Рис. 5. Уменьшение высоты диалогового окна UserForm приводит к сокрытию элементов управления, образующих индикатор текущего состояния

На рис. 6 показано диалоговое окно UserForm с отображенным индикатором текущего состояния.

Рис. 6. Индикатор текущего состояния в действии

[1] По материалам книги Джон Уокенбах. Excel 2010. Профессиональное программирование на VBA. – М: Диалектика, 2013. – С. 477–484.

I know this is an old thread but I had asked a similar question not knowing about this one. I needed an Excel VBA Progress Bar and found this link: Excel VBA StatusBar. Here is a generalized version that I wrote. There are 2 methods, a simple version DisplaySimpleProgressBarStep that defaults to ‘[|| ] 20% Complete’ and a more generalized version DisplayProgressBarStep that takes a laundry list of optional arguments so that you can make it look like just about anything you wish.

    Option Explicit
    
    ' Resources
    '   ASCII Chart: https://vbaf1.com/ascii-table-chart/
    
    Private Enum LabelPlacement
        None = 0
        Prepend
        Append
    End Enum
    
    #If VBA7 Then
     Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr) 'For 64 Bit Systems
    #Else
     Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'For 32 Bit Systems
    #End If
    
    Public Sub Test()
        Call ProgressStatusBar(Last:=10)
    End Sub
    
    Public Sub Test2()
    Const lMilliseconds As Long = 500
    Dim lIndex As Long, lNumberOfBars As Long
    Dim sBarChar As String
        sBarChar = Chr$(133) ' Elipses …
        sBarChar = Chr$(183) ' Middle dot ·
        sBarChar = Chr$(176) ' Degree sign °
        sBarChar = Chr$(171) ' Left double angle «
        sBarChar = Chr$(187) ' Right double angle »
        sBarChar = Chr$(166) ' Broken vertical bar ¦
        sBarChar = Chr$(164) ' Currency sign ¤
        sBarChar = Chr$(139) ' Single left-pointing angle quotation mark ‹
        sBarChar = Chr$(155) ' Single right-pointing angle quotation mark ›
        sBarChar = Chr$(149) ' Bullet •
        sBarChar = "|"
        
        For lIndex = 1 To 10
            Call DisplayProgressBarStep(lIndex, 10, 50, LabelPlacement.Append, sBarChar)
            Call Sleep(lMilliseconds)
        Next
        Call MsgBox("Status bar test completed.", vbOKOnly Or vbInformation, "Test2 Completed")
        Call DisplayProgressBarStep(lIndex, 10, bClearStatusBar:=True)
    End Sub
    
    Public Sub Test2Simple()
    Const lMilliseconds As Long = 500
    Dim lIndex As Long, lNumberOfBars As Long
        For lIndex = 1 To 10
            Call DisplayProgressBarStep(lIndex, 10, 50)
            Call Sleep(lMilliseconds)
        Next
        Call MsgBox("Status bar test completed.", vbOKOnly Or vbInformation, "Test2Simple Completed")
        Call DisplayProgressBarStep(lIndex, 10, bClearStatusBar:=True)
    End Sub
    
    ''' <summary>
    ''' Method to display an Excel ProgressBar. Called once for each step in the calling code process.
    ''' Defaults to vertical bar surrounded by square brackets with a trailing percentage label (e.g. [|||||] 20% Complete).
    '''
    ''' Adapted
    ''' From: Excel VBA StatusBar
    ''' Link: https://www.wallstreetmojo.com/vba-status-bar/
    ''' </summary>
    ''' <param name="Step">The current step count.</param>
    ''' <param name="StepCount">The total number of steps.</param>
    ''' <param name="NumberOfBars">Optional, Number of bars displayed for StepCount. Defaults to StepCount. The higher the number, the longer the string.</param>
    ''' <param name="LabelPlacement">Optional, Can be None, Prepend or Append. Defaults to Append.</param>
    ''' <param name="BarChar">Optional, Character that makes up the horizontal bar. Defaults to | (Pipe).</param>
    ''' <param name="PrependedBoundaryText">Optional, Boundary text prepended to the StatusBar. Defaults to [ (Left square bracket).</param>
    ''' <param name="AppendedBoundaryText">Optional, Boundary text appended to the StatusBar. Defaults to ] (Right square bracket).</param>
    ''' <param name="ClearStatusBar">Optional, True to clear the StatusBar. Defaults to False.</param>
    Private Sub DisplayProgressBarStep( _
        lStep As Long, _
        lStepCount As Long, _
        Optional lNumberOfBars As Long = 0, _
        Optional eLabelPlacement As LabelPlacement = LabelPlacement.Append, _
        Optional sBarChar As String = "|", _
        Optional sPrependedBoundaryText As String = "[", _
        Optional sAppendedBoundaryText As String = "]", _
        Optional bClearStatusBar As Boolean = False _
        )
    Dim lCurrentStatus As Long, lPctComplete As Long
    Dim sBarText As String, sLabel As String, sStatusBarText As String
        If bClearStatusBar Then
            Application.StatusBar = False
            Exit Sub
        End If
        
        If lNumberOfBars = 0 Then
            lNumberOfBars = lStepCount
        End If
        lCurrentStatus = CLng((lStep / lStepCount) * lNumberOfBars)
        lPctComplete = Round(lCurrentStatus / lNumberOfBars * 100, 0)
        sLabel = lPctComplete & "% Complete"
        sBarText = sPrependedBoundaryText & String(lCurrentStatus, sBarChar) & Space$(lNumberOfBars - lCurrentStatus) & sAppendedBoundaryText
        Select Case eLabelPlacement
            Case LabelPlacement.None: sStatusBarText = sBarText
            Case LabelPlacement.Prepend: sStatusBarText = sLabel & " " & sBarText
            Case LabelPlacement.Append: sStatusBarText = sBarText & " " & sLabel
        End Select
        Application.StatusBar = sStatusBarText
        ''Debug.Print "CurStatus:"; lCurrentStatus, "PctComplete:"; lPctComplete, "'"; sStatusBarText; "'"
    End Sub
    
    ''' <summary>
    ''' Method to display a simple Excel ProgressBar made up of vertical bars | with a trailing label. Called once for each step in the calling code process.
    '''
    ''' Adapted
    ''' From: Excel VBA StatusBar
    ''' Link: https://www.wallstreetmojo.com/vba-status-bar/
    ''' </summary>
    ''' <param name="Step">The current step count.</param>
    ''' <param name="StepCount">The total number of steps.</param>
    ''' <param name="NumberOfBars">Optional, Number of bars displayed for StepCount. Defaults to StepCount. The higher the number, the longer the string.</param>
    ''' <param name="ClearStatusBar">Optional, True to clear the StatusBar. Defaults to False.</param>
    Private Sub DisplaySimpleProgressBarStep( _
        lStep As Long, _
        lStepCount As Long, _
        Optional lNumberOfBars As Long = 0, _
        Optional bClearStatusBar As Boolean = False _
        )
        Call DisplayProgressBarStep(lStep, lStepCount, lNumberOfBars, bClearStatusBar:=bClearStatusBar)
    End Sub

Данный прогресс-бар позволяет отображать ход выполнения любого макроса.

Для использования этого индикатора перетащите из файла-примера в свой файл модуль класса ProgressIndicator и форму F_Progress

Использовать прогресс бар сравнительно просто — достаточно добавить в макрос несколько строк кода:

Sub ПростейшийПримерИспользованияПрогрессБара()

Dim pi As New ProgressIndicator ‘ создаём новый прогресс-бар

pi.Show «Подождите, работает макрос» ‘ отбражаем индикатор

‘ здесь код вашего макроса

pi.Hide ‘ закрываем индикатор

End Sub

Sub ПримерИспользованияПрогрессБара()

КоличествоЗапусковВнешнегоМакроса = 3000

Dim pi As New ProgressIndicator ‘ создаём новый прогресс-бар

pi.Show «Форматирование ячеек» ‘ отбражаем индикатор

‘ первое действие (на шкале индикатора от 0 до 95 процентов) — это окраска ячеек

pi.StartNewAction 0, 95, «Окраска ячеек», , , КоличествоЗапусковВнешнегоМакроса

‘ цикл с вызовом внешнего макроса «ФорматированиеЯчейки»

For i = 1 To КоличествоЗапусковВнешнегоМакроса

‘ инициируем очередное действие в индикаторе

pi.SubAction , «Обрабатывается ячейка $index из $count», «$time»

‘ собственно, код цикла

ФорматированиеЯчейки i

Next

‘ всё покрасили — теперь пора чистить ячейки )

pi.StartNewAction 95, 100, «Очистка ячеек»

Cells.Clear

pi.Hide ‘ закрываем индикатор

End Sub

Sub ФорматированиеЯчейки(ByVal n As Long) ‘ вызываемый макрос

Cells(n).Interior.ColorIndex = 15: Cells(n).BorderAround xlContinuous

End Sub

Давайте рассмотрим подробнее работу с индикатором.

Прогресс-бар выполнен в виде модуля класса, поэтому, для начала работы с ним,
надо прежде всего создать экземпляр этого класса
:

Dim pi As New ProgressIndicator ‘ создаём новый прогресс-бар

Итак, прогресс-бар создан, и теперь надо его отобразить.

Для этого мы используем метод Show объекта типа ProgressIndicator:

pi.Show «Форматирование ячеек» ‘ отбражаем индикатор

При использовании метода Show мы сразу задаём заголовок индикатора (можно здесь указать название вашего макроса)

Индикатор появился на экране — но полоса не отображается, ибо процент выполнения по-умолчанию равен нулю.

Для каждого действия мы будем задавать начальный и конечный процент выполнения задачи

К примеру, если первое действие вашего макроса занимает по времени примерно пятую часть от времени выполнения всего макроса,
то мы укажем интервал для индикатора от
0% до 20%:

pi.StartNewAction 0, 20

Как вы заметили, для запуска очередного действия используется метод StartNewAction объекта ProgressIndicator.

При вызове этого метода можно сразу задать текст для каждой из 3 текстовых строк индикатора:

pi.StartNewAction 0, 20, «Текст первой строки», «Текст строки 2», «Текст строки 3»

Если действие состоит из нескольких отдельных «поддействий», то можно также сразу задать и количество этих «поддействий»

(например, основное действие — это форматирование ячеек (от 0% до 20% индикатора), а поддействия — это окраска отдельных ячеек (первая строка — от 0% до 1% индикатора, вторая строка — от 1% до 2%, и т.д.))

Чтобы нам не мучиться с расчётами этих процентов, мы просто задаём количество действий (например, количество форматируемых ячеек, равное 3000),
и индикатор сам разделит диапазон от
0% до 20% на 3000 равных частей, плавно увеличивая длину полосы индикатора по мере форматирования отдельных ячеек.

pi.StartNewAction 0, 20, «Окраска ячеек», , , 3000

Чтобы уведомить индикатор об очередном «поддействии» внутри цикла, мы используем метод SubAction объекта ProgressIndicator

‘ инициируем очередное действие в индикаторе

pi.SubAction , «Обрабатывается ячейка $index из $count», «$time»

Как вы могли заметить, мы задали только значение второй и третьей строки индикатора, не указав никакого текста для первой строки.

В этом случае (если значения некоторых из 3 строк индикатора не заданы), эти строки не изменяются
(в первой строке индикатора останется текст, заданный ранее при использовании метода
StartNewAction)

Кроме того, в тексте для строк индикатора можно использовать следующие ключевые слова:

  • $index и $count — для вывода строк типа «Обрабатывается ячейка 515 из 3000«,
  • $time — для вывода ожидаемого времени до окончания макроса

(макрос анализирует текущий процент выполнения и затраченное время, и предсказывает, сколько времени осталось до окончания всех действий)

Если же необходимо просто увеличить длину полоски индикатора — можете использовать метод SubAction без параметров:

pi.SubAction

Вы можете выводить сколько угодно действий в индикаторе, причем совсем не обязательно, чтобы начальный процент очередного действия был равен конечному проценту предыдущего.

Вполне допустим следующий код:

pi.StartNewAction 5, 20, «Действие 1» ‘ начинаем не с нуля

pi.StartNewAction 20, 50, «Действие 2» ‘ начинается сразу после предыдущего

pi.StartNewAction 35, 60, «Действие 3» ‘ начинается раньше предыдущего

pi.StartNewAction 85, 90, «Действие 4» ‘ начинается через время после предыдущего

pi.StartNewAction 10, 100, «Действие 5» ‘ начинаем почти всё сначала

По окончании макроса желательно закрыть прогресс бар:

pi.Hide ‘ закрываем индикатор

У объекта ProgressIndicator имеется много различных свойств и методов.

Вкратце расскажу о некоторых свойствах:

  • свойство Caption позволяет задать новый заголовок индикатора
  • свойство FP позволяет получить доступ к отображаемой форме (и всем её элементам управления)
    (например, код pi.FP.PrintForm выведет индикатор на печать)
  • свойства Line1Line2 и Line3 позволяют в любом месте кода задать текст конкретной строки индикатора
  • свойства ShowPercents и ShowTime включают или выключают отображение процента выполнения и времени в заголовке индикатора
    (по умолчанию оба свойства имеют значение TRUE, т.е. в заголовке отображается и время, и процент выполнения макроса)

Из функций объекта мы рассмотрим только одну: AddChildIndicator

Эта функция создаёт дочерний прогресс бар, и отображает его выше или ниже родительского:

‘ создаём дочерний индикатор, и выводим его ниже основного

Dim pi2 As ProgressIndicator

Set pi2 = pi.AddChildIndicator(«Раскрашивание ячеек», 1)

При изменении процента выполнения в дочернем индикаторе пропорционально меняется и процент выполнения главного (родительского) прогресс-бара.

В прикреплённом файле, помимо модуля класса и формы индикатора,
присутствует также стандартный модуль с  несколькими примерами использования прогресс-бара.

На индикаторе присутствует кнопка «Отмена» — её нажатие вызывает останов всех запущенных макросов
(выполняется команда
End, останавливающая все макросы, и обнуляющая все переменные)

Поскольку у этой кнопки свойство Cancel установлено в TRUE, нажатие на клавиатуре клавиши ESC равносильно нажатию кнопки «Отмена»
(при нажатии Esc макрос останавливается)


добавлено 23.02.2012

Новая версия прогресс-бара — с поддержкой отображения лога на индикаторе, и возможностью отображения лога в виде текстового файла.

Высоту текстового поля с логом можно изменять:

Новая  версия индикатора, и примеры его использования — во втором прикреплённом файле.


PS: Идеальный прогресс-бар должен работать так  :)

Вложение

Размер

Загрузки

Последняя загрузка

ProgressIndicator.xls

103.5 КБ

62

4 дня 11 часов назад

ProgressIndicatorNew.xls

131.5 КБ

60

2 недели 4 дня назад

  • 80702 просмотра

Понравилась статья? Поделить с друзьями:
  • Vba excel процедура в процедуре
  • Vba excel протянуть формулу по столбцу
  • Vba excel пространство имен
  • Vba excel пропустить ошибку
  • Vba excel пропуск ошибок