Avoir une UserForm
instance par défaut exécutant l’émission est peut-être la chose la plus facile à faire, mais c’est aussi une cause directe de beaucoup de problèmes - des bogues faciles à introduire, mais difficiles à trouver, aux problèmes de maintenance et d’extensibilité: solution rapide, fonctionne "est le modèle" Smart UI ", qui fonctionne génial pour un prototype . Les grands projets qui évoluent constamment avec le temps exigent une architecture plus intelligente.
Les programmeurs l'appellent "model-view-presenter". La vue est la forme. Les données sont le modèle , et puis il y a le présentateur qui coordonne tout.
Appeler une feuille secondaire à partir d'un formulaire utilisateur
La vérité est que vous ne le faites pas. Un modal UserForm
est un dialogue dont le rôle n'est rien de plus que de recueillir les données de l'utilisateur. En le rendant uniquement responsable de la manipulation des données et en laissant le macro / appelant responsable du flux de contrôle, vous rendez le code plus robuste et plus facile à gérer, en particulier si le formulaire peut faire beaucoup de choses.
Commencez avec un MonthlyReportParams
module de classe simple :
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
Maintenant UserForm
, il ne reste plus qu’à travailler avec ces données, ce modèle .
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
Et maintenant, la macro qui ouvre ce formulaire peut reprendre le contrôle de ce qui se passe: le formulaire n’exécute plus le show, et nous pouvons lire tout ce qui se passe au même endroit:
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
Vous trouverez des informations supplémentaires sur les avantages de ce "renversement des responsabilités" dans cet article , ainsi qu'une logique de rappel dans cet article - disclaimer: j'ai écrit les deux; ce blog est le blog officiel du projet OSS complémentaire de Rubberduck VBIDE, que je possède.