Sélecteur par listes : version 2
Dans un précédent article intitulé Sélecteur par listes : version 1, nous avions mis en place un formulaire de sélection de valeurs. Aujourd’hui, nous allons adapter le principe pour que les valeurs proviennent d’une table, au lieu d’être statiques.
Avant de démarrer…
- Les principes de construction sont proches de ceux traités dans l’article Sélecteur par listes : version 1. Consultez cet article pour démarrer, de nombreux points seront repris ici.
- Pour que le mode de sélection soit le plus adaptable possible, nous allons également reprendre les principes vus dans l’article Une méthode élégante de sélection. Même si ça demande un peu plus d’efforts, nous obtiendrons un système plus évolutif.
- Je suppose que votre base de données contient une table de clients (
tbl Clients
). Le reste va être monté dans ce qui suit…
Étape 1 : la table de sélection
Comme expliqué dans l’article Une méthode élégante de sélection, on va essayer d’avoir le moins d’impact possible sur la table Clients (pas de champ à ajouter dans cette table). Du coup, il vous faut une table spécifique pour gérer les sélections. La structure de cette table est donnée dans l’article cité.
Étape 2 : la requête de sélection
Vous devez lier votre table Clients à la table de sélection construite précédemment. Une fois encore, reportez-vous à l’article Une méthode élégante de sélection. La requête y est détaillée aussi… La mienne s’appelle rqt Sélection Clients
dans la suite.
Étape 3 : le code VBA pour gérer les sélections
Pour gérer les sélections de façon transparente dans l’application, voici un certain nombre de procédures ou fonctions, que vous devez recopier dans un module standard de votre base :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
' --- ' INITIALISATION DES SELECTIONS ' --- ' Entrée : strTableCible <- Nom de la table dont ' on veut sélectionner les lignes ' strClefPrimaire <- Nom de la clef primaire de la table cible. ' strTableSelection <- Nom de la table de sélection ' strWhere <- Clause Where optionnelle ' Sub InitialiserSelection( _ ByVal strTable As String, _ ByVal strClefPrimaire As String, _ Optional ByVal strTableSelection As String = "tbl Sélections", _ Optional ByVal strWhere As String = "") ' Vider la table des sélections strTableSelection = "[" & strTableSelection & "]" CurrentDb.Execute "DELETE * FROM " & strTableSelection & ";" ' Renseigner les nouvelles valeurs ' de la table des sélections Dim strSQL As String strSQL = "INSERT INTO " & strTableSelection & " (Numéro, Sélectionné)" _ & " SELECT [" & strClefPrimaire & "], False" _ & " FROM [" & strTable & "]" If strWhere <> "" Then strSQL = strSQL & " WHERE " & strWhere CurrentDb.Execute strSQL End Sub ' --- ' SELECTIONNER TOUTES LES LIGNES ' --- ' Sub ToutSelectionner( _ Optional ByVal strTableSelection As String = "tbl Sélections") CurrentDb.Execute "UPDATE [" & strTableSelection & "] SET [Sélectionné] = True;" End Sub ' --- ' DESELECTIONNER TOUTES LES LIGNES ' --- ' Sub ToutDeselectionner( _ Optional ByVal strTableSelection As String = "tbl Sélections") CurrentDb.Execute "UPDATE [" & strTableSelection & "] SET [Sélectionné] = False;" End Sub ' --- ' INVERSER LA SELECTION D'UNE LIGNE ' --- Sub InverserSelection( _ ByVal lngNumero As Long, _ Optional ByVal strTableSelection As String = "tbl Sélections") Dim strSQL As String strSQL = "UPDATE [" & strTableSelection & "] SET [Sélectionné] = Not [Sélectionné]" _ & " WHERE [Numéro] = " & lngNumero CurrentDb.Execute strSQL End Sub ' --- ' COMPTER LE NOMBRE DE SELECTION ' --- Function NombreSelections( _ Optional ByVal strTableSelection As String = "tbl Sélections") As Long NombreSelections = DCount("*", strTableSelection, "[Sélectionné] = True") End Function |
En pratique, j’ai repris l’ensemble du module déjà donné dans l’article précédent, en ajoutant deux méthodes qui nous seront utiles ici :
- La procédure
InverserSelection
inverse l’état d’une ligne (sélectionnée ou pas). Il s’agit simplement d’inverser la valeur du champ Oui/NonSélectionné
, dans la tabletbl Sélections
. - La fonction
NombreSelections()
renvoie le nombre d’enregistrements sélectionnés (on compte le nombre de champsSélectionné
cochés, dans la tabletbl Sélections
).
Étape 4 : le formulaire
Construisez un formulaire en mode Création, donc vide. Placez-y les éléments accessoires (le titre par exemple), et surtout les éléments suivants :
- Une liste des clients (objet
ListBox
nommélstClients
). - Une deuxième liste, copiée de la précédente (objet
ListBox
nommélstClientsSelectionnes
). - 4 boutons pour la sélection d’un seul client (
btnSelectionnerUn
), la désélection d’un seul client (btnDeselectionnerUn
), la sélection de tous les clients (btnSelectionnerTout
), et la désélection de tous les clients (btnDeselectionnerTout
). On considère qu’une sélection consiste à faire passer un ou plusieurs clients de gauche à droite, et qu’une dé-sélection… fait l’inverse ! - 2 boutons pour l’interface générale :
btnOK
etbtnAnnuler
.
Les listes de clients
- Lors de la création des listes déroulantes, un Assistant devrait s’afficher. Choisissez l’option « Je veux que la zone de liste extraie les valeurs d’une table ou d’une requête« .
- Sélectionnez la requête rqt Sélection Clients créée plus haut.
- Choisissez les champs qui vous intéressent, en n’oubliant par la clef primaire de la table Clients (dans mon cas :
Numéro Client
). Ce champ sera placé en première colonne des listes (il peut être masqué s’il n’intéresse pas l’utilisateur). C’est lui qui donnera les numéros de client à manipuler. - Une fois les deux listes construites, faites en sorte que la première affiche les clients non sélectionnés, et la seconde les clients sélectionnés. Pour cela :
- Faites apparaître les propriétés de la première liste, onglet Données.
- La propriété
Contenu
doit lister l’instruction SQLSELECT
qui extrait les données. Cliquez sur les points de suspension à sa droite. - Vous obtenez un écran de requête. Appliquez le critère
Oui
au champSélectionné
, sur la grille de requête. - Fermez cette requête en enregistrant les changements.
- De retour au formulaire, refaites la même opération avec la seconde liste, cette fois
Sélectionné = Non
.
Étape 5 : initialisation du formulaire
A son ouverture, le formulaire doit réinitialiser la table des sélections, et mettre à jour les listes de clients. Voici le code VBA qui va s’en charger, à recopier dans le module du formulaire, pr
écisément :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
' --- ' CHARGEMENT DU FORMULAIRE ' --- Private Sub Form_Load() InitialiserSelection "tbl Clients", "Numéro Client", "tbl Sélections" MiseAJourListes End Sub ' --- ' MISE A JOUR DES LISTES ' --- Private Sub MiseAJourListes() Me.lstClients.Requery Me.lstClientsSelectionnes.Requery End Sub |
Étape 6 : gérer les transferts d’une liste à l’autre
4 boutons ont été prévus pour permettre les sélections. On va aussi faire en sorte qu’un double-clic sur une liste sélectionne (ou dé-sélectionne) le client concerné. Avec ce qui a été mis en place plus haut (au point 3), la gestion des boutons et des listes reste plutôt courte… 🙂
- Sélectionner / dé-sélectionner un client équivaut à inverser son état de sélection (rappelez-vous de la méthode
InverserSelection
plus haut). - Sélectionner ou dé-sélectionner tous les clients équivaut aussi à des appels de méthodes écrites précédemment.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
' --- ' SELECTION D'UN ELEMENT ' --- Private Sub btnSelectionnerUn_Click() InverserSelection Me.lstClients.Value MiseAJourListes End Sub ' --- ' DESELECTION D'UN ELEMENT ' --- Private Sub btnDeselectionnerUn_Click() InverserSelection Me.lstClientsSelectionnes MiseAJourListes End Sub ' --- ' SELECTION DE TOUS LES ELEMENTS ' --- Private Sub btnSelectionnerTout_Click() ToutSelectionner MiseAJourListes End Sub ' --- ' DESELECTION DE TOUS LES ELEMENTS ' --- Private Sub btnDeselectionnerTout_Click() ToutDeselectionner MiseAJourListes End Sub ' --- ' DOUBLE-CLIC POUR SELECTIONNER UN MOIS ' --- Private Sub lstClients_DblClick(Cancel As Integer) btnSelectionnerUn_Click End Sub ' --- ' DOUBLE-CLIC POUR DESELECTIONNER UN MOIS ' --- Private Sub lstClientsSelectionnes_DblClick(Cancel As Integer) btnDeselectionnerUn_Click End Sub |
Étape 7 : exploiter la sélection
On n’a plus qu’à gérer les boutons OK et Annuler. Commençons par le deuxième, facile, qui ferme le formulaire :
1 2 3 4 5 6 |
' --- ' FERMETURE DU FORMULAIRE ' --- Private Sub btnAnnuler_Click() DoCmd.Close End Sub |
Pour le bouton OK, tout dépend de ce que vous voulez en faire (reportez-vous à la fin de l’article Une méthode élégante de sélection pour quelques compléments). Dans mon cas, je vais ouvrir un état filtré sur les clients sélectionnés (l’état est basé sur la requête rqt Sélection Clients
). Ce qui donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
' --- ' VALIDATION DU FORMULAIRE ' --- Private Sub btnOK_Click() ' Est-ce qu'on a sélectionné au moins 1 client ? If NombreSelections() = 0 Then MsgBox "Vous n'avez sélectionné aucun client !", vbExclamation Exit Sub End If ' Fermer le formulaire et ouvrir l'état des clients DoCmd.Close DoCmd.OpenReport "rpt Clients", acViewPreview, , "[Sélectionné] = True" End Sub |
Ouf ! Si vous avez passé tous les obstacles…
- vous obtenez un formulaire ergonomique pour sélectionner les clients ;
- vous avez une méthode qui peut être adaptée à d’autres tables ;
- le tout sans impact sur la structure des tables existantes ;
- fonctionne en réseau ;
- garanti sans phosphates 🙂
Finalement, ça valait peut-être le coup d’arriver jusque là ?! 🙂
Bonjour,
La macro blogue à « inverser Selection »
‘ —
‘ SELECTION D’UN ELEMENT
‘ —
Private Sub btnSelectionnerUn_Click()
InverserSelection Me.lstCDM.Value
MiseAJourListes
End Sub
J’obtiens un message d’erreur
« Erreur d’exécution ’94’:
Utilisation incorrecte de Null »
Quelqu’un peut m’aider, j’ai pourtant suivi la procédure à la lettre.
Dans ce formulaire je souhaiterai sélectionner des élèves
Sur quelle ligne s’affiche le message d’erreur ?
Moha > A vue de nez, il faudrait aussi que
lngNumero
soit de typeString
et non plusLong
(du coup, pour être cohérent, la variable devrait s’appelerstrNumero
).Bonsoir Mr Hervé
Merci d’abord pour votre réponse, que j’ai essayé mais ça marche pas toujours:Erreur d’exécution ’13’ incompatibilité du type. N°QuittanceSelect : est de type texte sous forme alpha numérique : « 0040ZP »
‘ —
‘ INVERSER LA SELECTION D’UNE LIGNE
‘ —
Sub InverserSelection( _
ByVal lngNumero As Long, _
Optional ByVal strTableSelection As String = « QuittancesSéléctionnées »)
Dim strSQL As String
strSQL = « UPDATE [ » & strTableSelection & « ] SET [Séléctionnées] = Not [Séléctionnées] » _
& » WHERE [N°QuittanceSelect] = ‘ » & lngNumero & « ‘ »
CurrentDb.Execute strSQL
End Sub
Moha > Est-ce que ma réponse a la 1ère question résout aussi la deuxième ?
Pour exploiter la selection j’ai associé la requete sql ci-apès au boutton valider mais ça marche pas. pouvez vous m’aidé pour la corrigé :
Private Sub ValidSitQuittance_Click()
Dim strSQL As String
strSQL = « UPDATE ReqQuittancesSelect SET ReqQuittancesSelect.SitQuit = [Formulaires]![TraitementsQuittances]![SitQuitTraitement], ReqQuittancesSelect.DateSituation = [Formulaires]![TraitementsQuittances]![DateTraitement] WHERE (((ReqQuittancesSelect.Séléctionnées)=Yes)); »
CurrentDb.Execute strSQL
End Sub
Bonjour
Comment adapter le code de la procédure InversionSelection dans le cas où la valeur n’est pas (long) mais un texte (Alpha numérique)
Cordialement
Moha > A priori, il suffit d’adapter la requête SQL (variable
strSQL
) en délimitant le champNuméro
(s’il s’appelle comme ça) par des apostrophes. Du genre :dany tall > Le type incompatible signifie que la valeur passée à la procédure
InversionSelection
n’est pas conforme. Dans mon exemple, cette valeur est supposée être un entier long (Long
). Si la valeur fournie par la liste est d’un type différent, il faudra adapter le code.Bonjour,
je reviens sur mon problème concernant le message d’erreur » utilisation incorrecte de NULL » lorsque j’exécute le « btnSelectionnerUn » (mais je pense que j’ai pu résoudre le problème) par contre pour faire la bascule inverse donc de la liste « lstfrsselectionnes » vers « lsfrs » j’ai ce message d’erreur « Incompatibilité de type » sur la ligne « InverserSelection Me… »
Voici le code
‘ —————————-
‘ DESELECTION D’UN FOURNISSEUR
‘ —————————-
Private Sub btndeselectionun_Click()
InverserSelection Me.lstfrsselectionnes.Value
‘TransfererUn Me.lstfrsselectionnes, Me.lstfrs
MiseAJourListes
End Sub
Quelqu’un pourrait-il m’aider svp je peine sur ce sujet depuis deux semaines déjà.
Merci à tous.
Dany
dany tall > Le débogage s’arrête sur quelle ligne exactement ?
Voici le message d’erreur que j’ai lorsque j’essaie de transférer ou non un élément de ma liste de sélection vers ma liste cible en cliquant sur le bouton « btnSelectionnerUn »: « Utilisation incorrecte de NULL »
OK c’est exactement ce que j’ai fait maintenant j’ai un problème avec la procédure « InverserSelection » j’ai suivi le conseil de Jean – Guy car j’ai lu qu’il a eu le même problème il y a bien longtemps mais je ne parviens pas à bien le résoudre.
dany tall > C’est ça. Le module standard est un module qui doit être créé manuellement.
Par contre, le module du formulaire est accessible automatiquement (tout formulaire a un module unique associé). L’icône Code (ou Visualiser le code) donne accès à ce module, lorsqu’on est en mode Création de formulaire. Il suffit aussi, dans VBE, de double-cliquer sur le nom du formulaire (en fait, dans VBE : le nom du formulaire doit plutôt être vu comme « le nom du module de formulaire »).
Toutes les étapes à partir de la 5 sont à reprendre dans le module du formulaire. C’est ça qui va associer du code VBA (des gestionnaires d’événements) aux boutons. Il faut que les noms des boutons soient identiques aux noms des blocs, sinon ça ne marchera pas. Si un bouton s’appelle
btnTest
, son gestionnaire d’événement Clic doit démarrer par :Tapé dans le formulaire concerné? c’est-à-dire que je fais deux modules dont un standard et l’autre module dédié au formulaire? Dans le module du formulaire j’insère le code de l’étape 4 si j’ai bien compris?
dany tall > A priori, le code donné à l’étape 3 devrait être recopié dans un module standard, comme indiqué. Ça permet de le réutiliser sur d’autres formulaires si nécessaire. Par contre, ce qui suit est tapé dans le module du formulaire concerné, puisqu’il y a des gestionnaires d’événements associés aux boutons.
Bonjour Hervé,
je pense pas qu’il y ait une erreur sur le nom d’un objet par contre j’ai mis tout le code dans un même module est ce que le problème ne viendrait pas de là?
dany tall > Est-ce qu’il pourrait y avoir une erreur sur le nom d’un objet (table, objet graphique…) ?
Bonjour,
votre tuto sur le sélecteur de liste version 2 est vraiment très intéressante et m’aide beaucoup à traiter le même type de problème sur ma base de données. Par contre j’ai un petit soucis, en fait lorsque je clique sur le bouton ajouter un « élément », la sélection ne s’ajoute pas dans l’autre liste. Mais si je dé-sélectionne un élément de ma table « Tbl sélection », cette élément est bien supprimé de la liste « lst frs » mais il s’ajoute à la liste « lst frs sélectionnés », ce qui n’est pas normal.
Comment faire pour que mon formulaire « sélecteur de frs » fonctionne correctement de telle sorte que lorsque je sélectionne un élément dans la liste « lst frs » et que je clique sur mon bouton « ajouter », que cet élément s’ajoute bien dans la liste « lst frs sélectionnés » ?
En vous remerciant.
Bonjour.
Je vous fais part de ma solution au souci que j’avais avec la procedure inverserSelection dont je vous parlais hier.
Je me suis rapproché des procedures selectionner et deselectionner toutes les lignes pour les comparer avec la procedure inverser la selection d’une ligne.
J’ai ensuite copier la procedure inverser la selection d’une ligne.
Je l’ai renommé Ajouter la selection d’une ligne soit AjouterSelection( _
J’ai modifié le ligne de code:
strSQL = « UPDATE [ » & strTableSelection & « ] SET [Sélectionné] = Not [Sélectionné] » _
& » WHERE [Numéro] = » & lngNumero
Par
strSQL = « UPDATE [ » & strTableSelection & « ] SET [Sélectionné] = true » _
& » WHERE [Numéro] = » & lngNumero
J’ai donc remplacé NOT [Sélectionné] par True.
Cela fonctionne.
Puis pour le bouton enveler la sélection d’une ligne j’ai fais la même chose mais cette fois-ci en remplacant
NOT[sélectionné] par False.
massi15 > Il n’y a rien de « natif » sur Access pour se déplacer de cette manière. Un petit bout de VBA pourrait faire l’affaire (à vérifier ceci dit, je n’ai pas eu le temps de tester).
Je vous remercie de votre réponse rapide.
Pour l’instant, le bouton ajouter pointe sur le code btnSelectionnerUn_Click() du formulaire qui lui appelle le code InverserSelection du module 1.
Je précise que j’ai le même phénomène avec le bouton enlever quand je selectionne un élément à enlever de la liste de selection.
En attentant, je vais me pencher dans la rubrique liaisons office.
Pour mon cas le volume à traiter sera important.
J’ai quelques 200000 enregistrements à traiter répartis sur environ 200 fichiers excel( ils ont tous la même structure).
J’ai été attiré par votre méthode de selection, au départ pour l’initialisation de la base.
Je m’explique:
Mon projet servira à des utilisateurs à vocation régionale.
j’ai environ 200 fichiers comportant des données au niveau national.
Chaque utilisateur choisira les départements concernés à sa région. Le choix fait, Il sera créé une table avec les données extraites des fichiers excel nationaux correspondant aux départements choisis.
tous les traitement suivants se feront sur la table nouvellement créée. Les utilisateurs auront ainsi une base propre à leur région.
Dans mon projet, c’est la phase d’initialisation.
Merci pour votre aide.
Jean-Guy > Le bouton Ajouter est couplé à quel code VBA pour l’instant ?
Pour la partie 2, il y a quelques articles sur l’interface Access/Excel dans la rubrique « Liaisons Office », mais sans doute pas d’article répondant précisément à la question. Ça peut déjà donner quelques pistes.
Selon le volume à traiter, ce sera plus rapide d’importer les données Excel dans une table temporaire Access, puis de filtrer par requête.
Bonjour.
Suite a votre tuto sur le sélecteur de liste:version 1 je viens de suivre celui-ci.
Je suis bloqué par la procédure « inverserSelection ».
Je vous rassure, cette procédure fonctionne bien mais le résultat est déroutant pour un ultilsateur lambda.
Je m’explique: J’ai un bouton ajouter.
Je clique une fois sur le bouton, la sélection s’ajoute bien dans l’autre liste. Mais si l’on clique une seconde fois, cette même selection retourne dans la première liste, c’est normal puisqu’on inverse la selection.
Mais comment faire pour que cette selection reste bien ajoutée dans la seconde liste et qu’elle ne retourne pas à son emplacement initial ?
D’autre part, je voudrais pouvoir, avec les elements selectionnés, rechercher des enregistrements dans plusieurs fichiers excel et les importer dans une nouvelle table.
Avez vous travaillé sur des tutos qui pourraient m’aider à avancer dans mon projet.
En vous remerciant.
Jean-Guy
Bonjour,
Ce n’est pas ainsi. Les données sont affichées dans la liste, par exemple colonne « mot » et colonne « long ». Ordere de tri : long, mot. Je cherche à afficher le premier mot de chaque nouvelle longueur, sans savoir combien il y a de mots entre deux longueurs. J’essaye Alt+flèche bas, ou Ctrl+flèche bas pour avancer.
Mais rien de concluant. Une idée ?
merci. daniel
massi15 > Est-ce que cet article ne répondrait pas mieux à ton besoin ?
Bonjour,
Effectivement, j’ai plus d’enregistrements qu’il est possible d’afficher.
Exemple : j’affiche 20 enregistrements à l’écran, donc visibles.
Mais je veux afficher le 59° de la liste dans l’écran.
En VBA, quels ordres dois-je utiliser pour rendre visible cet enregistrement ?
Merci beaucoup
daniel
massi15 > Je verrais les choses autrement : « faire défiler » est un raisonnement qui repose sur l’interface graphique, mais pas sur les données. Il vaut mieux raisonner sur les données, l’interface vient ensuite : l’idée est « d’atteindre un enregistrement précis de la table » ?
Bonjour,
J’ai effectivement suivi votre conseil et utilidé DAO. Trés facile à mettre en oeuvre.
Mais, toujours en VBA, je cherche à faire défiler les enregistrements affichés, de manière à voir l’enregistrement sur lequel porte un certain travail.
Une idée des ordres à utiliser ? J’ai longuement cherché. Je sais faire bouger une fenêtre mais pas son contenu, ni l’ascenseur.
Bizarre.
Merci pour votre réponse
daniel
massi15 > Si c’est un problème de pur calcul de TVA (par exemple), et si les tables sont correctement structurées, on peut effectivement extraire le taux de TVA associé à un produit, et créer un champ calculé pour obtenir le TTC. Ça se fera dans une requête (graphique ou SQL) basée elle-même sur la jonction des tables concernées, avec un champ calculé donnant le TTC.
Comme la question de départ portait sur une fonction VBA, la question de DAO se posait effectivement. Mais si c’est du calcul « direct », il n’y a pas besoin de VBA du tout.. Ou alors, éventuellement, pour simplifier une formule dans une requête (et déplacer le calcul dans une fonction VBA).
Bonjour,
Je passe de 2003 à 2010 et réécris certaines séquences.
Dans la facturation, je dois aller chercher différents taux, divers numéros de compte.
Dont en particulier, dans la table TPays, les taux de TVA, les taux de réduction et remise…
de façon à calculer le TTC.
Tout ces accès doivent s’effectuer automatiquement (ouverture, lecture, fermeture). Or les tables sont déjà présentes puisque la base est ouverte : lancer une requête en SQl ne suffit pas sans passer par ADO ?
Ou par tout autre moyen.
merci
daniel
massi15 > Si j’ai suivi, il y a plusieurs manières de faire ce que tu souhaites, mais elles tournent quasiment toutes autour de DAO ou ADO, puisque ces couches sont des interfaces entre le stockage (SQL) et VBA.
Quel est l’objectif précis ?
Bonjour,
Je cherche une méthode semblable à celle que vous exposez mais en automatique.
Je m’explique. Dans une fonction en VBA, je dois aller dans la table Pays, chercher le pays concerné pour prendre les paramètres afférents.
Est-ce que en automatique je peux lancer dans une table déjà ouverte une recherche de type SQL, et récupérer divers paramètres.
Sans passer par DAO, ADO…puisque la table est déjà présente.
Merci beaucoup
daniel