Microsoft 365 Userform : Astuce pour pourvoir utiliser le bouton UnLoad Me - quand exit Cancel sur textbox

RyuAutodidacte

XLDnaute Impliqué
Supporter XLD
Bonjour à tous en ce dimanche,

Dans un UserForm (Pas de code ActiveX svp qui ne sont pas géré chez moi) , j'ai 2 TextBox qui utilisent un event Exit.
Pour les TextBox : Tant que les conditions ne sont pas remplis (donnée valide) le Exit est en Cancel True et garde donc le focus

Ce que j'aimerai et malgré ce focus est une astuce qui me permettrait quand même de pouvoir cliquer sur le Bouton qui UnLoad me
Déjà est ce possible ? et si oui comment svp ?
 
Solution
Il y a 2 problèmes:

1 - Le MsgBox à la sortie duquel un _Exit() / Cancel = True ne repositionne pas le curseur dans la TextBox qui a gardé le Focus si le UserForm a été affiché en vbModeless.

2 - Forcer la sortie du UserForm dans un _Exit() / Cancel = True

Le problème #1 se règle avec un Application.OnTime (voir le module dédié Module_SetFocus dans le fichier)

Le problème #2 est beaucoup plus délicat, car même en appuyant sur un CommandButton, l'_Exit() sera exécuté en priorité et un _Exit() / Cancel = True empêchera l'exécution du _CommandButton_Click() !!!

Les solutions:

a) - Accepter une saisie...

patricktoulon

XLDnaute Barbatruc
re
bonsoir
si me.hide:me.show fini par surcharger et bloquer excel
n'utilise pas l'instance de classe active du userform mais cell e de l'appel
me.hide:userform1.show
ça arrive de temps en temps avec les new version d'excel
je sais que c'est obscure pour certains mais c'est bien ca
change userform1 pour le nom de ton userform
;)
 

Dudu2

XLDnaute Barbatruc
Il y a 2 problèmes:

1 - Le MsgBox à la sortie duquel un _Exit() / Cancel = True ne repositionne pas le curseur dans la TextBox qui a gardé le Focus si le UserForm a été affiché en vbModeless.

2 - Forcer la sortie du UserForm dans un _Exit() / Cancel = True

Le problème #1 se règle avec un Application.OnTime (voir le module dédié Module_SetFocus dans le fichier)

Le problème #2 est beaucoup plus délicat, car même en appuyant sur un CommandButton, l'_Exit() sera exécuté en priorité et un _Exit() / Cancel = True empêchera l'exécution du _CommandButton_Click() !!!

Les solutions:

a) - Accepter une saisie vide dans la TextBox pour laisser passer et permettre au CommandButton de jouer son rôle.

b) - Fermer le UserForm par son menu système (la croix X en haut)

c) - Intercepter une touche (comme ESCAPE) dans la TextBox si cette convention est acceptable dans le contexte application

d) - Intercepter l'évènement CommandButton_MouseDown qui certes intervient après l'évènement _Exit() mais qui lui au moins est déclenché et permet de fermer le UserForm après l'affichage du MsgBox.
 

Pièces jointes

  • Exit sur Control de UserForm.xlsm
    23.6 KB · Affichages: 12
Dernière édition:

patricktoulon

XLDnaute Barbatruc
re



j'utilise le fichier du post #12
et comme ça
j'ai ajouté la condition modeclose pour ne pas déclencher le message si fermeture forcé par la croix avec un txt vide ou non numérique
VB:
Dim modeclose

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
    If modeclose = 1 Then Exit Sub
    With TextBox1
        If Not IsNumeric(.Value) Then
            MsgBox " non raté essaie encore"
            réactivewindow TextBox1
            Cancel = True
        End If
    End With
End Sub


Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If modeclose = 1 Then Exit Sub
    With TextBox2
        If Not IsNumeric(.Value) Then
            MsgBox " non raté essaie encore"
            réactivewindow TextBox2
            Cancel = True
        End If
    End With
End Sub

'pour pouvoir quitter meme avec le textbox vide ou  non numerique sans redeclencher le message
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
modeclose = 1
End Sub

Sub réactivewindow(ctrl As MSForms.TextBox)
    hwnd = ExecuteExcel4Macro("CALL(""user32"",""GetActiveWindow"",""JCC"")")         'handle fenetre active
    ExecuteExcel4Macro ("CALL(""user32"",""ShowWindowA"",""JJJJJ""," & hwnd & "," & 5 & ")")'réactive la fenetre
    ctrl.SetFocus
    ctrl.SelStart = 0: ctrl.SelLength = 100
End Sub
 

patricktoulon

XLDnaute Barbatruc
re
le bouton Quitter fait son effet mais la croix peut fermer sans message pour pouvoir abandonner la saisie
VB:
Dim modeclose

Private Sub CommandButton1_Click() 'bouton Quitter
Unload Me
End Sub

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
    If modeclose = 1 Then Exit Sub
    With TextBox1
        If Not IsNumeric(.Value) Then
            MsgBox " non raté essaie encore"
            réactivewindow TextBox1
            Cancel = True
        End If
    End With
End Sub


Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If modeclose = 1 Then Exit Sub
     With TextBox2
        If Not IsNumeric(.Value) Then
            MsgBox " non raté essaie encore"
            réactivewindow TextBox2
            Cancel = True
        End If
    End With
End Sub

'pour pouvoir quitter meme avec le textbox vide ou  non numerique sans redeclencher le message
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = 0 Then modeclose = 1 Else modeclose = 0
End Sub

Sub réactivewindow(ctrl As MSForms.TextBox)
    hwnd = ExecuteExcel4Macro("CALL(""user32"",""GetActiveWindow"",""JCC"")")         'handle fenetre active
    ExecuteExcel4Macro ("CALL(""user32"",""ShowWindowA"",""JJJJJ""," & hwnd & "," & 5 & ")")
    ctrl.SetFocus
    ctrl.SelStart = 0: ctrl.SelLength = 100
End Sub
 

Dudu2

XLDnaute Barbatruc
J'ai mis à jour le Post #12 et son fichier pour indiquer une solution supplémentaire - voir item d) - pour fermer le UserForm.
Certes elle ne permet pas d'éviter l'affichage du message d'erreur, mais au moins le UserForm se ferme après le clic sur le bouton.

A mon avis c'est finalement la plus simple à mettre en œuvre.
 

Dudu2

XLDnaute Barbatruc
Pour info je te donne le fichier limité à la version que je considère comme la plus simple et que j'utiliserais probablement même si elle a le petit inconvénient d'envoyer le message d'erreur (si erreur il y a) avant la fermeture.
 

Pièces jointes

  • Exit sur Control de UserForm Option Simple.xlsm
    24.8 KB · Affichages: 7

Dranreb

XLDnaute Barbatruc
Bonjour.
Attention les opérateurs de comparaison sont prioritaires sur les opérateurs logiques.
je préfère vous le dire car je vois aussi des = True et = False derrière des expressions Boolean, ce qui est inutile vu que la nouvelle expression Boolean ainsi formée à toujours la même valeur que l'expression seule.
VB:
' If TxtB1 And TxtB2 = True Then CommandButton1.Enabled = True 
If TxtB1 And TxtB2 Then CommandButton1.Enabled = True 
…
' If TxtB1 And TxtB2 = False Then CommandButton1.Enabled = False
If TxtB1 And Not TxtB2 Then CommandButton1.Enabled = False
N'y aurait il pas plus simple parfois de ce genre ? :
VB:
CommandButton1.Enabled = TxtB1 And TxtB2
 

mapomme

XLDnaute Barbatruc
Supporter XLD
Bonsoir à tous,

Venant après la bataille, j'ai pondu un code un peu plus concis :
  • On peut utiliser Tab et Shift+Tab pour passer de textbox en textbox (y compris aussi les deux options)
  • Le bouton Proceed est le bouton de validation par défaut (touche Enter)
  • Le bouton Cancel est le bouton d'annulation par defaut (touche Escape)
  • La variable publique Increment est signée (négative si on soustrait, positive si on additionne)
  • Si annulation du UserForm alors Increment vaut 0
  • Les différentes vérifications ne se font qu'au moment de l'activation du bouton Proceed
  • La touche Entrée valide le UserForm (classique sur Windows)
Testé sur PC avec Office 365
 

Pièces jointes

  • RyuAutodidacte- UserForm Saisie- v1.xlsm
    43.3 KB · Affichages: 9

patricktoulon

XLDnaute Barbatruc
Bonjour à tous
oui ben çaen fait un bins pour pouvoir fermer un usf avec un exit cancel=true sur un textbox
perso je préfère voir les chose plus simplement
le probleme c'est quoi :
ben tout simplement que le bouton ne peut pas prendre le focus puisqu'il est automatiquement redonné au textbox qui n'a pas recu la donnée et qui est donc en cancel =true

ben la méthode la plus simple et la plus évidente c'est de supprimer le focus au click du bouton
conclusion tu met la property du bouton "takefocusonclick " a false dans le bouton
et tu peux d'es l'ors faire ce que tu veux et décider comment le userform doit reagir dans le bouton
tout simplement

avant de dire j'ai testé
un userform , 2 textbox et un bouton

voila avec ça je peux quand même fermer le userform même si l'un ou l'autre ou les deux textbox sont vides
en mettant cette propriété a false le click est exécuté quand même
avec cette methode la réaction des textbox avec cancel est bien effective entre textbox mais exclu le bouton de l'équation

terminé ;)
VB:
Private Sub CommandButton1_Click()
Unload Me
End Sub

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If TextBox1 = "" Then Cancel = True
End Sub

Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If TextBox2 = "" Then Cancel = True
End Sub

demo avec message
VB:
Dim objActif As Object
Private Sub CommandButton1_Click()
If objActif = "" Then MsgBox " le " & objActif.Name & "  est vide " & "ben je ferme quand meme au revoir"
Unload Me
End Sub


Private Sub TextBox1_Enter()
Set objActif = TextBox1
End Sub
Private Sub TextBox2_Enter()
Set objActif = TextBox2
End Sub

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If TextBox1 = "" Then Cancel = True
End Sub

Private Sub TextBox2_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If TextBox2 = "" Then Cancel = True
End Sub
1639034711880.png
 
Dernière édition:

Dudu2

XLDnaute Barbatruc
Bonjour,

Quoique tu fasses, l'évènement _Exit() de la TextBox sera déclenché (et traité pas ton Sub) en priorité.
Tu ne peux absolument pas en sortir si Cancel = True.

Il faut prévoir par exemple d'accepter une valeur vide (Cancel = False) pour laisser passer quitte à faire une vérification des zones vides non valides sur un "Valider" ultérieur.

Une technique plus complexe à mettre en œuvre est de ne pas utiliser le Cancel et de déclencher le contrôle sur la TextBox de manière asynchrone (Application.OnTime Now, "FonctionDeContrôleTextBox") et si le contrôle détecte une erreur d'y faire un TextBox.SetFocus (qui est hélas sans effet dans l'_Exit() de la TextBox concernée).

Edit:
Ou alors, encore mieux, limiter le rôle de la fonction asynchrone au TextBox.SetFocus. en cas d'erreur détectée par les instructions de contrôle de validité de l'_Exit().
 
Dernière édition:

RyuAutodidacte

XLDnaute Impliqué
Supporter XLD
Bonjour,

Quoique tu fasses, l'évènement _Exit() de la TextBox sera déclenché (et traité pas ton Sub) en priorité.
Tu ne peux absolument pas en sortir si Cancel = True.

Il faut prévoir par exemple d'accepter une valeur vide (Cancel = False) pour laisser passer quitte à faire une vérification des zones vides non valides sur un "Valider" ultérieur.

Une technique plus complexe à mettre en œuvre est de ne pas utiliser le Cancel et de déclencher le contrôle sur la TextBox de manière asynchrone (Application.OnTime Now, "FonctionDeContrôleTextBox") et si le contrôle détecte une erreur d'y faire un TextBox.SetFocus (qui est hélas sans effet dans l'_Exit() de la TextBox concernée).

Edit:
Ou alors, encore mieux, limiter le rôle de la fonction asynchrone au TextBox.SetFocus. en cas d'erreur détectée par les instructions de contrôle de validité de l'_Exit().
ok Merci Dudu2,

Donc si je comprends bien, je laisse le Cancel à False je créé une fonction pour le focus qui se lance si erreur détecté via Application.OnTime,
c'est bien ça…

je suis pas sur d'avoir tout compris correctement …

Genre ceci :
VB:
'd'un coté on a :
Application.OnTime Now + TimeValue("00:00:02"), "AsyncTextBox"


'Et de l'autre on a :
Function AsyncTextBox(MyTexBox)
    
    If UserForm1.MyTexBox.Value = "" Then MyTexBox.SetFocus

End Function
 

Dranreb

XLDnaute Barbatruc
Bonjour.
Peut être quelque chose à combiner avec BeforeUpdate, qui n'est plus invoqué une seconde fois si la TextBox n'est plus modifiée, ce qui permettrait de positionner à True un global Boolean seulement s'il l'est, l'Exit le remettant à False à la fin. À tester …
 

Discussions similaires

Statistiques des forums

Discussions
312 219
Messages
2 086 369
Membres
103 198
dernier inscrit
CACCIATORE