Je voudrais utiliser un formulaire utilisateur informel pour que l'utilisateur puisse naviguer dans la feuille Excel avant de répondre à la question sur le formulaire utilisateur. Je dois mettre le code en pause ou en boucle jusqu'à ce que le formulaire utilisateur soit fermé (masqué ou déchargé).
Problème similaire à celui-ci: Comment puis-je attendre qu'un code spécifique s'exécute lorsque le formulaire est fermé et défini sur vbModeless? mais la solution ici ne fonctionne pas pour mon application; Mon formulaire utilisateur est ouvert au milieu d'un long sous-programme qui doit terminer son exécution après la fermeture du formulaire utilisateur.
Dim popupActive as Boolean popupActive = True StartingSINT_Popup.Show vbModeless 'Open userform 'have VBA code wait until userform is closed wait until popupActive = False 'set to false with OK button on userform 'continue code with info input inside StartingSINT_Popup userform
3 Réponses :
Je n'ai pas créé la fonction suivante, mais je l'utilise depuis longtemps et cela fonctionne.
Do While IsLoaded("StartingSINT_Popup")
Debug.Print Time; " StartingSINT_Popup Is Loaded!"
Loop
Vous devrez coder en dur le nom de la chaîne et ne pas utiliser le .Name du formulaire car le formulaire n'est peut-être pas encore chargé et ne contient pas cette propriété.
Voici un petit extrait de la façon dont vous pouvez utiliser cette fonction:
Private Function IsLoaded(ByVal formName As String) As Boolean
Dim frm As Object
For Each frm In VBA.UserForms
If frm.Name = formName Then
IsLoaded = True
Exit Function
End If
Next frm
IsLoaded = False
End Function
Cela plante VBA parce que (je pense) que la boucle while s'exécute si vite et qu'elle est toujours chargée.
Vous devrez probablement ajouter une ligne DoEvents dans la boucle et / ou un minuteur qui attend pour appeler la fonction.
@Nick FWIW En - attendre comme ça, c'est vraiment, vraiment étirer le paradigme procédural bien au-delà de ce qui est acceptable IMO.
@Nick - Pour info, je recommande de lire la rubrique associée détruire correctement une instance de formulaire utilisateur non modale a> basé sur la vue d'ensemble exceptionnelle de Mathieu "UserForm1.Show?"
Mon formulaire utilisateur est ouvert au milieu d'un long sous-programme qui doit finir de s'exécuter après la fermeture du formulaire utilisateur.
Votre procédure fait trop de choses et doit être décomposée en procédures plus petites et plus spécialisées.
La bonne façon de procéder est de changer le paradigme de procédural à event-driven
Au lieu d'afficher l'instance par défaut du formulaire comme ceci:
Private presenter As PopupPresenter Public Sub DoStuff() Set presenter = New PopupPresenter 'do stuff... presenter.Show presenter.Foo = "some data" 'rest of the code in this scope will run immediately AND THIS IS FINE End SubAvoir un module de classe qui contient une instanceWithEventde celui-ci:Private WithEvents popup As StartingSINT_Popup Public Foo As String Private Sub Class_Initialize() Set popup = New StartingSINT_Popup End Sub Public Sub Show() popup.Show vbModeless End Sub Private Sub popup_Closed() ' code to run when the form is closed MsgBox Foo End SubDans le code-behind du formulaire, déclarez un
Closed événement:Private presenter As PopupPresenter Public Sub DoStuff() Set presenter = New PopupPresenter 'do stuff... presenter.Show 'rest of the code in this scope will run immediately AND THIS IS FINE End SubEt puis le déclencher dans le gestionnaire
QueryClose:Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) If CloseMode = 0 Then 'controlbox was clicked (the "red X button") Cancel = True 'would otherwise destroy the form instance Me.Hide 'always hide, never unload End If RaiseEvent Closed End SubSupposons maintenant que vous ayez nommé cette classe
PopupPresenter, votre procédure peut maintenant faire ceci:Public Event Closed()Gardez le présentateur au niveau du module afin que l'objet ne fonctionne pas t sortir de la portée lorsque
DoStuffse termine, et transmettre toutes les variables / valeurs ou indiquer que l'objet présentateur doit faire son travail lorsque le formulaire est fermé. Vous pouvez le faire en exposant des propriétés ou des champs / variables publics (préférez les propriétés, mais c'est un tout autre sujet):Private WithEvents popup As StartingSINT_Popup Private Sub Class_Initialize() Set popup = New StartingSINT_Popup End Sub Public Sub Show() popup.Show vbModeless End Sub Private Sub popup_Closed() ' code to run when the form is closed End SubStartingSINT_Popup.Show vbModeless 'Open userform
Bien dit. Pourrait devoir remplacer ma fonction!
Comment pouvons-nous déclencher un événement après l'exécution de presenter.Show avec cette approche? Actuellement, UserForm_Activate ne fait rien, cherchant principalement à charger un modèle dans le formulaire
@Jose pas sûr que je suis. Si vous avez une fenêtre contextuelle Private WithEvents As SomeForm , le gestionnaire d'activation serait popup_Activate - vous ne devriez jamais taper les signatures du gestionnaire d'événements à la main ... liste déroulante en haut à gauche, puis sélectionnez l'événement que vous souhaitez gérer dans la liste déroulante en haut à droite. Consultez ce message (et l'article lié) pour plus d'informations sur Model-View-Presenter .
@MathieuGuindon Merci d'avoir fourni cette clarté. Que faire si j'ai du code dans mon UserForm qui définit le dimensionnement du formulaire et charge les valeurs de Model dans ComboBoxes , comment puis-je faire exécuter ce code ? Dois-je déclarer un Public Sub dans le UserForm et appeler le Sub via l'événement popup_Activate ?
J'aurais un Public Property Set Model (ByVal value As Object) et demanderais à la propriété du présentateur d'injecter le modèle là-bas. La propriété a tous les droits pour faire quelque chose comme Set viewModel = value , puis pour appeler une procédure InitializeView avant de retourner, où des choses comme Me.NameBox.Text = viewModel .Name se produit. Pendant ce temps, le gestionnaire NameBox_Change effectue viewModel.Name = Me.NameBox.Text et appelle éventuellement la validation du modèle (qui peut alors déterminer si le bouton OK est activé).
@MathieuGuindon merci !! Cela a du sens! J'apprends toujours MVP et OOP grâce à votre blog RubberDuck! J'étais la même personne qui a commenté votre message CarFactory il y a 2 semaines. Merci pour tout ce que vous faites pour nous!
Voici une alternative ...
1. Dans le module [public] d'origine (celui qui appelle userform 1), déclarez une variable booléenne publique.
Public done As Boolean
2. Dans le formulaire utilisateur 1,
a. Attribuez une valeur par défaut à la variable booléenne
b. Appelez Userform 2
c. Faites une boucle while qui ...
Code
Private Sub submit_Click()
'Userform submit code
Dim name As String
name = TextBox.Value
sql = "INSERT INTO table (field) VALUES ('" & name & "')"
Call query(sql)
'IMPORTANT: change Boolean variable to break loop before exiting userform
done = True
Unload Me
End Sub
3. Dans userform 2 , changez la valeur de Boolean pour casser la boucle
Code
Private Sub event_click()
done = False
Dim userform2 As New userform
userform2.Show Modeless
'This will loop through until userform2 changes done variable to "True"
Do While done = False
DoEvents
Loop
'Code after done with userform2
dataSource.Refresh
End Sub