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
le tiret(ou moins) tu ne peux le mettre que devant
VB:
'*****************************************************
'   KEYPRESS Forcer ˆ utiliser que les chiffres et tiret + UnLoad
'*****************************************************
Private Sub TextBox1_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    With TextBox1
        If KeyAscii = 27 Then Unload Me
        If Not Chr(KeyAscii) Like "[0-9-]" Then KeyAscii = 0
        If Chr(KeyAscii) = "-" And Len(.Value) > 0 Then KeyAscii = 0
    End With
End Sub
Private Sub TextBox2_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
    With TextBox2
        If KeyAscii = 27 Then Unload Me
        If Not Chr(KeyAscii) Like "[0-9-]" Then KeyAscii = 0
        If Chr(KeyAscii) = "-" And Len(.Value) > 0 Then KeyAscii = 0
    End With
End Sub
 

RyuAutodidacte

XLDnaute Impliqué
Supporter XLD
le "-" je suppose que c'est le moins c'est ça
donc on peu taper un nombre negatif ou positif c'est ça ton souhait ?
Oui c'est exactement ca
PS : le => If KeyAscii = 27 Then Unload Me je l'ai mis au pour donner la priorité au Unload

Edit : oui pour le 1er TextBox le tiret est que devant, tandis que pour le 2ème TextBox le tiret se trouve entre 2 chiffres dont le chiffre1 <= chiffre2
Donc pour Private Sub TextBox2_KeyPress ton code ne rempli pas les conditions

Ci-dessous je t'écris/montre mes conditions concernant ma TextBox2
VB:
TB_TextBox2 = Split(TextBox2,"-")
TextBox2 Like "*#-#*" and IsNumeric(TB_TextBox2(0)) and IsNumeric(TB_TextBox2(1)) and TB_TextBox2(0) <= TB_TextBox2(1)
 
Dernière édition:

RyuAutodidacte

XLDnaute Impliqué
Supporter XLD
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
VB:
CommandButton1.Enabled = TxtB1 And TxtB2
En effet l'écriture tel quel est logique et évite la condition, bien vu Dranreb 👍
 

RyuAutodidacte

XLDnaute Impliqué
Supporter XLD
si tu parles de mes keypress n'oublie pas d'enlever ta ligne d'origine
Re Patrick, pas sur d'avoir compris t a phrase mais j'ai modifié mon poste #33 (si c'est de ça dont tu parlais …)
Oui en effet, il y a des choses plus compliqué parfois pour avoir les mêmes choses que sur PC

PS : je suis qd même assez content de l'astuce du compteur qui me donne l'effet escompté (pour moi)
du coup aujourd'hui j'ai fait qq modifs et je l'ai testé sur un PC que l'on m'a prété et tout à l'air OK :)
Si c'est ok (sans les dernières modif.) de ton/votre (cher membres du forum), c'est Top
 

RyuAutodidacte

XLDnaute Impliqué
Supporter XLD
Bonsoir @patricktoulon , @Dudu2 , @Dranreb ,

Je reviens vers vous pour un petit update (sachant que sur Mac difficile d'appréhender les UserForms étant donné que l'on ne peut pas les créer sur Mac Grrrrr MS) - astuce supp en plus de la solution de Dudu2

Donc j'ai revu … revu … et revu mon code (bon entrainement sur les Userforms) et je me suis aperçu que j'avais besoin en plus des Actions Tab | Tab + Shift | Return pour passer de TextBox en TextBox
J'en ai profité pour remettre les MsgBox sachant que je devais m'occuper de la perte du curseur dans la TextBox active ; j'ai réussi à biaiser avec le SetFocus en faisant un aller-retour vers : autre Control => TextBox (pour que ça marche correctement sur Mac (testé) et PC (pas testé :( - pas de pc sous la main)
J'ai aussi revu le code afin qu'il soit plus simple …

SVP Votre Avis m'intéresse … pour/contre …multiplateforme PC/MAC … ça donne quoi sur PC ? … etc …
Merci d'avance
 

Pièces jointes

  • USERFORM PC-MAC New Version.xlsm
    45.6 KB · Affichages: 2

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:

RyuAutodidacte

XLDnaute Impliqué
Supporter XLD
Bonsoir @mapomme et @patricktoulon,

merci pour vos réponses :

mapomme : l'approche est différente et est très clair avec un code vraiment très propre,
ça me montre une bonne vision de comment on peut procéder dans le codage d'un Userform.
Alors dans cette situation, comme le bouton Proceed gère les message d'erreur puis redonne le focus au TextBox, cela ne pose donc pas de problème sur la perte du curseur dans les TextBox.
Merci pour cette exemple très concis ( bien les trad. en anglais c'est très clair 👍)

Patricktoulon : J'adore l'astuce, je me la garde sous le coude

PS : je souhaite quand même forcer l'utilisateur à faire une bonne saisie dès le départ, … histoire qu'il prenne les bonnes habitudes dès le début - en tout cas c'est bien cool ;)
 

Dudu2

XLDnaute Barbatruc
Bonjour les gens,

Je reviens sur cette question car je suis confronté aux problèmes liés à l'évènement _Exit() d'un Control pour mes développements applicatifs et après avoir fouillé la question je peux dire ce qui suit.

Avec l'évènement <Control>_Exit() qui est fort utile pour effectuer des contrôles sur la valeur saisie en TextBox et ComboBox, il n'y a pas moins de 2 problèmes spécifiques et 1 problème général:
  1. Tant qu'on est dans la fonction <Control>_Exit() (Cancel = True) aucun autre évènement (Bouton cliqué...) ne peut se déclencher sauf les évènements liés au <Control> lui-même.
    Donc pas moyen de faire autre chose que de sortir du <Control>_Exit() par un Cancel = False ou fermer le UserForm par la croix du menu système.

  2. Un <Control>.SetFocus dans une fonction <Control>_Exit() donne des résultats aberrants qui dépendent de la distance en TabIndex du Control en <Control>_Exit().
    - 2nd appel récursif de la fonction <Control>_Exit() sur le SetFocus d'un Control au-delà du Control en TabIndex + 1.
    - Impossibilité de SetFocus sur un Control au-delà du Control en TabIndex + 2.

    C'est le cas par exemple où, selon la valeur saisie, on veut se positionner sur tel ou tel autre Control du UserForm.

  3. De plus, de manière générale, si on utilise un MsgBox (ou un autre UserForm de message) dans une fonction d'évènement d'une TextBox ou ComboBox de UserForm affiché en vbModeless, le curseur ne s'affiche pas au retour du MsgBox, même si l'ActiveControl est correct.
Je vais adresser ces problèmes dans 2 Ressources:
-> MsgBox en UserForm vbModless (en attente de publication)
-> Problèmes liès à l'évènement Exit d'un Control

Avec comme solutions pour:
  1. Une manière de coder pour faciliter la sortie.
  2. Une fonction asynchrone autonome.
  3. Une fonction asynchrone autonome.
@+
 
Dernière édition:

Dudu2

XLDnaute Barbatruc
Salut le Experts,

Voilà pour le MsgBox dans un UserForm affiché en vbModeless, la ressource vient d'être publiée... et déjà modifiée !

En effet, pour "réactiver" le Control j'utilisais la séquence:
VB:
With Control
    .Visible = False
    .Visible = True
End With

Mais que ce soit avec la propriété .Visible ou la propriété .Enabled, cela provoque inévitablement le déclenchement de l'évènement Control_Exit(), un effet induit parfaitement indésirable.

Donc je suis passé à une méthode indolore qui consiste à simuler l'action de l'utilisateur avec un clic souris.

Il est donc maintenant parfaitement possible d'écrire sans effets secondaires:
VB:
Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean) 
    'UserForm is closed = condition to move out of TextBox1
    If Not Me.Visible Then Exit Sub
 
    'TextBox1 is empty = condition to move out of TextBox1
    If Len(Trim(Me.TextBox1.Value)) = 0 Then Exit Sub

    'Not numeric value = condition to stay in TextBox1
    If Not IsNumeric(Me.TextBox1.Value) Then
        MsgBoxInModelessUserForm "You need to input a numeric value !"
        Cancel = True
    End If
End Sub
 
Dernière édition:

Dudu2

XLDnaute Barbatruc
Reste encore à publier une petite Ressource pour le bon usage du Contol_Exit() et du SetFocus asynchrone qui va avec. Car il est dommage d''avoir ces limitations dues à des problèmes purement Excel sur une fonction évènementielle aussi utile que le Contol_Exit() et qui est à mon avis sous-utilisée.

Souvent c'est l'évènement Control_Change() qui est utilisé mais il ne donne que des valeurs intermédiaires et pas la valeur finale. Donc c'est assez difficile d'en tirer quelque chose sauf pour des cas particuliers.
 

Discussions similaires

Statistiques des forums

Discussions
311 711
Messages
2 081 799
Membres
101 818
dernier inscrit
tiftouf5757