Ter a instância padrão de UserForm
executando o programa é talvez a coisa mais fácil de se fazer, mas também é uma causa direta de muitos, muitos problemas - desde bugs de fácil introdução, mas difíceis de encontrar até manutenção e extensibilidade. problemas: a solução "nice & quick, works" é o padrão "Smart UI", que funciona muito bem para um protótipo . Projetos maiores que crescem constantemente ao longo do tempo exigem uma arquitetura mais inteligente.
Os programadores chamam de "model-view-presenter". A vista é o formulário. Os dados são o modelo , e depois há o apresentador que coordena tudo.
Calling a Sheet Sub from a Userform
A verdade é que você não sabe. Um modal UserForm
é um diálogo , cuja função nada mais é do que coletar a entrada do usuário. Ao torná-lo responsável apenas pela manipulação de dados e deixar a macro / responsável pela chamada responsável pelo fluxo de controle, você torna o código mais robusto e mais fácil de manter - especialmente se o formulário puder fazer muitas coisas.
Comece com um módulo de classe MonthlyReportParams
simples:
Option Explicit
Public Month As Integer ' encapsulate into properties to implement
Public Year As Integer ' logic for validation on assignment.
Public Property Get IsValid() As Boolean
IsValid = Month >= 1 And Month <= 12 And _
Year >= 1900 And Year <= 2100
End Property
Agora, tudo o que o UserForm
precisa fazer é trabalhar com esses dados, esse modelo .
Option Explicit
Private params As MonthlyReportParams
Private cancelled As Boolean
Private Sub Class_Initialize()
Set params = New MonthlyReportParams
End Sub
Public Property Get Model() As MonthlyReportParams
Set Model = params
End Property
Public Property Set Model(ByVal value As MonthlyReportParams)
Set params = value
MonthBox.value = params.Month
YearBox.value = params.Year
End Property
Public Property Get IsCancelled() As Boolean
IsCancelled = cancelled
End Property
Private Sub MonthBox_Change()
' make sure the textboxes contain numeric values before assigning to Integer
If IsNumeric(MonthBox.Value) Then params.Month = CInt(MonthBox.Value)
OnValidate
End Sub
Private Sub YearBox_Change()
' make sure the textboxes contain numeric values before assigning to Integer
If IsNumeric(YearBox.Value) Then params.Year = CInt(YearBox.Value)
OnValidate
End Sub
Private Sub OkButton_Click()
Me.Hide
End Sub
Private Sub CancelButton_Click()
OnCancel
End Sub
Private Sub OnCancel()
cancelled = True
Me.Hide
End Sub
Private Sub OnValidate()
OkButton.Enabled = Model.IsValid
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
' runs when form is just about to close
If CloseMode = VbQueryClose.vbFormControlMenu Then
' user clicked the [X] button
Cancel = True ' don't destroy the form
OnCancel
End If
End Sub
E agora, a macro que exibe este formulário pode recuperar o controle sobre o que está acontecendo: o formulário não está mais exibindo o programa e podemos ler tudo o que está acontecendo em um só lugar:
Public Sub RunMonthlyReport(Optional ByVal targetSheet As Worksheet = Nothing)
If targetSheet Is Nothing Then
' no sheet was specified; work of the ActiveSheet
Debug.Assert Not ActiveSheet Is Nothing
Set targetSheet = ActiveSheet
End If
' create the model
Dim m As MonthlyReportParams
Set m = New MonthlyReportParams
m.Month = Month(Now)
m.Year = Year(Now)
' create the dialog, assign the model
With New MonthlyReportParamsDialog
Set .Model = m
.Show ' next line only runs after dialog has closed
If Not .IsCancelled Then
' run the report with the values in the model
targetSheet.Report m.Month, m.Year
End If
End With
End Sub
Informações adicionais sobre os benefícios dessa "inversão de responsabilidades" podem ser encontradas neste artigo e outra lógica de retorno de chamada neste artigo - disclaimer: Eu escrevi os dois; esse blog é o blog oficial do Rubberduck projeto add-in VBIDE OSS, que eu possuo.