Лучше ли показывать пользовательские формы ProgressBar в VBA как модальные или немодальные?
Лучше ли показывать пользовательские формы ProgressBar в VBA как модальные или немодальные? Каковы лучшие практики для разработки индикаторов прогресса в VBA?
Немодальные пользовательские формы требуют использования Application.Interactive = False
тогда как модальные пользовательские формы по самой своей природе блокируют любое взаимодействие с приложением, пока основная процедура не завершится или не будет отменена.
Если Application.Interactive = False
используется, однако, клавиша Esc прерывает выполнение кода, поэтому использование Application.EnableCancelKey = xlErrorHandler
и обработка ошибок (Err.Number = 18
) требуется как в пользовательской форме, так и в вызывающей процедуре.
Ресурсоемкие процедуры вызова также могут привести к CommandButton_Click
а также UserForm_Activate
события осечки в немодальных пользовательских формах.
В целом, индикаторы выполнения, которые используют модальные пользовательские формы, кажутся более простыми, потому что выполняемый код полностью содержится в модуле UserForm, и для передачи переменных меньше необходимости.
Однако проблема с использованием модальных пользовательских форм для индикаторов прогресса заключается в том, что для каждой процедуры, для которой требуется индикатор выполнения, требуется отдельный модуль UserForm, поскольку вызывающая процедура должна находиться внутри процедуры UserForm_Activate.
Таким образом, хотя в немодальной пользовательской форме можно использовать один индикатор прогресса многократного использования, он будет менее надежным, чем выполнение кода из нескольких модальных пользовательских форм.
Какой способ лучше?
Спасибо!
5 ответов
Я собираюсь закрыть это и сказать, что Модал победитель. Я пробовал оба способа, но в итоге вы пытаетесь закрыть слишком много лазеек с немодальными пользовательскими формами. Модал сложнее, потому что он более строгий, но он побуждает вас разбивать код на более мелкие куски, что в любом случае лучше в долгосрочной перспективе.
Там также третий способ, используя Application.StatusBar
, Вы даже можете имитировать истинный индикатор выполнения, используя последовательность символов U+25A0 и U+25A1.
Определенно Модал. Если вы собираетесь использовать Modeless, вам следует запускать его в отдельном потоке вне процесса, а не в главном потоке Excel.exe.
Я думаю, что первоначальная тема заслуживает ответа, так как вопрос был сформулирован так хорошо, что Google находит его первым.
Раздел 1 - Теория
Первое, что нужно сказать, это то, что перевести переменные между модулями совсем не сложно.
Единственное, что вам нужно сделать, это создать отдельный модуль и поместить туда все глобальные переменные. Тогда вы сможете прочитать их везде во всех формах, листах, модулях.
Второе, что окно должно быть немодальным. Почему так? Ответ заключается в том, чтобы сохранить мобильность кода, т.е.
- функция, в которой выполняется самый обычный процесс, не должна находиться в модуле UserForm
- Вы можете вызвать окно с индикатором прогресса отовсюду и
- единственная связь между обычной функцией / процедурой - глобальные переменные
Это большое преимущество, чтобы быть универсальным здесь.
Раздел 2 - Практика
1) Создайте модуль "Декларация" с глобальными переменными:
Public StopForce As Integer 'эта переменная будет использоваться как индикатор того, что пользователь нажал кнопку отмены
Публичный PCTDone As Single 'это% работы, которая уже была сделана
Public CurrentFile As String 'любой другой параметр, который мы хотим передать в форму.
2) Создать форму с помощью кнопки. В событии OnClick кнопки должен быть код, где мы ссылаемся на глобальную переменную StopForce в модуле декларации
Private Sub CommandButton1_Click()
Declaration.StopForce = 1
End Sub
3) Добавьте одну процедуру, где вы обновляете индикатор выполнения
Sub UpdateProgressBar(PCTDone_in As Single)
With UserForm1
' Update the Caption property of the Frame control.
.FrameProgress.Caption = Format(PCTDone_in, "0%")
' Widen the Label control.
.LabelProgress.Width = PCTDone_in * _
(.FrameProgress.Width)
' Display the current file from global variable
.Label1.Caption = Declaration.CurrentFile
End With
End Sub
4) в любом другом модуле мы должны иметь функции или процедуру / подпрограмму, в которой выполняется процедура:
For i=1 to All_Files
Declaration.CurrentFile = myFiles (i)
FormFnc.UpdateProgressBar (i / .Range("C11").Value)
DoEvents
If Declaration.StopForce = 1 Then
GoTo 3
End If
Next i
На самом деле у вас есть следующие свойства, в результате чего плюсы / минусы в зависимости от ваших потребностей:
Type | Impact on UI | Impact on caller execution
----------|--------------|-----------------------------
Modal | Blocked | Blocked until Form is closed
Modeless | Not blocked | Continues
Если вы хотите заблокировать пользовательский интерфейс и позволить звонящему продолжить, то вам нужно открыть форму в модальном режиме с помощью Application.OnTime
,