VBA : Arguments multiples pour un même paramètre par l’exemple

Certaines syntaxes permettent de passer plusieurs arguments dans le paramètre. C’est le cas de la commande MsgBox par exemple, où le paramètre Buttons accepte plusieurs valeurs de l’énumérateur vbMsgBoxStyle.

Il est assez simple d’utiliser cette technique dans vos propres commandes ou fonctions. Voici comment procéder :

L’énumérateur

Commencez par créer un module standard pour accueillir la fonction. Dans l’en-tête, après la ligne Option Explicit, nous allons définir l’énumérateur. L’énumérateur est une simple structure de données de variable.





Option Compare Database
Option Explicit

Public Enum efFichierExt
    Unite = 8
    Chemin = 16
    Fichier = 32
    Extension = 64
End Enum

Pour en savoir plus sur l’énumération en VBA, son potentiel et ses bienfaits dans votre code, consultez mon tutoriel : http://loufab.developpez.com/tutoriels/access/enumVBA/

Il est très important que les valeurs de l’enum soit en base 8, comme vous le verrez par la suite.

La fonction

L’écriture de la fonction est assez triviale. La signature de la fonction fait référence à l’enum comme un type classique.

Public Function fFichierExt(strCheminFichier As String, _
                            iType As efFichierExt) As String

'-----------------------------------------------------------------

Le paramètre iType est donc de type efFichierExt, notre enum.

Maintenant, pour tester la présence des différentes valeurs dans iType, il ne faut pas utiliser les opérateurs logiques (=, <>…) habituels mais AND.

Voici comme on procède :

If iType And Unite Then      ' l'unité
   vRetour = Left(strCheminFichier, InStr(strCheminFichier, ":"))
End If

iType And Unite est l’équivalent de « Est-ce que iType contient Unite ? »

Voici la fonction complète qui permet d’extraire de parties du chemin d’un fichier.





Public Function fFichierExt(strCheminFichier As String, _
                iType As efFichierExt) As String
'--------------------------------------------------------------
' Procedure : fFichierExt
' Author    : Fabrice CONSTANS (MVP) blogaccess.free.fr
' Date      : 13/03/2013
' Purpose   : Retourne l'un des éléments suivant le chemin/fichier '               passé
' Parametres:
' strCheminFichier contient le chemin et fichier
' strType = enum eTypeFichierExt
'    64   renvoi l'extension du fichier sans le point
'    32   renvoi le nom du fichier sans son extension
'    16   renvoi le chemin sans le nom ni l'extension
'    8    renvoi l'unité
'--------------------------------------------------------------
   On Error GoTo Errsub

   Dim vRetour As String
   If iType And Unite Then      ' l'unité
        
     vRetour = Left(strCheminFichier, InStr(strCheminFichier, ":"))

   End If
   If iType And Chemin Then     ' le chemin

      vRetour = vRetour & Mid(strCheminFichier, 3, _
                  InStrRev(strCheminFichier, "\") - 2)

   End If
   If iType And Fichier Then

      Dim tmpFic As String

      If strCheminFichier Like "*.*" Then

         tmpFic = Right(strCheminFichier, _
         Len(strCheminFichier) - InStrRev(strCheminFichier, "\"))
         vRetour = vRetour & Left(tmpFic, InStrRev(tmpFic, ".") - 1)
      Else
         vRetour = strCheminFichier
      End If

  End If

    
  If iType And Extension Then   ' renvoi l'extension
     If iType And Fichier Then vRetour = vRetour & "."
        vRetour = vRetour & Right(strCheminFichier, _
           Len(strCheminFichier) - InStrRev(strCheminFichier, "."))
  End If
  fFichierExt = vRetour
  Exit Function

Errsub:
   
'ici utiliser votre propre traitement d'erreur
Exit Function

End Function

Comme vous le voyez, iType est analysé plusieurs fois. A chaque analyse on concatène ou non l’élément souhaité.

La première analyse détermine si l’on doit renvoyer l’unité, la deuxième le chemin etc.

L’appel se fait de la manière suivante :

? fFichierExt("c:\windows\temp\monfichier.tmp", Fichier + Extension)

On remarque que, comme pour MsgBox, les paramètres multiples sont additionnés.

Dans cet exemple le résultat sera :

monfichier.tmp

On peut donc renvoyer tout ou partie de la valeur passée. Par exemple :

? fFichierExt("c:\windows\temp\monfichier.tmp", Unite + Chemin + _
                    Fichier + Extension)

Evidemment, vous pouvez faire l’addition dans l’ordre que vous souhaitez, le résultat sera toujours le même.

? fFichierExt("c:\windows\temp\monfichier.tmp", Extension + Fichier)

Conclusion

L’utilisation d’un enum en base 8 et de l’opérateur logique AND permet de passer autant de paramètres que l’on souhaite. Encore faut-il en avoir l’utilité…

Vous pouvez utiliser la fonction ci-dessus dans vos applications dans la mesure où vous ne modifiez pas le nom de l’auteur.

Erreur 3061 : Trop peu de paramètres 1 requis

Ce message est assez commun lorsque vous manipulez des requêtes dans VBA. Nous allons voir pourquoi cela s’affiche et quelles sont les solutions à y apporter. Dans l’exemple suivant tout est correct , aucune erreur n’est déclenchée.

Sub getName()
Dim db As dao.Database
Dim rst As dao.Recordset

Set db = CurrentDb
Set rst = db.OpenRecordset("SELECT [Nom] FROM " & _
          "tCONTACT WHERE Nom='Martin';", dbOpenSnapshot)

MsgBox "son nom est " & rst.Fields("Nom").Value

rst.Close
Set rst = Nothing
Set db = Nothing

End Sub

Lorsqu’on utilise cette requête avec un paramètre issue d’un formulaire comme ci-dessous :

Sub getName()

...
Set rst = db.OpenRecordset("SELECT [Nom] FROM " & _
          "tCONTACT WHERE Nom=forms.fChercher.txt_nom;", dbOpenSnapshot)

MsgBox "son nom est " & rst.Fields("Nom").Value
...

End Sub

Voici le message Erreur 3061 Trop peu de paramètres… qui apparaît :

Erreur 3061 Trop peu de paramètres. 1 attendu.
Erreur 3061 Trop peu de paramètres. 1 attendu.

La seule solution à ce problème est que les paramètres de provenant de l’IHM, du Form, soient interprétés non plus par le moteur de base de données comme avec ce code mais directement par VBA.

1ère Solution :

La partie IHM est directement interprétée par VBA qui n’envoie que la valeur au moteur de base de données.

Set rst = db.OpenRecordset("SELECT [Nom] FROM tCONTACT " & _

          "WHERE Nom=""" & Forms.fChercher.txt_nom & """;", dbOpenSnapshot)

C’est ceci qui est envoyé au moteur de base de données :

SELECT [Nom] FROM tCONTACT WHERE Nom="Martin";

2ème Solution :

Set rst = db.OpenRecordset("SELECT [Nom] FROM tCONTACT " & _

          "WHERE Nom=Eval('Forms.fChercher.txt_nom');", dbOpenSnapshot)

Ici c’est un peu différent, c’est la fonction VBA Eval() qui fait la liaison entre le moteur de base de données et l’interface (formulaire). Eval() résout l’expression Forms et renvoi sa valeur.

3ème Solution :

Celle-ci est un peu plus complexe car elle nécessite l’écriture d’une fonction utilisateur.

Set rst = db.OpenRecordset("SELECT [Nom] FROM tCONTACT " & _
           "WHERE Nom=fDonneNom();", dbOpenSnapshot)

Dans un module standard écrivez cette fonction :

Public Function fDonneNom() As Variant
    fDonneNom = Forms.fChercher.txt_nom
End Function

Ce cas est plutôt à envisager lorsque la valeur renvoyée est le résultat d’une opération complexe comme un calcul, une concaténation…

Public Function fDonneNom() As String
    fDonneNom = Forms.fChercher.txt_nom
End Function

4ème Solution :

Cette solution est à envisager surtout si vous avez de nombreux paramètres issus de l’IHM ou qu’ils sont hétérogènes ; issus de plusieurs sources (VBA, résultat de requête, IHM…).

Commencez par modifier la requête en utilisant la clause PARAMETERS. Vous pouvez utiliser le QBE (Query By Exemple), autrement dit l’éditeur de requêtes de Microsoft Access, en Mode Création pour créer cette clause et l’alimenter.

Le menu Parameters.
Le menu Parameters.

Remplissez le tableau qui apparaît avec un paramètre par ligne et réglez son type (texte, date, entier…). Une fois ceci effectué placez vos paramètres sur la ligne de critères.

Si vous basculer en mode SQL, voici ce que vous pourrez observer :

PARAMETERS [txt_nom] Text ( 255 ), [NumSociete] Long;

SELECT tContact.Nom, tContact.Prenom, tContact.IdSociete 
FROM tContact

WHERE (((tContact.Nom) Like [txt_nom]) 
AND ((tContact.IdSociete)=[NumSociete]));

 La clause PARAMETERS et ses arguments sont terminés par un point-virgule. Coté VBA voici comment utiliser les PARAMETERS de cette requête.

Sub getName()

Dim db As DAO.Database
Dim qry As DAO.QueryDef
Dim rst As DAO.Recordset
Dim sql As String

Set db = CurrentDb

'composition de la requete SQL
sql = "PARAMETERS [txt_nom] Text ( 255 ), [NumSociete] Long; "
sql = sql & " SELECT tContact.Nom, tContact.Prenom, " 
sql = sql & "        tContact.IdSociete"
sql = sql & " FROM tContact"
sql = sql & " WHERE tContact.Nom Like [txt_nom] "
sql = sql & "     AND tContact.IdSociete=[NumSociete];"

'creation de la requete
Set qry = db.CreateQueryDef("rqtemporaire", sql)

'affectation des valeurs aux parametres
qry.Parameters("txt_nom") = Forms.FChercher.txt_nom
qry.Parameters("NumSociete") = Forms.FChercher.numSociete

'composition du recordset
Set rst = qry.OpenRecordset(dbOpenSnapshot)

'Resultat
MsgBox "Il s'apelle " & rst.Fields("Nom").Value

rst.Close
qry.Close

Set qry = Nothing
Set rst = Nothing
Set db = Nothing

End Sub

Comme vous le voyez il faut passer par un objet Query pour accéder aux Parameters, puis faire un recordset à partir de ce dernier. C’est plus complexe mais lorsque vous avez de nombreuses valeurs à passer à la requête vous gagnez du temps.