Bonjour

Il m'a été donné, il y a 2 ou 3 jours, d'intervenir dans cette discussion :
https://www.developpez.net/forums/d8.../#post10902761
pour signaler une faille d'un didacticiel relatif aux saisies contrôlées de numériques.

Bien que peu adepte de ce genre (contrôles en cours de saisie) et privilégiant personnellement un contrôle en fin de saisie (suffisamment efficace et d'une plus grande légèreté), j'ai voulu écrire de quoi satisfaire (et pour le coup de manière plus flexible et complète) ceux qui s'intéressent à ce type de démarche. Et tant qu'à faire, j'ai délibérément choisi une démarche inhabituelle (histoire d'aborder différemment le problème)

============================== ~||~ =========================


------------------------------La fonction num_ok traite une valeur de type string et retourne une valeur de même type

La valeur traitée est une saisie. La valeur retournée est :
- soit la saisie acceptée d'une expression numérique valide
- soit la valeur antérieure à la modification de la saisie, si expression numérique non valide


Syntaxe de la fonction :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
ret =  num_ok(Saisie, anterieur, [negatif_ok] , [nb_decimales],[sep_decimal]
PARAMETRES Nature Type Description
Saisie obligatoire String la saisie à contrôler : la propriété Text de la zone d'édition (d'une
textbox, d'une combobox, ... par exemple)
anterieur obligatoire String A passer tel quel et sous ce nom (auto-géré)
negatif_ok facultatif Boolean spécifier à true pour gérer également les nombres négatifs
ou à false pour imposer un nombre positif.
= False si omis
nb_decimales facultatif Byte nombre maximum de décimales autorisé.
= 2 si omis.
0 pour imposer un nombre entier
sep_decimal facultatif String facultatif : "." ou "," pour imposer le point ou la virgule comme
séparateur décimal
Si omis : l'utilisateur pourra utiliser, à son gré, l'un ou l'autre
de ces deux séparateurs

-----------------------------------------------------------------------Le code de la fonction :

Il est à mettre dans un module standard, de sorte à être accessible depuis n'importe quelle feuille de calcul ou userform.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Public Function num_ok(ByVal Saisie As String, ByRef anterieur As String, _
   Optional negatif_ok As Boolean = False, Optional nb_decimales As Byte = 2, Optional sep_decimal As String) As String
  Static ds As String
  num_ok = Saisie
  If ds = "" Then ds = Application.International(xlDecimalSeparator)
  ''''''''''''''''''''''''''''If (negatif_ok = False And InStr(num_ok, "-") > 0) Or (negatif_ok = True And InStrRev(num_ok, "-") > 1) Then num_ok = anterieur: Exit Function
  If InStr(num_ok, "-") > Abs(CInt(negatif_ok)) Then num_ok = anterieur: Exit Function
  If num_ok <> "" And num_ok <> "-" Then
    If num_ok Like "* *" Or Not IsNumeric(Replace(num_ok, ".", ds)) Then num_ok = anterieur
    If num_ok Like "*[,.]" & String(nb_decimales, "#") & "?" Or (nb_decimales = 0 And num_ok Like "*[,.]") Then num_ok = anterieur
    If sep_decimal = "." Then num_ok = Replace(num_ok, ",", sep_decimal)
    If sep_decimal = "," Then num_ok = Replace(num_ok, ".", sep_decimal)
  End If
  anterieur = Trim(num_ok)
End Function
------------------------------------------------------- Comment appeler cette fonction :

J'ai choisi de le faire dans l'évènement Change (d'une textbox, combobox, ...) car cette manière de faire accepte également les copiés/collés valides et rejette ceux qui ne le sont pas (sans nécessité de faire "jouer" des procédures complémentaires).

Voici quelques exemples suffisamment parlants :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
Private Sub TextBox1_Change()
  'exemple pour n'accepter qu'un nombre entier positif
 
  Static anterieur As String
  TextBox1.Text = num_ok(TextBox1.Text, anterieur, , 0, ",")
End Sub
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
Private Sub TextBox2_Change()
  'exemple pour accepter nombre négatif ou positif avec 3 décimales et en imposant le "." pour séparateur décimal
 
 
  Static anterieur As String
  TextBox2.Text = num_ok(TextBox2.Text, anterieur, True, 3, ".")
End Sub
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
Private Sub TextBox3_Change()
 
  'exemple pour accepter nombre positif avec un 2 décimales en laissant l'utilisateur choisir son séparateur décimal
 
  Static anterieur As String
  TextBox3.Text = num_ok(TextBox3.Text, anterieur)
End Sub
Remarque : le choix du séparateur décimal est important car déterminant de l'utilisation que l'on veut ensuite faire de la saisie.
- Choisir le "." facilite cette démarche car la fonction Val suffit alors à convertir la saisie en numérique
- Choisir la virgule (pour des raisons de convivialité, par exemple) imposera alors une conversion par utilisation (selon le cas) des fonctions habituelles de conversion (Cint, Csng, etc ...)
- Laisser l'utilisateur choisir ce séparateur (également pour des raisons de convivialité dans un contexte de personnels de diverses nationalités) imposera l'utilisation de la fonction Replace (pour remplacer par un "." la "," éventuelle), puis de la fonction Val


EDIT :
1) je viens de remplacer la ligne 6 par ce que j'ai écrit en lieu et place en ligne 7 (juste pour alléger, mais aussi s'amuser un peu).
2) si j'ai fait ici (en non dans la section générale de VBA) cette contribution, c'est qu'il y a une bonne raison (utilisation d'une propriété propre à VBA/EXCEL)
Pour ceux qui voudraient utiliser cette fonction hors Excel (que ce soit en VBA ou VB6), voilà une manière que je n'aime personnellement pas trop : --->>
remplacer cette ligne de code :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
If ds = "" Then ds = Application.International(xlDecimalSeparator)
par celles-ci :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
If ds = "" Then
   ds = IIf(IsNumeric("1.1"), ".", ",")
End If
3) cette fonction ne traite, telle quelle, que les deux séparateurs décimaux utilisés dans les pays qui les ont adoptés (tout l'occident et la plupart des autres pays).
Elle ne traite pas le caractère momayyez (unicode U+066B) en vigueur dans quelques pays arabes du continent asiatique. Ceux qui souhaiteraient utiliser également ce caractère pourront le faire en complétant/modifiant (après analyse et compréhension) certaines lignes du code de cette fonction.