VBA : Opérateur Like commun à SQL, VBA et aux Expressions

L’opérateur logique LIKE est un opérateur d’égalité strictement réservé aux chaines de type texte. Sa particularité est de pouvoir comparer des valeurs à un masque composé de caractères de substitution.

Glossaire

Dans cet article : Masque, caractères de substitution, masque de comparaison, règles désigne la deuxième partie du Like.

Comme le = mais plus flexible

Il s’utilise comme n’importe quel opérateur logique soit :

[Expression à comparer] Like [masque de comparaison]

Retourne un True, False ou Null.

'En VBA
If ExpressionAcomparer Like ExpressionLike then

'Expression
=iif(ExpressionAcomparer Like ExpressionLike;True;False)

'SQL
... WHERE ExpressionAcomparer Like ExpressionLike ...

La syntaxe est identique en SQL (*), VBA ou dans les expressions.

(*) Les moteurs de base de données utilisent plutôt les jockers ANSI.
* devient % et ? devient _.

Les jokers

Les jokers sont des caractères de substitution permettant de composer le masque de comparaison. Ils sont combinables.

L’astérisque *

L’astérisque * remplace plusieurs caractères alphanumériques et spéciaux.

ExpressionDescription
« abcde45é# » like « abc* »Toute chaine commençant par abc
« 01è+abc » like « *abc »Toute chaine finissant par abc
« 01abc_ç » like « *abc* »Toute chaine contenant abc
« ab(6ç1c » like « ab*c »Toute chaine commençant pas ab et finissant par c

    Le point d’interrogation ?

    Le point d’interrogation ? remplace un seul caractère quel qu’il soit : lettre, chiffre, caractère spécial.

    ExpressionDescription
    « A » Like « ? »Toute chaine contenant 1 caractère
    « abcD » like « abc? »Toute chaine commençant par abc avec 1 caractère à la fin.
    « 0adc » like « ?abc »Toute chaine commençant par 1 caractère et finissant par abc.
    « 0abc) » like « ?abc? »Toute chaine contenant abc avec 1 caractère de part et d’autre.
    « x4z » like « ??? » Toute chaine contenant 3 caractères contigus.

      Comme le point d’interrogation remplace 1 caractère on peut en utiliser plusieurs à la suite dans un masque de comparaison.

      Le dièse #

      Le caractère dièse # fonctionne comme le point d’interrogation mais remplace un chiffre, donc ni lettre, ni un caractère spécial.

      ExpressionDescription
      « 1 » like « # »un chiffre de 0 à 9
      identique à [0-9]
      « 100.99 » like « ###.## »3 chiffres et 1 point et 2 chiffres
      « +100.99 » like « [!-]###.## » »
      « A25X17 » like « [A-C]##[!W]## »A, B ou C, 2 chiffres, pas de W, 2 chiffres

      Les crochets [ ]

      Les crochets permettent de remplacer un caractère situé dans une plage particulière. Il a aussi comme fonction de pouvoir utiliser les caractères de substitutions en tant que caractères numériques.

      ExpressionDescription
      « B » Like « [A -C] » Le tiret détermine une plage de caractères contigus entre 2 bornes. Ici A, B ou C.
      « C » Like « [A,C,E] »La virgule, détermine une série de caractères. Ici A, C ou E.
      « ANZ » Like « A[M-O][W,Z] »La combinaison des 2. Ici A, M ou N ou O et W ou Z.
      « B2 » Like « [A-C][!1] »A, B, C mais pas 1.
      « Cbc3 » Like « [A-D]bc[!0] »A à D puis bc puis <>0
      « abc2 » vrai !
      « add2 » faux ! (pas de bc)
      « abc0 » faux ! (0 à la fin)
      « rL5 » Like « [a-z][A-Z][0-9] » Uniquement en VBA avec un Option Compare Binary !
      La réponse doit contenir 3 digits. 1 lettre en minuscule, 1 en majuscule, un chiffre.

      « aB1 » vrai !
      « AX9 » faux ! A est en majuscule.
      « aAd » faux ! pas de chiffre à la fin.
      « aB123 » faux ! 2 et 3 en trop.
      « abR5 » faux ! a ou b en trop.
      « BMW10 » Like « [A-C]M[S-Z]10 »A,B ou C et M et X,S à Z et 10
      « ‘bonjour' » Like « [«  »,’]*[«  »,’] »On teste si la chaine est entourée de  » ou ‘. (*)

      (*) Ceci n’est que pour l’exemple, cette comparaison n’est pas du tout robuste.

      Crochets avec des caractères spéciaux

      Avec des caractères spéciaux la musique est la même. Seules les paroles changent.

      ExpressionDescription
      « Ä » like « [À-Æ] »du 0192 au 0198
      « ) » like « [!-/] »du 0033 au 0047

      Ce qui ne fonctionne pas avec les crochets

      Les combinaisons ne fonctionnent pas. Il faut obligatoirement séparer vos règles. Ici quelques exemples.

      Expression FAUSSEDescriptionExpression VRAI
      « [Z-A] »Toujours respecter l’ordre de tri. « [A-Z] »
      « [8-5] »Toujours respecter l’ordre de tri.« [5-8] »
      « [A-C,!1] »Pas d’assemblage« [A-C][!1] »
      « [a-z,A-Z,0-9] »Pas d’assemblage« [a-z][A-Z][0-9] »
      « [a-z!] »La négation toujours au début« [!a-z]

      Attention ! Les plages de caractères doivent toujours être placées dans l’ordre croissant. Par exemple [A-C] est valide alors que [C-A] ne l’est pas, de même que [0-9] est valide et [9-0] ne l’est pas. Pour déterminer l’ordre des caractères, consultez la table ASCII et réalisez des tests.

      La négation

      On peut utiliser 2 types de négation avec l’opérateur Like.

      Le Not habituel qui englobe l’ensemble de la comparaison, comme dans l’exemple suivant

      ExpressionDescription
      Not « abc » Like « a?c »Ne correspond pas
      Not « abc » Like « a*c »Ne correspond pas

      Le ! point d’exclamation dans les crochets du masque de comparaison. Dans ce cas on obtient plus de finesse dans la comparaison.

      ExpressionDescription
      « abc » Like « [!a]bc »Faux ! le premier caractère ne doit pas être « a ».
      « 1bc » Like « [!a-z]bc »Vrai ! le premier caractère n’est pas un caractère compris entre a et z.

      Rechercher les caractères génériques

      On peut être emmené à rechercher l’un des caractères de substitution dans une chaîne. Quelques exemples dans le tableau suivant.

      ExpressionDescription
      « Es-tu là ? » Like « Es-tu là [?] »Le point d’interrogation à la fin d’une phrase.
      « Es-tu là ! » Like « Es-tu là ? »Un caractère à la fin. ? ou autre.
      « 45 » like « []## »Pas d’espace en début de chaine.

      « AB » Like « A[]B »
      Pas d’espace entre A et B
      « A B » est faux !
      « [Vrai] » Like « [[]Vrai] »Commence par le crochet ouvrant [.
      Le crochet fermant ] n’a pas besoin d’être isolé.
      « #N/A » Like « [#]N/A »Une chaîne commençant par le caractère #.
      «  » »abc » » » Like «  » »??? » » »Une chaine commençant par  » et finissant par « .
      « AOC « Médoc » 33″ like « AOC «  »* » » ## »AOC « une chaine » 33
      «  » »abc » » » Like chr(34) & « abc » & chr(34)On peut aussi utiliser ce genre de notation où on utilise chr(34) pour retourner le « .

      Avec d’autres types de données

      Le LIKE peut fonctionner avec d’autres types de données mais les valeurs testées seront transformées en texte pour leur évaluation.

      Par exemple avec des dates on peut rechercher des séries.

      ExpressionDescriptionEquivalent VBA
      date() Like « ##/##/2032 »nous somme en 2032Year(date)=2009
      date() Like « ##/1#/2032 »Nous sommes au 4e trimestre 2032Format(Date, »q »)=4 (*)
      date() Like « 10/##/#### »Nous somme le 10Day(date)=10

      (*) Dans les expressions la virgule doit être remplacée par un point virgule.
      Format(Date; »q »)

      On peut trouver certains intérêts à utiliser un LIKE plutôt qu’une expression plus académique, mais attention aux résultats qui peuvent être surprenants et loin de vos attentes. Un Like n’est pas forcément plus performant par rapport aux fonctions prévues.

      (*) t en SQL, q en VBA. On peut également utiliser la fonction DatePart().

      Quelques tests dans la fenêtre d’exécution de VBE

      Vous pouvez vous entrainer dans la fenêtre d’exécution pour concevoir vos masques.

      ? "ceci#est [la preuve] par 9 ?" LIKE "ceci[#]*[[]la preuve[]]*[0,9]?[?]"
      Vrai
      

      La casse

      Vous avez pu remarquer que Like n’est pas sensible à la casse lors de la comparaison.
      Il y a cependant un moyen d’en tenir compte. Pour cela il faut modifier le paramètre Option Compare situé dans l’en-tête du module VBA qui contient l’instruction.

      Option Compare Database
      Option Explicit
      OptionDescription
      Option Compare DatabaseNe tient pas compte de la casse
      « AAA » est égal à « aaa »
      Option Compare BinaryTient compte de la casse
      « AAA » est différent de « aaa »

      Pour une prise en charge de la casse dans vos requêtes vous avez d’autres solutions :

      • Changer de moteur de base de données. Ms Sql server, Mariadb, PostgreSQL, Oracle prennent en charge la casse.
      • Créer votre propre fonction VBA que vous appellerez depuis vos requêtes. Voir l’exemple ci-dessous.

      Mettez cette fonction dans un module Standard.

      Option Compare Binary  'respect de la casse
      Option Explicit
      
      Public Function isLike(value As Variant, masque As Variant) As Variant
      'Fabrice Constans (C2ED)
      'Tient compte d'un passage de Null et de son retour.
      'Appel de la fonction dans tous les composants d'Access
      'VBA : If isLike(date() ,"##/##/2032") then
      'Expression : =Iif(isLike(date() ;"##/##/2032");"Vrai";"False")
      'SQL : SELECT madate FROM matable WHERE isLike(madate ,"##/##/2032");
      
          isLike = value Like masque
      
      End Function
      
      

      Attention à la valeur Null !

      Le Null est retourné lorsque au moins l’une des expressions vaut Null.

      ExpressionDescription
      Null Like « * »La valeur à comparer vaut Null
      « abc » Like NullLe masque de comparaison vaut Null
      Null Like NullLes 2 côté de la comparaison valent Null
      ? Null Like "*"
      Null
      ? "giraphe sophie" Like Null
      Null
      ? Null Like null
      Null

      Conclusion

      En conclusion voici un opérateur bien utile qui rempli 90% des demandes en toutes circonstances. Il pêche cependant par le manque de prise en compte de la casse avec le moteur de base de données interne d’Access, ACE et dans les expressions des formulaires, état et macros. Ceci peut être rapidement contourné avec la petite fonction isLike() indiquée dans cet article.
      Pour des besoins plus poussés n’hésitez pas à utiliser RegEx. Si le moteur ACE ne le prend pas en charge la plupart des autres moteurs de base de données ont intégré RegEx dans leur SQL.

      VBA : Replace VBA boosté au RegEx

      La fonction Replace() de VBA est largement employée par les développeurs. Mais lorsqu’ils souhaitent faire une recherche moins statique, ils regardent avec envie l’opérateur Like sans pouvoir faire un mix des 2.

      Etant confronté à des traitements de chaines de caractères, je me suis créé une petite fonction dérivée du Replace, avec une touche de VBScript.RegEx.

      Voici ce que cela donne :

      Function ReplaceReg(ByVal expression As String, ByVal findPattern As String, ByVal replace As String, _
                          Optional ByVal caseSensitive As Boolean = False) As String
      '---------------------------------------------------------------------------------------
      ' Procedure : ReplaceReg
      ' Author    : Fabrice CONSTANS (C²ED - c2ed.software@gmail.com)
      ' Date      : 24/09/2024
      ' Purpose   : Un Replace() VBA avec du RegEx
      ' Parameters: expression à analyser, findPattern pattern regex, replace ce qui doit être
      '             mis à la place du pattern, caseSensitive tenir compte de la casse. 
      ' Copyright : © L'intégralité du code ci-dessous est la propriété de son auteur (Author)
      '             Son utilisation est strictement limitée au présent logiciel.
      ' Pattern regex : https://documentation.help/MS-VBScript-fr/vsproPattern.htm
      ' Exemple de patterns :
      'LIKE      RegEx
      'chaton*   ^chaton.
      'chat*n    chat.n
      'chat[A,E] chat[AE]
      'chat=,    chat=.,
      'chat      chat$
      '---------------------------------------------------------------------------------------
      
          On Error GoTo Errsub
          Dim regEx As Object
          Set regEx = CreateObject(VBScript.RegExp)
          
          With regEx
              .pattern = pattern
              .Global = True
              .IgnoreCase = Not caseSensitive
          End With
          
          ReplaceReg = regEx.replace(expression, replace)
          Exit Function
      
      Errsub
          ReplaceReg = inputText ' retourne le texte original en cas d’erreur
          
      End Function

      Le contrat d’utilisation

      • expression : texte à modifier.
      • findPattern : pattern RegEx conforme au standard VBScript.Regex
      • replace : texte de remplacement
      • caseSensitive indique si la casse doit être prise en compte.

      Les patterns

      Les expressions régulières RegEx sont bien plus puissant que le l’opérateur Like, mais plus complexe à maitriser.
      En revanche, l’opérateur Like est disponible à la fois SQL et en VBA, où il fonctionne de la même manière.

      Remplacement global

      Comme le Replace dans son utilisation le plus basique, notre fonction remplace toutes les occurrences rencontrées. C’est la propriété Globle=True qui garantie se comportement. Si on utilise False, seule la première occurrence sera traitée.

      Perfectible ? Toujours !

      La fonction est perfectible : on pourrait y ajouter des options comme start ou count pour limiter les remplacements. Mais dans la majorité des cas, elle fait le job.

      • start qui exclue les X premiers caractère de l’expression
      • count qui opère X remplacement

      Je n’utilise jamais le Start, par contre le Count peut s’avérer utile dans certains cas.

      Exemple d’utilisation

      ? replaceReg("ODBC;DRIVER={MySQL ODBC 8.0 UNICODE Driver};SERVER=192.168.10.1;DATABASE=baseLocale;Option=43;charset=UTF8;",";SERVER=.*?;",";SERVER=azure.microsoft.com;")
      ODBC;DRIVER={MySQL ODBC 8.0 UNICODE Driver};SERVER=azure.microsoft.com;DATABASE=baseLocale;Option=43;charset=UTF8;

      On a remplacé n’importe quelle adresse du server par ;SERVER=azure.microsoft.com;

      Conclusion

      Si vous traitez des chaînes complexes en VBA, pensez RegEx. Et si vous aimez cette fonction, n’hésitez pas à l’utiliser — en conservant l’en-tête bien sûr.

      VBA : Classe et gain de temps

      Le concept de classe en VBA a quelques lacunes mais il ne doit pas être négligés pour autant.

      En effet, grâce à elles on peut gagner un temps précieux. Voici un petit exemple fonctionnel d’une classe qui gère les évènements DblClick et NotInList d’une zone de liste déroulante. Le DblClick permet d’ouvrir un formulaire de saisie/édition avec l’item de la liste, tandis que NotInList permet de saisir l’item.

      Créer le module de classe

      Dans VBE faites Insertion/Module de classe. En bas à gauche dans la fenêtre des propriétés vous devez renseigner le nom de l’instance : cComboBox

      Saisissir

      Saisissez la première ligne de code suivante immédiatement après les lignes Option.

      Private WithEvents LmCombo          As Access.ComboBox

      WithEvents indique que cet objet pourra lever des évènements.

      Access.ComboBox est le type Liste déroulante.

      Ensuite nous aurons besoin de variables de type String pour stocker différents éléments nécessaires à l’ouverture du formulaire.

      'nom du formulaire qui est appelé lors du notinlist
      Private strFormNameLie     As String       
         'nom du champ id de la table recevant la nouvelle valeur
      Private strIdFieldName     As String       
         'nom du champ recevant la valeur saisie dans la combo 
      Private strControlName     As String       
         'nom de la table recevant la nouvelle valeur
      Private strTableFormLie    As String

      En suivant nous écrivons deux méthodes habituelles dans les classes.

      La Class_Initialize est exécutée au démarrage de la classe. Elle ne contient rien dans cet exemple mais si vous devez initialiser une variable ou une propriété c’est ici que vous devrez le faire.

      Private Sub Class_Initialize()
      ' Initialise
      
      End Sub

      Tandis que Class_Terminate est exécuté lorsqu’on libère la classe, qu’on la décharge. Celle-ci est importante car c’est là qu’on décharge également toutes nos variables.

      Private Sub Class_Terminate()
      ' libère les variables
          On Error Resume Next
          Set LmCombo = Nothing
      End Sub

      Pour charger la combo depuis le formulaire il faut utiliser un « Setteur ». Le voici !

      Public Property Set objComboBox(objCombo As Access.ComboBox)
          Set LmCombo = objCombo
          LmCombo.OnDblClick = "[Event Procedure]"
          LmCombo.OnNotInList = "[Event Procedure]"
      End Property

      On voit qu’on lui passe un objet combobox et qu’il est chargé dans la variable définie précédemment. Les 2 lignes suivantes permettent d’activer l’écoute des évènements dont nous avons besoin : DblClick et NotInList.

      Les autres variables ou propriétés de la classe doivent également être valorisées toujours à l’aide de Setteurs.

      Public Property Let frmNameLie(strNomFormulaireLie As String) 'le formulaire de saisie modif à ouvrir
          strFormNameLie = strNomFormulaireLie
      End Property
      
      Public Property Let idFieldName(strNomIdFieldFormulaireLie As String) 'le nom de la colonne id de la source du formulaire
          strIdFieldName = strNomIdFieldFormulaireLie
      End Property
      
      Public Property Let controlNameLie(strNomControleFormulaireLie As String) 'le nom du controle texte qui recevra la saisie
          strControlName = strNomControleFormulaireLie
      End Property
      
      Public Property Let tableNameLie(strNomTableFormulaireLie As String) 'le nom de la table du formulaire
          strTableFormLie = strNomTableFormulaireLie
      End Property

      Rien de bien particulier à part qu’on utilise un Let au lieu du Set qui est réservé aux objets.

      Nos variables sont devenus des propriétés de la classe.

      Méthode privée

      Une méthode privée permettra d’attendre la fermeture du formulaire de saisie avant de poursuivre l’exécution du code. Nous obtiendrons un déroulement synchrone du processus.

      Ce n’est pas la meilleure méthode mais pour cet exemple cela suffira.

      Private Sub pAttendreFermeture(vlFrmRprt As Object)
      '-----------------------------------------------------------
      ' Procedure   : pAttendreFermeture
      ' Author      : Fabrice CONSTANS
      ' Date        : 20/10/2011
      ' Description : Attend la fermeture de l'objet pour rendre la main
      ' Paramètres  : vlFr est l'objet à controler
      '-----------------------------------------------------------
      On Error GoTo pgAttenteFermeture_Error
         Do
            DoEvents
         Loop While vlFrmRprt.Visible
      
      pgAttenteFermeture_Error:
              On Error GoTo 0
              Exit Sub
      End Sub

      Les évènements

      Les évènements vont être gérés à l’aide de procédure qui deviendront de fait des méthodes de la classe. La différence c’est qu’à aucun moment nous ne devront les appeler pour qu’elles fonctionnent. Ceci grâce au WithEvents de la déclaration et à l’activation des écouteurs.

      Le Notinlist

      On remarque que la signature de la procédure est strictement identique à celle qui pourrait être générée dans un formulaire. Je vous conseille d’ailleurs d’utiliser des signature générée et de les copier dans la classe, vous gagnerez un temps précieux et vous n’aurez pas le risque que cela ne fonctionne pas.

      Public Sub LmCombo_NotInList(NewData As String, Response As Integer) 'signature de la procédure
      '--------------------------------------------------------------------
      ' Procedure : LmContact_NotInList
      ' Author    : Fabrice CONSTANS (MVP)
      ' Date      : 21/01/2016
      ' Purpose   : Nouveau contact, on appelle le formulaire de saisie.
      '------------------------------------------------------------------
      '
      Dim strNouveauNumero As String
      
      On Error GoTo Errsub
      
          If vbYes = MsgBox("Cette valeur n'existe pas. " & _
                 "Souhaitez-vous la créer ?", vbInformation + vbYesNo, _
                                            "classe ccombo") Then
            'ouverture du formulaire en mode Ajout  
            DoCmd.OpenForm strFormNameLie, acNormal, , , acFormAdd
            'on gèle le contrôle reception de la valeur saisie
            Forms(strFormNameLie).Controls(strControlName).Enabled = False
            'on ajoute la valeur saisie dans la liste
            Forms(strFormNameLie).Controls(strControlName).Value = NewData
            'traitement synchrone
            pAttendreFermeture Forms(strFormNameLie)
      
      'Vérification que le valeur existe (peut-être que l'utilisateur a
      ' changé d'avis et a annulé la création
         If dLookUp("nz(" & strIdFieldName & ",-1)", _
             strTableFormLie, strControlName & "=""" & NewData & """") > 0 Then
               'la valeur a été saisi
               Response = acDataErrAdded
            Else
              'la valeur n'a pas été saisi (annulation)  
              Response = acDataErrContinue
              LmCombo.Parent.Undo
           End If
         Else
           'il ne souhaite pas la création
           Response = acDataErrContinue
           LmCombo.Parent.Undo
         End If
      
      Exitsub:
            On Error GoTo 0
            Exit Sub
      Errsub:
             msgbox "cComboBox.LmCombo_NotInList", Err, Erl, Err.Description
      End Sub

      Vous pouvez constater que nous utilisons bien les constantes habituelles pour ce type d’évènement : NewDataacDataErrContinueacDataErrAdded

      Le DblClick

      Cet évènement est plus simple que le précédent puisqu’il ne fait qu’ouvrir le formulaire en mode consultation/modification.
      A la sortie on pense à rafraichir le contenu de la combo au cas ou l’utilisateur a modifié une valeur.
      Il va de soit que pour cette dernière le traitement doit également être synchrone.

      Public Sub LmCombo_DblClick(Cancel As Integer)
      '------------------------------------------------------------------
      ' Procedure : btnEditContact_Click
      ' Author    : Fabrice CONSTANS (MVP)
      ' Date      : 16/02/2016
      ' Purpose   : edit et raffraichit la liste au retour
      '------------------------------------------------------------------
      '
      Dim Id As Long
      
      On Error GoTo Errsub
      
          If IsNull(LmCombo.Column(0)) Then
              MsgBox "Pour créer un item vous devez entrer sa valeur.", _
                     vbInformation + vbOKOnly, "classe ccombo"
              Exit Sub
          End If
      
          Id = LmCombo.Column(0)
      
          DoCmd.OpenForm strFormNameLie, acNormal, , _
                      strIdFieldName & "=" & Id, acFormEdit
      
          pAttendreFermeture Forms(strFormNameLie)
          LmCombo.Undo
          LmCombo.Requery
          LmCombo.value = Id
      
       Exitsub:
         On Error GoTo 0
         Exit Sub 
      
      Errsub:
          msgbox ("cComboBox.LmCombo_DblClick", Err, Erl, Err.Description)
      End Sub

      Voilà la Classe cCombo est prête à être utilisée. Voyons cela dans les faits.

      Utiliser la classe dans un formulaire

      En premier lieu il faut indiquer au formulaire que nous allons utiliser cette classe. Pour cela nous devons utiliser autant de variables qu’il y a de zone de liste à implémenter. Ces variables doivent être globale, donc déclarer immédiatement après les 2 lignes d’Options du module de classe du formulaire.

      Dim cComboClient         As cComboBox

      Dans l’événement Sur Ouverture du formulaire on poursuit la déclaration.

      Set cComboClient = New cComboBox
      
      cComboClient.controlNameLie = "RaisonSociale"
      cComboClient.frmNameLie = "fClient"
      cComboClient.idFieldName = "id_Client"
      Set cComboClient.objComboBox = Me.Controls("lmClient")
      cComboClient.tableNameLie = "tClient"

      New permet de créer l’instance de la classe et ainsi pouvoir valoriser les propriétés.

      Dans cet exemple la liste déroulante se nomme lmClient. Vous pouvez adapter ce code déclaratif à votre propre cas. Vous ne devez jamais modifier la classe pour y intégrer des noms propres à l’application. Une classe doit rester générique sous peine de ne plus être portable.

      Il va de soit qu’à chaque nouvelle liste déroulante à instancier, un code similaire devra être ajouté. C’est tout l’intérêt d’une classe.

      Une fois sauvegardé, vous pouvez utiliser le formulaire.

      Les tests de comportement à faire :

      • Entrer une nouvelle valeur.
      • Double cliquer sur la zone de liste.

      Classe en VBA, quels avantages ?

      Le poids de l’application : On remplace des centaines de lignes par une partie déclarative.

      L’uniformité du comportement : Comme on utilise le même code pour tous les contrôles nous sommes sûr que le comportement sera la même.

      Maintenance et évolution : Si un bug est constaté ou que l’on souhaite faire évoluer le comportement, on intervient dans la classe. La duplication de code n’est plus nécessaire on évite des heures de test, d’erreurs potentielles et de tous les problèmes inhérent à cette pratique.

      Rapidité de conception : Une simple déclaration permet d’exploiter la classe.

      Classe en VBA, quels inconvénients ?

      Gérer toutes les erreurs de l’application : En cas d’erreur non traitée avec les classes chargées se déchargent. Il faudra alors exécuter les déclarations une nouvelle fois.

      Difficulté technique : Pour les non programmeurs, appréhender le concept de classe peut poser des problèmes.

      Complexité : Les classes sont plus complexes à mettre en œuvre qu’un code procédural et évènementiel classique. Si vous traiter un formulaire il est conseillé de concevoir les traitements dans le formulaire puis de créer la classe à partir d’un code fonctionnel. Si c’est un classe standard créé le code dans un module standard, cela vous permettra de tester rapidement votre création, ensuite le transformer-la en classe.

      Conclusion

      J’espère que cette incursion dans les classes vous aura plu et qu’elle vous donnera des idées d’implémentations pour vos développements. N’hésitez pas à commenter ce billet.

      VBA : Split()

      Split() est une fonction pratique et largement utilisée. A partir d’une chaine de texte elle renvoie un objet Tableau (Array) composé de ses éléments en utilisant le séparateur désigné.

      Par exemple :

      Dim monTexte as String
      Dim monTableau as Variant
      montexte = "Bonjour je suis Sam"
      
      monTableau = Split(montexte, " ")

      Dans l’exemple ci-dessus le tableau monTableau contiendra :
      montableau(0) >> « Bonjour »
      montableau(1) >> « je »
      montableau(2) >> « suis »
      montableau(3) >> « Sam »

      On peut donc utiliser un boucle (Do, While, For) pour parcourir le tableau, comme on peut utiliser l’indice comme c’est fait dans la liste précédente. Ci-dessous un exemple avec une boucle For.

      Dim i As Long
      Dim vArray As Variant
      vArray = Split("Bonjour je suis Sam", " ")
      For i = 0 To UBound(vArray)
          Debug.Print vArray(i)
      Next

      Split renvoie un objet tableau, Split() est un tableau

      Si Split est un tableau on peut l’utiliser en tant que tel sans l’affecter à une variable.
      Dans l’exemple suivant on récupère directement l’item du tableau.

      Dim monPrenom as String
      monPrenom = Split(montexte, " ")(3)

      La variable monPrenom vaudra :
      monPrenom >> « Sam »

      Le paramètre (3) accolé directement à la fonction Split() peut vous paraitre étrange à première vue pourtant ce n’est ni plus ni moins que l’indice du tableau.

      Conclusion

      Lorsqu’on veut récupérer une valeur et que l’on connait avec certitude sa position, utiliser cette syntaxe est plus rapide, cependant lorsqu’on veut utiliser plus d’une valeur, l’utilisation classique est plus performante.

      VBA : La recherche et les accents en SQL et VBA.

      Rechercher dans une base de données avec l’opérateur Like est assez trivial. Avec quelques jokers on arrive à retrouver ce que l’on souhaite. Cependant lorsque on a affaire à des contenus ayant des caractères accentués il est difficile de récupérer à la fois ceux qui en comporte et ceux qui n’en ont pas.

      L’objet de ce billet est d’utiliser VBA pour contourner ce problème.

      La première chose à faire est de créer une fonction qui va traiter le mot recherché.

      Function ConvertAccForLike(strValue As String) As String
      ' Fabrice Constans (MVP ACCESS) mars 2016
      If InStr(1, strValue, "a") > 0 Then
          strValue = Replace(strValue, "a", "[aàâä]")
      End If
      If InStr(1, strValue, "e") > 0 Then
          strValue = Replace(strValue, "e", "[eéèêë]")
      End If
      If InStr(1, strValue, "i") > 0 Then
          strValue = Replace(strValue, "i", "[iîï]")
      End If
      If InStr(1, strValue, "o") > 0 Then
          strValue = Replace(strValue, "o", "[oôö]")
      End If
      If InStr(1, strValue, "u") > 0 Then
          strValue = Replace(strValue, "u", "[uùûü]")
      End If
      ConvertAccForLike = strValue
      End Function

      Dans cette fonction, on recherche la présence des voyelles, une à une, en commençant par le a, ensuite le e, puis le i, etc. Chaque fois que la voyelle est détectée on la remplace par la syntaxe Contient de l’opérateur Like, soit […]

      Pour le mot « eleve », la valeur renvoyée sera :

      "[eéèêë]l[eéèêë]v[eéèêë]"

      Les mots trouvés seront :

      eleve, éleve, élève, élevé…

      Vous pouvez l’utiliser directement dans objet requête, une source de formulaire, une clause Where d’un OpenForm ou OpenReport comme dans une requête en VBA.

      Voici son utilisation :

      SELECT * FROM matable WHERE champ1 LIKE convertAccForLike("eleve");

      Évidemment vous pouvez compléter avec les autres jokers.

      SELECT * FROM matable WHERE champ1 LIKE convertAccForLike("eleve?");

      Dans ce cas la valeur renvoyée sera :

      "[eéèêë]l[eéèêë]v[eéèêë]?"

      Ce bout de code fonctionne avec des tables liées, cependant lorsque vous utilisez d’autres bases de données comme Sql Server, MariaDB, PostgreSql, il vaut mieux travailler directement sur ces dernières pour des raisons évidentes de performance.

      N’hésitez pas à laisser un commentaire.

      VBA : Bang ou Dot ?

      Lorsque vous développez en VBA, êtes-vous plutôt Bang (!) ou plutôt Dot (.) ou mélangez-vous allégrement les deux formes de syntaxe Dang/Bot ?

      Pour savoir où vous vous situez, voici un exemple de chaque syntaxe.

      'Bang
      Forms!monformulaire!maliste
      
      'Dot
      Forms.monformulaire.maliste
      
      'Dang ou Bot :)
      Forms.monformulaire!macolonne

      Le Bang

      C’est la syntaxe originelle de VBA quand il apparut pour la première fois sur Ms Access 1.1. On retrouve de nombreux exemples sur Internet et dans des applications anciennes ou développez à grand coup de copier/coller issu du net, et sans surprise dans le code remonté par les IA.

      Le Bang a une signification particulière depuis l’apparition du Dot. Prenons par exemple l’objet Recordset de la bibliothèque DAO (Microsoft Office xx.0 Object Library) dans l’Explorateur d’Objets (F2) de VBE.

      On voit dans l’Explorateur d’Objets qu’un élément de la classe bénéficie d’une icône particulière et qu’en le sélectionnant il apparait comme Membre par défaut. Le Bang désigne cet objet (ou propriété) implicitement sans pour autant qu’on spécifie sa nature.

      Partant du modèle indiquant que Fields est le membre par défaut de Recordset et que Value est le membre par défaut de Field on crée l’exemple de manipulation Bang/Dot suivant :

      Sub test()
      Dim rs As DAO.Recordset
      Set rs = CurrentDb.OpenRecordset("select * FROM matable", dbOpenSnapshot)
      
      'renvoi le nom de la colonne Nom
      Debug.Print rs!nom.Name  'bang/dot
      Debug.Print rs.Fields("nom").Name  'équivalent dot
      
      'renvoi la valeur de la colonne Nom
      Debug.Print rs!nom    'bang ou Value est le membre par défaut de Field
      Debug.Print rs.Fields("nom")  'dot avec bang implicite
      Debug.Print rs.Fields("nom").value  'dot explicite
      
      'provoque une erreur
      Debug.Print rs!nom!Name     'bang non maitrisé
      Debug.Print rs.Fields("nom")!Name  'dot/bang non maitrisé
      
      rs.Close
      Set rs = Nothing
      
      End Sub

      Alors pourquoi le Dot ? Quels avantages ?

      L’utilisation de Dot, même si elle est plus verbeuse, permet l’affichage de l’autocomplétion à la touche du point (Dot) appuyée.

      L’autocomplétion pour ceux qui ne sont pas habitués à l’utilisation des EDI (Environnement de Développement Intégré) sont des listes très utiles qui recenses les propriétés, méthodes et objets des classes invoquées.

      Outre sont coté pratique d’aide à la saisie, il y a, pour les plus curieux, un tremplin de découverte et d’exploration de classe.

      Le côté verbeux de Dot ne doit pas rebuter le développeur car à première lecture du code et sans forcément connaitre la classe utilisée, il n’y a pas de doute sur ce que le concepteur fait.

      Conclusion

      Si la forme Dot peut remplacer la forme Bang, il faut être vigilant lors de l’utilisation du Bang et le mélange des deux doit être effectué dans la plus grande connaissance de la classe.

      Etes-vous Dot, Bang ou Dang/Bot ?
      Si vous voyez du Bang, éprouvez-vous le besoin de le transformer en Dot ?
      Donnez moi votre point de vue sur le sujet.

      VBA : IsIn() une fonction d’analyse de paramètres

      Cette fonction détermine si la valeur (value) passée appartient à la liste (listvalue).

      Public Function isIn(value As Variant, listValue As String, _
                           Optional sep As String = ";") As Boolean
      '------------------------------------------------------------------------------------
      ' Procedure : isIn
      ' Author    : Fabrice CONSTANS (MVP)
      ' Date      : 10/08/2023
      ' Purpose   : renvoi VRAI si value transmise fait partie de la liste listValue
      ' Parameters: listValue est une liste d'éléments séparés par sep
      '             value peut être un élément de la liste listValue
      ' Return    : Boolean
      ' Copyright : © L'intégralité du code ci-dessous est la propriété de son auteur (Author)
      '             
      '------------------------------------------------------------------------------------
      
          Dim tbl As Variant
          Dim i As Long
          If isNullOuVide(value) Then Exit Function
      
          tbl = Split(listValue, sep)
          For i = 0 To UBound(tbl)
              If tbl(i) = CStr(value) Then
                  isIn = True
                  Exit Function
              End If
          Next
      End Function

      2 paramètres obligatoires :

      • value pour la valeur à chercher
      • listvalue pour la liste des valeurs à explorer

      1 paramètre facultatif :

      • sep pour le séparateur de la liste à explorer.

      On l’utilise de cette manière avec le séparateur par défaut.

      If isIn(1,"2;5;8;6") Then
         Msgbox "1 fait bien parti de la liste
      Else
         Msgbox "1 ne fait pas parti de la liste"
      Endif

      Ou encore en précisant le séparateur.

      mavar = 1
      maliste = "1|2|3|8|94|100|15|50"
      
      If isIn(mavar, maliste, "|") then
         Msgbox "1 fait bien parti de la liste
      Else
         Msgbox "1 ne fait pas parti de la liste"
      Endif  
       

      Cette fonction peut être modifiée pour traiter le Null. J’ai également créé des variantes avec des Array et ParamArray pour le paramètre listvalue.

      Vous utilisez une autre méthode ?
      Vous souhaitez échanger sur ce sujet ?
      N’hésitez pas à laisser un commentaire.

      VBA : Débogage, les bonnes pratiques (1)

      Dans cette série de billets nous allons délivrer des conseils, détailler des techniques et mettre en pratique le débogage au travers d’exemples simples.

      Introduction

      Le débogage est l’action de suivre le déroulement du code pour en observer le comportement et corriger des problèmes ou bugs. Tout développement passe par cette phase dont la durée varie suivant la maîtrise du langage. La facilité de débogage est proportionnelle à la simplicité du code. Faites un code simple, efficace et vous aurez d’autant plus de facilité à le déboguer.

      Qu’est-ce que VBE ?

      VBE est l’acronyme de Visual Basic Editor, soit l’éditeur Visual Basic Application commun à tous les produits de la suite Office et autre produit intégrant le langage de programmation VBA. C’est avec lui que nous allons travailler tout au long de ces billets.

      On y accède au choix après l’ouverture d’un fichier Access :

      • Avec la combinaison de touches Alt + F11,
      • En ouvrant un nouveau module ou un existant,
      • En ouvrant ou créant un événement du type [Procédure Evènementielle] d’un formulaire/état.
      • Pour les versions récentes via le ruban Créer/ Autre /Menu Macro/ Module ou Module de classe.

      Cet éditeur comporte un menu, des barres d’outils ainsi que plusieurs fenêtres.

      Nous allons aborder tour à tour chacune d’elles au fur et à mesure de notre besoin.

      Premiers réglages : Réduire les risques d’erreurs

      Le débogage commence avant l’écriture de la moindre ligne de code. En effet, au-delà de la bonne connaissance du produit il existe quelques précautions à prendre pour limiter le risque d’erreurs de codage. Il faut en premier lieu utiliser les meilleurs réglages de l’éditeur VB (Visual Basic Editeur, VBE).

      Les réglages de VBE

      Avant de commencer l’écriture de la moindre ligne de code vous devez vous assurer des réglages des options de l’éditeur VB.

      Vérification automatique de syntaxe

      Cette option est intéressante lorsque vous débutez car elle vous signale les erreurs lors du changement de ligne et vous propose un accès à la rubrique d’aide la plus adéquate pour résoudre le problème.
      Dans le cas d’une erreur sur la ligne, la ligne apparait en rouge. Si la vérification automatique de syntaxe est active, le mot clef posant problème est signalé et un message d’erreur s’affiche.

      Avec la vérification automatique de syntaxe :

      Lorsque le curseur n’est plus sur la ligne comportant l’erreur.

      Sans la vérification :

      Activée ou non la ligne apparaitra en rouge si elle comporte une erreur, la différence est uniquement sur l’affichage ou non du message.

      Lorsque vous faites de la concaténation de texte, comme des requêtes SQL dans le code (les plus intégristes me traiteront d’hérétique), ou de la copie de code cette option peut vite devenir agaçante. Personnellement je désactive cette option.

      Déclaration des variables obligatoires

      Vivement conseillée en toute circonstance, cette option permet d’ajouter sur tous les nouveaux modules, l’instruction Option Explicit en en-tête. Cela oblige le développeur à déclarer chaque variable avant son utilisation. Vous réduisez ainsi les erreurs liées aux mauvais noms de variables souvent causées par des fautes de frappe.
      Très fréquente, cette erreur conduit à un code qui ne plante pas mais qui ne fait pas ce qu’on lui demande. C’est souvent le plus difficile à déboguer.
      Si vous avez déjà commencé à coder lorsque vous lisez ces lignes, il n’est pas trop tard pour ajouter cette option dans tous vos modules y compris ceux des formulaires et des états, cependant vous devrez le faire manuellement.
      Cette instruction vient immédiatement après Option Compare.

      Option Compare Database 
      Option Explicit

      Notez qu’en l’absence de déclaration, une variable est de type Variant comme une variable déclaré sans type. Le type Variant est  très gourmand en mémoire.

      Complément automatique des instructions

      Cette option ajoute un confort et une rapidité en mettant à disposition la complétion de code. La complétion, souvent désignée sous le terme d’auto-complétion ou intellisense, se comporte de différentes manières.

      1er cas d’utilisation

      Lorsque vous saisissez un début d’instruction et que vous appuyez simultanément sur CTRL+Espace  plusieurs cas de figure peuvent se produire.
      INFORMATION : Les cas sont ceux rencontrés avec les références standards.

      Une seule correspondance

      Il n’existe qu’une instruction possible, votre saisie est automatique complétée.
      Entrez Doc et CTRL+Espace, le mot clé DoCmd s’affiche.

      Plusieurs correspondances possibles

      Il existe plusieurs instructions qui répondent à cette saisie. Une liste déroulante s’affiche, l’instruction choisie est celle qui correspond à la saisie.
      Entrez Do et CTRL+Espace et la liste s’affiche.

      Utilisez les touches de déplacement et la tabulation pour valider ou la souri ou encore compléter le mot clef.
      Pour annuler utilisez la touche Echap.

      2ème cas d’utilisation

      Lorsque vous utilisez le nom d’un objet ou d’une classe et que vous saisissez le point, la même liste déroulante s’ouvre avec les choix possibles pour cet objet.

      3ème cas d’utilisation

      Dans certains cas, comme la déclaration de variables typées, la complétion entre en action après avoir entré le mot clé AS.

      On rencontre aussi ce fonctionnement lorsque l’instruction saisie nécessite un paramètre Enum, La méthode OpenRecordset de DAO en est un parfait exemple. Voir le billet sur les déclarations Enum ici.

      Conclusion
      Avec la complétion vous n’aurez plus d’excuses pour avoir inventé des noms de méthodes ou propriétés farfelues. Si vous êtes un peu curieux vous pourrez explorer les composants d’un objet, en action avec l’aide vous découvrirez ainsi de nouvelles méthodes et propriétés.

      La complétion fonctionne également avec vos bibliothèques de fonctions, procédures et classes ainsi qu’avec les contrôles, propriétés et méthodes des formulaires et états.
      La complétion ne fonctionne pas dans les cas suivants :
      • Quand il y a une erreur identifiée (ligne en rouge) non corrigée.
      • L’objet que vous utilisez n’existe pas (bibliothèque non référencée, nom farfelu…)
      • L’objet n’est pas typé précisément (Late Binding) c’est le cas pour Variant et Object par exemple.

      Notez que dans ce dernier cas vous pouvez utilisez la méthode « Early Binding » durant la conception, puis le transformer en « Late Binding » par la suite. Cette technique est décrite dans le billet traitant du Binding.

      La complétion est donc essentielle pour éviter les fautes de frappe et l’utilisation de propriétés ou méthodes inexistantes mais également dans le perfectionnement de votre apprentissage d’une bibliothèque.

      Bon utilisation !

      ACCESS : Tri d’enregistrements sur le contenu d’une zone de liste déroulante

      Il est fréquent et même commun d’utiliser des listes déroulantes dans des formulaires.

      Dans le cas où le contenu affiché n’est pas présent dans la source de données, il est difficile de faire un tri sur cette donnée.

      Je vous livre ici une astuce qui va vous permettre de faire un tri de la source du formulaire à partir des données affichées dans la liste.

      Admettons une liste nommée LmAdherent dont le champ affiché est nommé Nom_Adherent et provient d’une source externe au formulaire.

      Dans ce cas sur l’évènement Sur Ouverture indiqué cette syntaxe :

      Me.OrderBy = "[Lookup_LmAdherent].[Nom_Adherent]" 
      Me.OrderByOn = True

      On concatène le mot clef « Lookup_ » au nom de la liste déroulante LmAdherent puis un point et le nom de la colonne à trier.

      Chaque fois que vous ouvrirez le formulaire, il apparaitra trié sur le contenu Nom_Adherent alors que cette données n’est pas contenu dans la source du formulaire.

      Vous pouvez découvrir d’autre syntaxe en observant ce qui se passe dans la propriété OrderBy du formulaire lorsque vous utilisez le tri du menu contextuel.

      Bonne utilisation !