É melhor mostrar UserForms ProgressBar no VBA como modal ou sem modo?
-
24-09-2019 - |
Pergunta
É melhor mostrar UserForms ProgressBar no VBA como modal ou sem modo?Quais são as melhores práticas para desenvolver indicadores de progresso em VBA?
UserForms sem modelo requerem o uso de Application.Interactive = False
, enquanto os UserForms modais, por sua própria natureza, bloqueiam qualquer interação com o aplicativo até que o procedimento principal seja concluído ou cancelado.
Se Application.Interactive = False
é usado, no entanto, a tecla Esc interrompe a execução do código, portanto o uso de Application.EnableCancelKey = xlErrorHandler
e tratamento de erros (Err.Number = 18
) é necessário tanto no UserForm quanto no procedimento de chamada.
Procedimentos de chamada com uso intensivo de recursos também podem resultar em CommandButton_Click
e UserForm_Activate
eventos falhando em UserForms sem janela restrita.
Em geral, indicadores de progresso que utilizam UserForms modais parecem mais simples, pois o código que está sendo executado está totalmente contido no módulo UserForm, e há menos necessidade de passagem de variáveis.
O problema, entretanto, com o uso de UserForms modais para indicadores de progresso é que um módulo UserForm separado é necessário para cada procedimento que precisa de um indicador de progresso, porque o procedimento de chamada deve estar dentro do procedimento UserForm_Activate.
Portanto, embora seja possível ter um único indicador de progresso reutilizável em um UserForm sem janela restrita, ele será menos confiável do que executar o código em vários UserForms modais.
Qual caminho é melhor?
Obrigado!
Solução 2
Vou fechar este e dizer que Modal é o vencedor. Eu tentei nos dois lados, mas você acaba tentando fechar muitas brechas com formas de usuário modificadas. O Modal é mais difícil porque é mais rigoroso, mas incentiva você a dividir seu código em pedaços menores, o que é melhor a longo prazo de qualquer maneira.
Outras dicas
Há também uma terceira maneira, usando o Application.StatusBar
. Você pode até simular uma verdadeira barra de progresso usando uma sequência de caracteres U+25A0 e U+25A1.
Definitivamente modal. Se você vai considerar o Modyless, deve executá-lo em um encadeamento separado e fora de processo e não no thread principal do Excel.exe.
Acho que vale a pena responder ao tópico inicial, já que a pergunta foi formulada tão bem que o Google a encontra primeiro.
Seção 1 - Teoria
A primeira coisa a dizer é que transferir as variáveis entre os módulos não é nada difícil.
A única coisa que você precisa fazer é criar um módulo separado e colocar lá todas as variáveis globais.Então você poderá lê-los em todos os lugares, em todos os formulários, planilhas, módulos.
A segunda coisa é que a janela deve ser MODELESS.Porquê isso?A resposta é para manter a mobilidade do código, ou seja
- a função onde o processo mais rotineiro é executado não deve estar localizada no módulo UserForm
- você pode chamar a janela com barra de progresso de qualquer lugar e
- a única conexão entre a função/procedimento da rotina são as variáveis globais
Esta é uma grande vantagem para ser versátil aqui.
Seção 2 - Prática
1) Crie um módulo "Declaração" com as variáveis globais:
Public StopForce como inteiro 'Esta variável será usada como um indicador de que o usuário pressionou o botão Cancelar
PCTDone público como único "Este é o % do trabalho que já foi feito
CurrentFile público como cadeia de caracteres ' qualquer outro parâmetro que queremos transferir para o formulário.
2) Crie o formulário com o botão.No evento OnClick do botão deverá existir um código onde nos referimos à variável global StopForce em Declaração módulo
Private Sub CommandButton1_Click()
Declaration.StopForce = 1
End Sub
3) Adicione um procedimento onde você atualiza a barra de progresso
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) em qualquer outro módulo devemos ter as funções ou o procedimento/sub onde é feita a rotina:
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
Na verdade, você tem propriedades seguintes, resultando em prós/contras, dependendo da sua necessidade:
Type | Impact on UI | Impact on caller execution
----------|--------------|-----------------------------
Modal | Blocked | Blocked until Form is closed
Modeless | Not blocked | Continues
Se você deseja bloquear a interface do usuário e deixar o chamador continuar, então você precisa abrir o formulário no modo modal com Application.OnTime
.