XL 2010 Une fonction personnalisée au comportement étonnant

Magic_Doctor

XLDnaute Barbatruc
Bonsoir,

Je me heurte à un problème avec une fonction personnalisée.
Dans le paramétrage de cette fonction, il y a 2 paramètres optionnels : un pour un compteur (que l’on omettra), l’autre pour une valeur qui ne peut être dépassée.
Supposons que la valeur indépassable soit, par exemple, 3,45. Si je rentre dans la cellule :
3,3 --> 3° 3’
3,4 --> 3° 4’
3,44 --> 3° 44’
3,48 --> 3° 45’
3,57 --> 3° 45’

Mais, curieusement :
3,5 ou 3,6 ou 3,7 ou 3,8 ou 3,9 --> non pas 3° 5’ ou … 3° 9’, mais toujours 3° 45’.

Comment régler ce problème ?

La fonction :
VB:
Function ConvertirDecimal$(NbDec$, ChxDegGr As Byte, Optional compteur, Optional max)
'Retranscrit une valeur décimale en format TEXTE (qui ne doit pas dépasser 2 décimales après la virgule) en degrés & minutes ou grades
'- NbDec : un nombre entier ou décimal qui n'excède alors pas 2 chiffres après la virgule
'- ChxDegGr : si 1 --> degrés sous forme, par exemple : 42,75 <=> 42° 75'
'             si 2 --> grades
'- compteur : un numéro de compteur (1, 2, 3...)
'             si omis --> pas de signe +/- avant la chaîne de caractères
'             si stipulé --> un signe +/- (suivant la valeur du compteur) avant la chaîne de caractères
'             Ex : pour degrés --> compteur omis : 42,25 --> 42° 25'
'                                  compteur stipulé (suivant la valeur de celui-ci) : + 42° 25' | - 42° 25'
'                  pour grades --> compteur omis : 55,75 --> 55,75 gr
'                                  compteur stipulé (suivant la valeur de celui-ci) : + 55,75 gr | - 55,75 gr
'- max : une valeur que ne peut dépasser la variable "NbDec"
'Magic_Doctor

Dim verif As Boolean, deg%, min As Byte, largo As Byte, pos As Byte, pref$
    
    On Error Resume Next                                        'sinon bordel incompréhensible
    
    If IsMissing(max) = False And NbDec > max Then NbDec = max  '"NbDec" <= "max"
    
    verif = IIf(NbDec = Int(NbDec), True, False)                'vérifie si NbDec est entier ou pas
    deg = Int(NbDec)                                            'nombre de degrés (à gauche de la virgule)
    largo = Len(NbDec): pos = InStr(NbDec, ",")
    min = IIf(verif = False, Right(NbDec, largo - pos), 0)      'nombre de minutes (à droite de la virgule)
    
    If IsMissing(compteur) Then
        If ChxDegGr = 1 Then                                    'saisie en degrés sexagésimaux
            ConvertirDecimal = deg & "°" & IIf(min = 0, "", " " & min & "'")
        Else                                                    'saisie en grades
            ConvertirDecimal = NbDec & " gr"
        End If
    Else
'        pref = IIf(compt(compteur) = 1, "+ ", "- ")             'symbole "+/-" qui précède la chaîne de caractères suivant la valeur de "compt(compteur)"
'        If ChxDegGr = 1 Then                                    'saisie en degrés sexagésimaux
'            ConvertirDecimal = IIf(deg = 0 And min = 0, "", pref) & deg & "°" & IIf(min = 0, "", " " & min & "'")
'        Else                                                    'saisie en grades
'            ConvertirDecimal = IIf(NbDec = 0, "", pref) & NbDec & " gr"
'        End If
    End If

End Function
 

Pièces jointes

  • Fonction.xlsm
    17.2 KB · Affichages: 27
Solution
Re,

laurent 950, je viens encore d'essayer, ça bloque à l'ouverture.
Comment vous faite la gestion du compteur avec IsMissing ?
Dans mon application il y a plusieurs compteurs (compt(1), compt(2)...). Chacun de ces compteurs a en général 2 options (mais on pourrait en mettre 36 pour le même compteur).
En paramétrage de la fonction on peut mettre un numéro de compteur (numéro qui se trouve entre les parenthèses qui suivent compt). Le compteur en paramétrage a 2 options : si 1 --> + | si 2 --> -

J'en profite pour glisser ma dernière mouture. Maintenant ça marche (!!!) et avec une seule fonction (en utilisant la seconde ça déconnait) :
VB:
Function ConvertirDecimal$(NbDec$, ChxDegGr As Byte, Optional compteur, Optional max)...

eriiic

XLDnaute Barbatruc
Bonjour,

le On Error Resume Next n'est jamais une bonne solution pour éliminer quelque chose qu'on ne comprend pas ;-)

Pas trop clair. Tu annonces vouloir une valeur indépassable et tu demandes pourquoi ça ne va pas au-dessus ?

Déjà en 1ère ligne tu compares 2 chaines "3.5" > "3.45" => True
1611184273194.png

je ne vais pas plus loin, je ne pense pas que c'est ce que tu veux faire.

Remarque : mon séparateur décimal est le . Il est un peu tard pour pour que je piusse plus loin, mais c'est dommage que ta fonction n'en tienne pas compte.
Mais même si je saisis des . je me retrouve avec une comparaison de chaines
eric
 
Dernière édition:

eriiic

XLDnaute Barbatruc
PS : tu m'as fais redémarrer le PC...
Plutôt que d'utiliser des variants pour pouvoir utiliser IsMissing, tu peux typer tes variables optionnelles et leur attribuer une valeur par défaut, par exemple :
Optional Max as double = -1 (soit inutilisée, soit utilisée et réellement par défaut)
Si omis => -1
Ainsi tu ne laiises plus vba choisir à ta place (mal parfois)
 
Dernière édition:

Magic_Doctor

XLDnaute Barbatruc
Bonsoir Eriiiic,

Pour le On Error, tout à fait d’accord (on se démerde comme on peut pour virer cette verrue, question d’honneur !), mais, pour la faire courte, en attendant de localiser la source du problème (si tant est que je la trouve…), je l’ai mis.

Pour le reste, ma foi, je pense avoir été clair. Il y a une valeur, supposons 3° 45’, que l’on ne peut dépasser. Si nous rentrons, par exemple, 5° 23’, automatiquement cette valeur entrée (qui est supérieure à 3° 45’) se convertira en 3° 45’. C’est en quelque sorte un contrôle de saisie : éviter au pèlerin de rentrer n’importe quoi.

Dans :
VB:
If IsMissing(max) = False And NbDec > max Then NbDec = max  '"NbDec" <= "max"
Je dis tout simplement que : si j’ai stipulé, en paramétrage, une valeur maximale et que le nombre entré est supérieur à celle-ci, alors ce même nombre entré prendra cette valeur maximale qu’il ne peut dépasser. J’aurais très bien pu mettre, dans ce cas de figure, un « ? ».

Quant au séparateur décimal, je suis définitivement de la vieille école. Je sais, c’est dépassé… Pourquoi pas aussi mettre systématiquement, dans un texte, un espace après chaque point ? Pour faire comme eux… En revanche, dans les calculs, cela ne me pose aucun problème.

Je vais regarder de plus près, après le dîner, ta dernière suggestion (moi aussi je déteste les IsMissing).

Bonne nuit
 

TooFatBoy

XLDnaute Barbatruc
Bonjour,

Juste une remarque à l'adresse d'eriiiic. ;)

Supposons que la valeur indépassable soit, par exemple, 3,45. Si je rentre dans la cellule :
3,3 --> 3° 3’
3,4 --> 3° 4’
3,44 --> 3° 44’
3,48 --> 3° 45’
3,57 --> 3° 45’

Mais, curieusement :
3,5 ou 3,6 ou 3,7 ou 3,8 ou 3,9 --> non pas 3° 5’ ou … 3° 9’, mais toujours 3° 45’.

Pas trop clair. Tu annonces vouloir une valeur indépassable et tu demandes pourquoi ça ne va pas au-dessus ?
En fait, ce qu'il faut bien comprendre, c'est que 3,5 ne donne pas logiquement 3° 50' comme on pourrait s'y attendre naturellement, mais donne 3° 5'.
Donc 3,5 doit être traité comme si c'était 3,05 et donc comme étant inférieur à 3,45...

Autrement dit, s'il n'y a qu'une seule décimale saisie, il faut considérer qu'il y a un 0 (zéro) entre la virgule et ladite décimale.
 
Dernière édition:

Magic_Doctor

XLDnaute Barbatruc
Re,

La nuit portant conseil, j'ai réglé le problème en un tournemain (voir PJ).
La notation que j'expose est une convention personnelle pour rentrer rapidement les données.
3,5 --> 3° 5'
3,50 --> 3° 50'

Quand je veux 50', je ne pense pas spontanément à 5. Et encore moins, quand je veux 5' à 05...

Si ce sont les degrés & minutes qui posent problème, imaginons qu'à la place de ° & ' on mette "poire" & "banane" :
3,5 --> 3poire 5banane
3,50 --> 3poire 50banane

Les fonctions :
VB:
Function ConvertirDecimal$(NbDec$, ChxDegGr As Byte, Optional compteur, Optional max)
'Retranscrit une valeur décimale en format TEXTE (qui ne doit pas dépasser 2 décimales après la virgule) en degrés & minutes ou grades
'- NbDec : un nombre entier ou décimal qui n'excède alors pas 2 chiffres après la virgule
'- ChxDegGr : si 1 --> degrés sous forme, par exemple : 42,75 <=> 42° 75'
'             si 2 --> grades
'- compteur : un numéro de compteur (1, 2, 3...)
'             si omis --> pas de signe +/- avant la chaîne de caractères
'             si stipulé --> un signe +/- (suivant la valeur du compteur) avant la chaîne de caractères
'             Ex : pour degrés --> compteur omis : 42,25 --> 42° 25'
'                                  compteur stipulé (suivant la valeur de celui-ci) : + 42° 25' | - 42° 25'
'                  pour grades --> compteur omis : 55,75 --> 55,75 gr
'                                  compteur stipulé (suivant la valeur de celui-ci) : + 55,75 gr | - 55,75 gr
'- max : une valeur que ne peut dépasser la variable "NbDec"
'Magic_Doctor

Dim verif As Boolean, deg%, min As Byte, pref$
    
    verif = IIf(NbDec = Int(NbDec), True, False)                           'vérifie si NbDec est entier ou pas
    deg = Int(NbDec)                                                       'nombre de degrés (à gauche de la virgule)
    min = IIf(verif = False, ChiffresApresVirgule(CDbl(NbDec), False), 0)  'nombre de minutes (à droite de la virgule)
    min = IIf(min > 59, 59, min)
    
    If IsMissing(max) = False Then
        If deg = Int(max) Then
            If min > ChiffresApresVirgule(CDbl(max), False) Then min = ChiffresApresVirgule(CDbl(max), False)
        End If
    End If
    
    If IsMissing(compteur) Then
        If ChxDegGr = 1 Then                                    'saisie en degrés sexagésimaux
            ConvertirDecimal = deg & "°" & IIf(min = 0, "", " " & min & "'")
        Else                                                    'saisie en grades
            ConvertirDecimal = NbDec & " gr"
        End If
    Else
'        pref = IIf(compt(compteur) = 1, "+ ", "- ")             'symbole "+/-" qui précède la chaîne de caractères suivant la valeur de "compt(compteur)"
'        If ChxDegGr = 1 Then                                    'saisie en degrés sexagésimaux
'            ConvertirDecimal = IIf(deg = 0 And min = 0, "", pref) & deg & "°" & IIf(min = 0, "", " " & min & "'")
'        Else                                                    'saisie en grades
'            ConvertirDecimal = IIf(NbDec = 0, "", pref) & NbDec & " gr"
'        End If
    End If

End Function
J'utilise aussi celle-ci :
VB:
Function ChiffresApresVirgule(dNum#, Optional Opt As Boolean = True)
'Renvoie le nombre de chiffres après la virgule ou tous les chiffres après la virgule
'- dNum : le chiffre à traiter
'- opt : si omis ou True --> le nombre de chiffres après la virgule
'        si False --> tous les chiffres après la virgule
'Ex : 125,587349 | opt omis ou True --> 6 (il y a 6 chiffres après la virgule)
'                  opt = False --> 587349 (tous les chiffres qui sont après la virgule)
'Incognito

Dim SepDec$, tmp, posDec, Nb As Double, cap$

    SepDec = Application.International(xlDecimalSeparator)
    tmp = CStr(dNum)
    posDec = InStr(tmp, SepDec)
    Nb = Len(tmp) - Len(Right(tmp, posDec)) 'nombre de chiffres après la virgule
    cap = Right(dNum, Nb)                   'chiffres après la virgule
  
    ChiffresApresVirgule = IIf(Opt, IIf(posDec = 0, 0, Nb), IIf(posDec = 0, 0, cap))

End Function
 

Pièces jointes

  • Fonction.xlsm
    19.4 KB · Affichages: 5
Dernière édition:

laurent950

XLDnaute Barbatruc
Bonjour @Magic_Doctor
Votre code ma donné des idées je me suis permis d'y travailler et de faire des tests
je vous poste ce que j'ai réalisé et j'ai aussi créer un poste
XL 2013 - Les Fonctions personnalisé avec Aide à la saisie et Aide boite de dialogue | Excel-Downloads (excel-downloads.com)

le but est aussi de consigné l'aide de cette fonction a travers une Boite de dialogue dynamique
mais aussi d'une aide à la saisie (Connaitre les arguments Obligatoire et ceux Faculcatif []) dans la chaine de saisie de la fonction. c'est une bonne aide. Mais je ne me souvient plus pour cette dernière et j'ai donc créer un Poste via votre exemple complet (un bon exemple)
 

Pièces jointes

  • Fonction (Magic_Doctor_Test).xlsm
    23.2 KB · Affichages: 5

laurent950

XLDnaute Barbatruc
Re @Magic_Doctor
Tous l'interet est de pouvoir consigné le code de cette fonction avec une aide
Via :
- la boite de dialogue
- Via la saisie au clavier

Avec cela les fonctions personnalisé sont prennent dans le temps.

Mon exemple est quand même pas mal fait, vous devriez reprendre mon Poste et corrigé pour que cela match

J'attend une réponse pour le complément des étiquettes des arguments dans la saisie des fonctions

Merci @Magic_Doctor
 

Magic_Doctor

XLDnaute Barbatruc
Re,

À l'ouverture de votre fichier, il y a un problème :
argumentdescriptions

Une dernière petite modification pour la fonction :
VB:
Function ConvertirDecimal$(NbDec$, ChxDegGr As Byte, Optional compteur, Optional max)
'Retranscrit une valeur décimale en format TEXTE (qui ne doit pas dépasser 2 décimales après la virgule) en degrés & minutes ou grades
'- NbDec : un nombre entier ou décimal qui n'excède alors pas 2 chiffres après la virgule
'- ChxDegGr : si 1 --> degrés sous forme, par exemple : 42,75 <=> 42° 75'
'             si 2 --> grades
'- compteur : un numéro de compteur (1, 2, 3...)
'             si omis --> pas de signe +/- avant la chaîne de caractères
'             si stipulé --> un signe +/- (suivant la valeur du compteur) avant la chaîne de caractères
'             Ex : pour degrés --> compteur omis : 42,25 --> 42° 25'
'                                  compteur stipulé (suivant la valeur de celui-ci) : + 42° 25' | - 42° 25'
'                  pour grades --> compteur omis : 55,75 --> 55,75 gr
'                                  compteur stipulé (suivant la valeur de celui-ci) : + 55,75 gr | - 55,75 gr
'- max : une valeur que ne peut dépasser la variable "NbDec"
'Magic_Doctor

Dim verif As Boolean, deg%, min As Byte, pref$
   
    verif = IIf(NbDec = Int(NbDec), True, False)                           'vérifie si NbDec est entier ou pas
    deg = Int(NbDec)                                                       'nombre de degrés (à gauche de la virgule)
    min = IIf(verif = False, ChiffresApresVirgule(CDbl(NbDec), False), 0)  'nombre de minutes (à droite de la virgule)
    min = IIf(min > 59, 59, min)
   
    If IsMissing(max) = False Then
        If deg >= Int(max) Then deg = Int(max): min = ChiffresApresVirgule(CDbl(max), False)
    End If
   
    If IsMissing(compteur) Then
        If ChxDegGr = 1 Then                                    'saisie en degrés sexagésimaux
            ConvertirDecimal = deg & "°" & IIf(min = 0, "", " " & min & "'")
        Else                                                    'saisie en grades
            ConvertirDecimal = NbDec & " gr"
        End If
    Else
'        pref = IIf(compt(compteur) = 1, "+ ", "- ")             'symbole "+/-" qui précède la chaîne de caractères suivant la valeur de "compt(compteur)"
'        If ChxDegGr = 1 Then                                    'saisie en degrés sexagésimaux
'            ConvertirDecimal = IIf(deg = 0 And min = 0, "", pref) & deg & "°" & IIf(min = 0, "", " " & min & "'")
'        Else                                                    'saisie en grades
'            ConvertirDecimal = IIf(NbDec = 0, "", pref) & NbDec & " gr"
'        End If
    End If

End Function
 

Pièces jointes

  • Fonction.xlsm
    19.3 KB · Affichages: 6

TooFatBoy

XLDnaute Barbatruc
La notation que j'expose est une convention personnelle pour rentrer rapidement les données.
3,5 --> 3° 5'
3,50 --> 3° 50'

Quand je veux 50', je ne pense pas spontanément à 5. Et encore moins, quand je veux 5' à 05...

Si ce sont les degrés & minutes qui posent problème, imaginons qu'à la place de ° & ' on mette "poire" & "banane" :
3,5 --> 3poire 5banane
3,50 --> 3poire 50banane
On a bien compris que ce n'est qu'une convention personnelle, une façon de coder tes données pour la saisie.

Ce qui trouble le commun des mortels c'est qu'elle parait totallement illogique puisqu'elle semble aller à l'encontre des conventions internationales qui veulent que 3,5 est égal à 3,50 (et aussi égal à 3,500, à 3,5000, etc.).

Le fait d'utiliser des pommes, des poires ou des scoubidoubidous ne changent rien à la troublitude qu'apporte ta notation personnelle.
C'est bien évidemment la virgule qu'il faut changer pour faire disparaître instantanément ce trouble.

Par exemple, avec une espace ça donne ceci :
3 5 => 3° 5'
3 50 => 3° 50'
C'est déjà moins troublant car beaucoup plus logique. Non ? ;)

ps : j'ai bien compris que l'espace est infiniment moins pratique que la virgule lors de la saisie.
(enfin... surtout pour une personne n'utilisant qu'une seule main)
 

Magic_Doctor

XLDnaute Barbatruc
Re,

laurent 950, je viens encore d'essayer, ça bloque à l'ouverture.
Comment vous faite la gestion du compteur avec IsMissing ?
Dans mon application il y a plusieurs compteurs (compt(1), compt(2)...). Chacun de ces compteurs a en général 2 options (mais on pourrait en mettre 36 pour le même compteur).
En paramétrage de la fonction on peut mettre un numéro de compteur (numéro qui se trouve entre les parenthèses qui suivent compt). Le compteur en paramétrage a 2 options : si 1 --> + | si 2 --> -

J'en profite pour glisser ma dernière mouture. Maintenant ça marche (!!!) et avec une seule fonction (en utilisant la seconde ça déconnait) :
VB:
Function ConvertirDecimal$(NbDec$, ChxDegGr As Byte, Optional compteur, Optional max)
'Retranscrit une valeur décimale en format TEXTE (qui ne doit pas dépasser 2 décimales après la virgule) en degrés & minutes ou grades
'- NbDec : un nombre entier ou décimal qui n'excède alors pas 2 chiffres après la virgule
'- ChxDegGr : si 1 --> degrés sous forme, par exemple : 42,75 <=> 42° 75'
'             si 2 --> grades
'- compteur : un numéro de compteur (1, 2, 3...)
'             si omis --> pas de signe +/- avant la chaîne de caractères
'             si stipulé --> un signe +/- (suivant la valeur du compteur) avant la chaîne de caractères
'             Ex : pour degrés --> compteur omis : 42,25 --> 42° 25'
'                                  compteur stipulé (suivant la valeur de celui-ci) : + 42° 25' | - 42° 25'
'                  pour grades --> compteur omis : 55,75 --> 55,75 gr
'                                  compteur stipulé (suivant la valeur de celui-ci) : + 55,75 gr | - 55,75 gr
'- max : une valeur que ne peut dépasser la variable "NbDec"
'Magic_Doctor

Dim verif As Boolean, deg1%, deg2%, min1 As Byte, min2 As Byte, largo As Byte, pos As Byte, pref$
 
    verif = IIf(NbDec = Int(NbDec), True, False)               'vérifie si "NbDec" est entier ou pas
    deg1 = Int(NbDec)                                          'nombre de degrés (à gauche de la virgule)
    largo = Len(NbDec): pos = InStr(NbDec, ",")
    min1 = IIf(verif = False, Right(NbDec, largo - pos), 0)    'nombre de minutes (à droite de la virgule)
    min1 = IIf(min1 > 59, 59, min1)                            'les minutes ne peuvent excéder 59
 
    If IsMissing(max) = False Then
        verif = IIf(max = Int(max), True, False)               'vérifie si "max" est entier ou pas
        deg2 = Int(max)                                        'nombre de degrés (à gauche de la virgule)
        largo = Len(max): pos = InStr(max, ",")
        min2 = IIf(verif = False, Right(max, largo - pos), 0)  'nombre de minutes (à droite de la virgule)
        If deg1 = deg2 And min1 > min2 Then min1 = min2
        If deg1 > deg2 Then deg1 = deg2: min1 = min2
    End If
 
    If IsMissing(compteur) Then
        If ChxDegGr = 1 Then                                   'saisie en degrés sexagésimaux
            ConvertirDecimal = deg1 & "°" & IIf(min1 = 0, "", " " & min1 & "'")
        Else                                                   'saisie en grades
            ConvertirDecimal = NbDec & " gr"
        End If
    Else
'        pref = IIf(compt(compteur) = 1, "+ ", "- ")            'symbole "+/-" qui précède la chaîne de caractères suivant la valeur de "compt(compteur)"
'        If ChxDegGr = 1 Then                                   'saisie en degrés sexagésimaux
'            ConvertirDecimal = IIf(deg1 = 0 And min1 = 0, "", pref) & deg1 & "°" & IIf(min1 = 0, "", " " & min1 & "'")
'        Else                                                   'saisie en grades
'            ConvertirDecimal = IIf(NbDec = 0, "", pref) & NbDec & " gr"
'        End If
    End If

End Function

Marcel32, en l'occurrence, je m'en tamponne le coquillard des conventions internationales ! Vous n'en avez pas marre des règlements... Il n'y a absolument rien de trouble quant à mon choix arbitraire. Plutôt que d'utiliser 2 cellules (une pour les degrés, l'autre pour les minutes), je rentre ces 2 données à la fois sous forme d'un seul nombre décimal : à gauche de la virgule ce sont les degrés, à droite les minutes et basta (où diable est donc le problème ?). Il n'y a pas de secondes, puisque sur les cartes, pour la déclinaison magnétique et sa variation annuelle, il n'y a, pour les degrés (sexagésimaux), que les degrés et les minutes et, pour les grades, que 2 décimales après la virgule. En effet, ce n'est pas de la précision à la sauce Exocet... mais je m'en tiens tout simplement à ce qui figure sur les cartes. Enfin, la virgule me paraît nettement plus parlante, lors d'une saisie, que l'espace (voir PJ) ; mais là c'est un détail superfétatoire (on pourrait tout aussi bien mettre un slash ou ce que vous voudrez).
 

Pièces jointes

  • Fonction.xlsm
    20 KB · Affichages: 7
Dernière édition:

Statistiques des forums

Discussions
315 109
Messages
2 116 322
Membres
112 717
dernier inscrit
doguet