Création d'un formulaire de recherche multicritère

Jean BALLAT

Cet article se propose de créer un formulaire de recherche multicritère suivant deux méthodes (sans code ou avec code). ♪

- Titre : Création d'un formulaire de recherche multicritère

- Auteur : Jean BALLAT

- Parution : 18 décembre 2008

- Mise à jour : 17 septembre 2019

- Licence : Copyright ® 2008 Jean BALLAT. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. INTRODUCTION

- II. LES CAHIERS DES CHARGES

- II-A. La recherche sans code

- II-B. La recherche avec code VBA

- II-C. Les tables de la base de données

- III. LES FORMULAIRES

- III-A. La recherche sans code

- III-A-1. Les listes déroulantes combinées

- III-A-1-a. Création de la liste des agences via l'assistant

- III-A-1-b. Personnalisation de la liste

- III-A-2. Création du sous-formulaire

- III-A-2-a. Création de la source

- III-A-2-b. Création du sous-formulaire

- III-A-2-b-i. Création de l'entête et de la section Détail

- III-A-2-b-ii. Création du pied de formulaire

- III-A-2-c. Les propriétés

- III-A-2-c-i. Propriétés du sous-formulaire

- III-A-2-c-ii. La section Pied de formulaire

- III-A-2-d. Implantation du sous-formulaire

- III-A-3. Création de la zone de liste

- III-A-4. Récupération des statistiques

- III-A-5. Récupération du nombre de fiches

- III-A-6. le bouton « Effacer tous les filtres »

- III-A-6-a. Implantation du bouton

- III-A-6-b. La macro « Initialisation des contrôles »

- III-A-7. Le bouton « Imprimer le filtre »

- III-A-7-a. Implantation du bouton

- III-A-7-b. La macro « Imprimer le Filtre »

- III-A-8. La macro de « Mise à jour des listes »

- III-A-8-a. Composition de la macro « MiseAJour »

- III-A-8-b. Implantation de la macro

- III-A-9. Ouverture du formulaire « Fiche Employé »

- III-A-9-a. Présentation de la fiche détaillée

- III-A-9-b. Création et implantation de la macro « M_OuvrirFiche »

- III-A-10. Initialisation du formulaire principal

- III-B. La recherche avec code VBA

- III-B-1. Le sous-formulaire « SF_FiltreAvecCode »

- III-B-1-a. Création et propriétés du sous-formulaire

- III-B-1-b. Implantation du sous-formulaire

- III-B-2. La liste « lstEmploye »

- III-B-3. Le bouton « Effacer tout »

- III-B-4. Le bouton « Imprimer le filtre »

- III-B-5. La récupération des statistiques

- III-B-6. Le code VBA dans le formulaire

- III-B-6-a. La procédure « sur Ouverture » du formulaire principal

- III-B-6-b. La procédure « Sur clic » du bouton « Effacer tout »

- III-B-6-c. La procédure « Sur Chargement » du sous-formulaire

- III-B-6-d. La procédure « Sur clic » du bouton « Imprimer le filtre »

- III-B-6-e. La procédure « sur Double Clic » de la liste lstEmploye

- IV. LES ÉTATS

- IV-A. L'état sans code

- IV-A-1. Description de l'état

- IV-A-2. La macro « MiseAJourControle »

- IV-A-3. Implantation de la macro

- IV-B. L'état avec Code

- IV-B-1. Description de l'état

- IV-B-2. Le Code VBA

- IV-B-2-a. La procédure « Sur Ouverture » de l'état

- IV-B-2-b. La procédure « Au Formatage » de l'entête de l'état

- V. CONCLUSION

- VI. TÉLÉCHARGEMENT

- VII. REMERCIEMENTS

I - INTRODUCTION

Cet article n'est pas le premier à développer ce sujet (cf. les cours et tutoriels). Cependant, j'ai cherché, dans celui-ci, à traiter le sujet sans code (pour les non-initiés) et avec un peu de code (pour les accros).
Les filtres seront appliqués soit par choix dans des listes déroulantes dépendantes (Formulaire SANS code), soit par double clic dans une des colonnes du sous-formulaire (Formulaire AVEC code).

II - LES CAHIERS DES CHARGES

II-A - La recherche sans code

Le but est de pouvoir, au travers d'un sous-formulaire :
- afficher la fiche d'un employé en récupérant des critères de filtre dans des listes déroulantes combinées ;
- imprimer le résultat des filtres en affichant dans l'état le détail des critères appliqués.

La mise à jour automatique des listes se fera via des macros simples.

On entendra par « listes combinées », des listes dont le contenu sera fonction des choix faits dans les autres listes affichées (aussi bien en amont qu'en aval).

II-B - La recherche avec code VBA

Le but est de pouvoir, à partir d'un sous-formulaire, filtrer les données en double-cliquant sur les colonnes de celui-ci.
Le double clic pourra se faire dans un ordre quelconque.
Le résultat sera imprimable. Dans l'état, on retrouvera le détail des critères du filtre appliqué.

II-C - Les tables de la base de données

La base de données est constituée de cinq tables.

Tables

Objet

Clé Primaire

Relation avec (clé étrangère)

Table liée

T_Employes

Liste le détail des employés de la Société

CodeEmploye

T_Agences

Liste les agences du Groupe

CodeAgence

CodeAgence

T_Employes

T_Diplomes

Liste les diplômes des employés

CodeDiplome

CodeDiplome

T_Employes

T_PosteOccupe

Liste les différents postes de l'entreprise

CodePosteOccupe

CodePosteOccupe

T_Employes

T_SituationFamille

Liste les différentes situations de famille (Marié, Célibataire…)

CodeFamille

CodeFamille

T_Employes

Le modèle relationnel de la base de données utilisée

La base de données est disponible dans le chapitre « TÉLÉCHARGEMENT ».

III - LES FORMULAIRES

III-A - La recherche sans code

La composition de ce formulaire se déroule en sept étapes.
1) La création des listes déroulantes combinées.
2) La composition du sous-formulaire.
3) La création de la zone de liste dépendante des listes de critères.
4) La récupération des statistiques.
5) La récupération du nombre de fiches (avec respect du singulier et du pluriel).
6) Le bouton qui réinitialise le formulaire.
7) Le bouton d'impression du résultat du filtre.

III-A-1 - Les listes déroulantes combinées

Je ferai plusieurs remarques par rapport à la création des listes :
- la liste affiche la mention « ---Tous--- » au-dessus des valeurs ;
- les données sont triées par ordre croissant.

III-A-1-a - Création de la liste des agences via l'assistant

Le formulaire principal n'est lié à aucune table. On le dit alors « indépendant ».
Cliquez donc sur l'onglet « Formulaires » dans la fenêtre « Base de données » et double-cliquez sur « Créer un formulaire en mode Création »

Cliquez sur l'outil « Zone de liste déroulante » dans la boîte à outils.
Cliquez sur le formulaire à l'endroit où vous souhaitez implanter votre liste.
Access ouvre alors un assistant.
Nous suivrons donc toutes les étapes de celui-ci.

Étape 1 :

Dans cette étape, Access nous donne le choix pour alimenter notre liste.
Laissez le choix par défaut, car les données seront issues d'une table.

Étape 2 :

Access nous affiche donc la liste des tables ou des requêtes disponibles.
Choisissez la table « T_Agences ».

Étape 3 :

À ce niveau de l'assistant, nous allons choisir les éléments qui nous seront nécessaires dans notre liste.
Cliquez sur le bouton affichant « >> » afin de récupérer les deux champs.

Étape 4 :

Cette phase nous propose de trier les données de notre liste.
Choisissez de trier les données par Agence.

Étape 5 :

Nous pouvons à ce niveau, modifier la largeur de nos colonnes. Access cache la colonne clé par défaut.
Laissez-la masquée. Ainsi après notre choix, Access affichera le nom de l'agence choisie et non son code. (Une liste déroulante ne pouvant afficher l'information d'une seule colonne à la fois. Cependant, il est à noter que si plusieurs champs sont intégrés de la requête source de notre liste, ceux-ci seront visibles lors du déroulement de la liste, mais que dans tous les cas, Access n'affichera dans le contrôle de liste que le contenu sélectionné du premier champ de celle-ci dont la largeur de la colonne est supérieure à 0 cm).

Étape 6 :

Nous voici arrivés au terme de l'assistant.
Il suffit de saisir ici un texte qui s'affichera à la gauche de notre liste.
Si cela n'est pas fait par défaut, tapez « Agence ».

Procédez de la même façon avec les trois autres listes en utilisant les tables :

- T_Diplomes (nom de la liste : cboDiplome) ;

- T_PosteOccupe (nom de la liste : cboPosteOccupe) ;

- T_SituationFamille (nom de la liste : cboFamille).

III-A-1-b - Personnalisation de la liste

Cette phase de la création de notre contrôle va nous permettre de :
- renommer le contrôle qui pour l'instant a reçu un nom par défaut (ModifiableX - X étant un numéro affecté par Access) ;
- modifier le contenu de la liste afin de pouvoir la rendre solidaire des trois autres ;
- ajouter les expressions critères dans la requête source de la liste ;
- ajouter dans la source de notre liste la mention « --- Tous --- ».
En cliquant droit sur le contrôle, affichez les propriétés.

Première modification : changer le nom de notre contrôle.

Après avoir affiché les propriétés, cliquez sur l'onglet « Autres » et saisissez : « cboAgence ».
J'ai préfixé le nom avec « cbo » puisqu'il s'agit d'une liste déroulante c'est-à-dire une combobox.
Ce principe de préfixage permet de savoir, en relisant les noms des objets de notre base, de quel type d'objet il s'agit.
Je vous conseille à ce propos la lecture de l'article de Agyronet sur les conventions typographiques dans le Code Visual Basic

Seconde modification : rendre la liste dépendante des trois autres.

Cliquez sur l'onglet « Données » puis cliquez dans la propriété « Contenu ».
Deux boutons s'affichent alors sur la droite du formulaire.
Cliquez sur le bouton contenant « … ».
Access affiche alors la fenêtre de génération des requêtes.

Afin de rendre les quatre listes solidaires, nous allons ajouter la table « T_Employes ». Par ce biais, on obtiendra un lien avec les autres listes.
On ajoutera dans le contenu de la requête les champs « CodeDiplome », « CodePosteOccupe », « CodeFamille ». Ils ne seront pas affichés, car ils servent uniquement de support à nos critères.

Troisième modification : ajouter les critères sous les champs CodeDiplome, CodePosteOccupe, CodeFamille.
Si pour les Utilisateurs confirmés la création d'une expression ne pose pas de problème particulier, je vais quand même repasser en détail les manipulations à faire pour créer une expression critère en utilisant le générateur d'expression.
Avant de passer au pas à pas pour la création de l'expression, il me faut préciser que la fonction que nous allons utiliser est une fonction conditionnelle du même type que la fonction SI() d'Excel. En effet, nous ne récupèrerons la valeur de la liste déroulante que si un choix a été fait dans la liste.
Ainsi, sous le champ « GExpression2 », si la liste CodeDiplome affiche la mention « ---Tous--- » alors je n'ai pas de critère sinon je récupère la valeur du choix.
Cette fonction qui s'appelle VraiFaux dans Access s'articule comme SI.
Elle se décompose en trois arguments :
=VraiFaux(Condition; Valeur si Condition vraie; Valeur si Condition fausse).

Étape 1 :

Pour lancer le générateur d'expression, il faut revenir dans l'écran générateur des requêtes.
- Cliquez droit sur la liste déroulante « cboAgence » et affichez les propriétés.
- Dans l'onglet « Données », cliquez dans la propriété « Contenu » puis sur le bouton tout à droite.
- Cliquez dans la ligne « Critères » sous le champ « CodeDiplome » (cf. la petite loupe).
- Cliquez sur l'outil « générateur d'expression » (cf. la grande loupe).

Étape 2 :

Nous allons composer la partie « Condition » de notre fonction.
Suivez les étapes ci-dessous.
1) Tapez =GExpression2.
2) Double-Cliquez une fois sur le « + » du dossier « Formulaires ».
3) Cliquez une fois sur le « + » du dossier « Formulaires chargés ».
4) Cliquez une fois sur le nom du formulaire qui nous intéresse.
5) Double-cliquez sur le nom de la liste que nous sommes occupés à lier (ici « cboDiplome »).
6) Tapez =0 (Valeur de notre liste si la mention « ---Tous--- » est affichée).

Étape 3 :

On continue avec le second argument : Valeur si condition Vraie.
Comme précédemment, suivons le pas à pas.
1) Tapez « ; » le traditionnel séparateur d'arguments.
2) Double-cliquez sur le « + » du dossier « Tables ».
3) Cliquez une fois sur le dossier « T-Employes ».
4) Le générateur ajoute « expr ». Cliquez simplement sur le mot et supprimez-le.

Étape 4 :

Enfin, nous allons terminer notre fonction en créant le troisième argument :
1) Cliquez une fois sur le nom du formulaire qui nous intéresse ;
2) Double-cliquez sur le nom de la liste que nous sommes occupés à lier (ici c'est toujours « cboDiplome ») ;
3) Le générateur ajoute « expr » comme précédemment, on le supprime et on ferme l'expression par une parenthèse.

Il ne nous reste plus qu'à valider et à recommencer la même opération pour les deux autres champs de la requête.

Enfin, vous avez compris que pour lier les autres listes entre elles, il faut faire la même chose pour les trois autres.

À vous de jouer. Allez-y doucement, cela vous évitera quelques recherches assommantes…

Quatrième modification : ajouter la mention « Tous » dans la liste.
Nous devrons intervenir dans la syntaxe SQL de la requête source de notre liste.
Il nous faut donc retourner dans les propriétés de notre liste afin d'afficher le générateur de requête.

Nous commençons à connaître le chemin.
- Clic droit sur le contrôle.
- Clic « Propriétés » puis onglet « Données ».
- Clic dans la propriété « Contenu » puis sur le bouton à droite de la ligne.
- Dans le générateur de requête, cliquez sur la liste des affichages disponibles.
- Choisissez alors SQL.

Cliquez à la fin de la syntaxe affichée, supprimez le caractère « ; » et tapez :
UNION SELECT 0, « ---Tous--- » FROM T_Agences
Fermer la fenêtre Sql et cliquez sur Oui pour enregistrer les modifications.

Quelques explications sur la syntaxe saisie.

- Dans une requête UNION, il doit y avoir autant de champs dans les deux requêtes réunies.

- La mention « ---Tous--- » est entre guillemets, texte oblige.

- Remarquez l'espace avant les tirets de la mention « Tous ». Ainsi en triant sur l'Agence, « Tous » sera en première ligne.

Vous trouverez une autre méthode pour ajouter « Tous » dans une liste dans la F.A.Q.

Il vous reste donc à réaliser ce même travail sur les trois autres listes du formulaire.

III-A-2 - Création du sous-formulaire

III-A-2-a - Création de la source

Voici l'image de la requête source de notre sous-formulaire. Nous aurons besoin de recréer les mêmes critères que dans les différentes listes. En effet, lorsqu'un choix est fait dans une des listes, il faut que le sous-formulaire soit mis à jour en fonction du choix.
Nous retrouvons sous les quatre champs codes (Agence, Diplôme, Famille, PosteOccupe) la même syntaxe « VraiFaux » que nous avions créée dans nos différentes listes du formulaire principal.
Enregistrez la requête sous le nom de « R_Personnel ».

Voici un tableau reprenant les différentes expressions utilisées.

Champ

Expression critère

CodePosteOccupe

VraiFaux([Formulaires]![F_FiltreSansCode]![cboPosteOccupe]=0;[T_Employes].[CodePosteOccupe];[Formulaires]![F_FiltreSansCode]![cboPosteOccupe])

CodeDiplome

VraiFaux([Formulaires]![F_FiltreSansCode]![cboDiplome]=0;[T_Employes].[CodeDiplome];[Formulaires]![F_FiltreSansCode]![cboDiplome])

CodeAgence

VraiFaux([Formulaires]![F_FiltreSansCode]![cboAgence]=0;[T_Employes].[CodeAgence];[Formulaires]![F_FiltreSansCode]![cboAgence])

CodeFamille

VraiFaux([Formulaires]![F_FiltreSansCode]![cboSituationFamille]=0;[T_Employes].[CodeFamille];[Formulaires]![F_FiltreSansCode]![cboSituationFamille])

III-A-2-b - Création du sous-formulaire

III-A-2-b-i - Création de l'entête et de la section Détail

1) Cliquez sur le groupe « Formulaires ».
2) Double-cliquez sur « Créer un formulaire en mode Création ».
3) Affichez les sections Entête et pied de formulaire.
4) Affichez la fenêtre des propriétés et cliquez dans l'onglet « Données ».
5) Dans la propriété « Source », sélectionnez la requête « R_Personnel ».
6) Positionnez les champs comme dans l'image ci-contre.

III-A-2-b-ii - Création du pied de formulaire

Remarquez les champs statistiques implantés dans le pied de formulaire. Ceux-ci permettront de récupérer les données dans le formulaire principal.

Ci-dessous le détail des champs statistiques (nom du contrôle et source du contrôle)

Nom du contrôle

Source du contrôle

txNbPersonnelSF

=Compte([NomEmploye])

txtMasseSalarialeSF

=Somme([SalaireActuel])

txtMoyenneSalaireSF

=Moyenne([SalaireActuel])

txtMaxSalaireSF

=Max([SalaireActuel])

txtMinSalaireSF

=Min([SalaireActuel])

III-A-2-c - Les propriétés

III-A-2-c-i - Propriétés du sous-formulaire

1) Cliquez droit sur l'extérieur du formulaire et affichez les propriétés ;
2) Paramétrez-le en reportant les différentes propriétés comme ci-contre ;
3)On remarquera la propriété :  :« Affichage par défaut : Formulaires continus ».
Ce paramétrage permettra d'afficher plusieurs enregistrements l'un au-dessous de l'autre.

III-A-2-c-ii - La section Pied de formulaire

Cliquez sur l'onglet « Format » ou « Toutes » des propriétés du pied de formulaire et mettez la propriété « Visible » sur Non

III-A-2-d - Implantation du sous-formulaire

Affichez en côte à côte la fenêtre base de données et le formulaire principal en mode création.
Cliquez maintenu sur le SF_FiltreSansCode et glissez dans le formulaire principal à l'endroit souhaité.

III-A-3 - Création de la zone de liste

La zone de liste a pour but d'afficher les noms des salariés issus des différents filtres appliqués par les listes déroulantes. La source de celle-ci est donc également liée aux quatre listes de choix.

1) Ouvrez le formulaire principal en mode création ;
2) Dessinez le contrôle zone de liste ;
3) Cliquez droit sur celui-ci et affichez les propriétés.
Voici ci-contre les différentes propriétés à modifier.

III-A-4 - Récupération des statistiques

Dans le formulaire principal, nous implanterons quatre champs indépendants qui nous permettront de récupérer les valeurs calculées dans le pied du sous-formulaire.
Voir dans le tableau ci-dessous les caractéristiques des différents champs.

Nom du contrôle

Source du contrôle

txtMasseSalariale

=SF_FiltreSansCode.Formulaire!txtMasseSalarialeSF

txtMoyenneSalaire

=SF_FiltreSansCode.Formulaire!txtMoyenneSalaireSF

txtSalaireMaxi

=SF_FiltreSansCode.Formulaire!txtMaxSalaireSF

txtSalaireMini

=SF_FiltreSansCode.Formulaire!txtMinSalaireSF

Ces expressions de calcul pourront être créées par l'intermédiaire du générateur d'expression. Voir l'utilisation plus haut dans cet article.

III-A-5 - Récupération du nombre de fiches

La récupération du nombre de fiches ne devrait pas poser de problèmes, car il s'agit de récupérer la valeur dans le pied de formulaire du sous-formulaire, donc comme ci-dessus. Cependant, il nous faut respecter l'accord du mot « fiche » en fonction du nombre trouvé.
Nous utiliserons donc la fonction VraiFaux qui nous avons déjà expliquée plus haut et une concaténation du mot « fiche » au singulier ou au pluriel suivant le cas.

L'expression à créer dans la propriété « SourceContrôle » du contrôle « txtNbSalaries » est donc :

=VraiFaux(SF_FiltreSansCode.Formulaire!txNbPersonnelSF <=1 ;SF_FiltreSansCode.Formulaire!txNbPersonnelSF & " Fiche";SF_FiltreSansCode.Formulaire!txNbPersonnelSF & " Fiches")

III-A-6 - le bouton « Effacer tous les filtres »

III-A-6-a - Implantation du bouton

Ce bouton est implanté via la boîte à outils. Le dessin de celui-ci ne présente pas de difficultés particulières. Cependant, il faudra bien lui appliquer une action lorsqu'on cliquera dessus. Dans un premier temps, cliquez sur le bouton « Assistants de contrôle » de la boîte à outils afin de désactiver ceux-ci.
Cliquez à nouveau dans la boîte à outils sur l'outil « Bouton » et dessinez un rectangle sur le formulaire principal.
Affichez les propriétés du bouton et modifiez les propriétés suivantes :
- Nom (onglet « Autres ») : btnEffacerTout
- Légende (onglet : « Format ») : Effacer tous les filtres

III-A-6-b - La macro « Initialisation des contrôles »

Description de la fenêtre « Macro »
Au niveau de l'image de gauche
1) Bouton qui affiche ou masque la colonne « Nom de la Macro ». (Marquée également par (1) à gauche de l'image.)
En identifiant les macros par un nom, cela nous permet d'en regrouper plusieurs dans une même feuille.
2) Colonne « Action » : permet de choisir l'action que la macro devra exécuter.
3) Colonne « Commentaire » : reçoit un texte explicitant l'action exécutée.

Au niveau de l'image de droite
1) Argument : zone permettant de paramétrer l'action à exécuter.
2) Aide contextuelle sur l'argument en cours de renseignement. (À noter : F1 toujours de rigueur pour compléter l'information.)

Nous allons écrire une macro qui va initialiser les différents éléments de notre formulaire à l'ouverture de celui-ci ou en cliquant sur le bouton que nous venons de dessiner.

Pour écrire une macro :
- dans la fenêtre « Base de données », cliquez sur l'objet « Macros » puis « Nouveau » ;
- dans la fenêtre affichée, cliquez sur l'outil marqué (1) dans l'image de gauche ci-dessus. (La colonne « Nom de la macro » s'affiche. (Numérotée (1) également).) ;
- dans la colonne de gauche, saisir un nom suffisamment évocateur de l'action de la macro (ici : Initialisation) ;
- dans la seconde colonne intitulée « Action », sélectionnez l'action « DéfinirValeur ». En choisissant une action, le volet des arguments s'affiche ;
- renseignez les arguments comme dans le tableau ci-dessous.

Il y aura autant d'actions « DéfinirValeur » qu'il y aura de contrôles à initialiser.

Action

Expression

Valeur

DéfinirValeur

[Formulaires]![F_FiltreSansCode]![cboAgence]

0

DéfinirValeur

[Formulaires]![F_FiltreSansCode]![cboDiplome]

0

DéfinirValeur

[Formulaires]![F_FiltreSansCode]![cboPosteOccupe]

0

DéfinirValeur

[Formulaires]![F_FiltreSansCode]![cboSituationFamille]

0

Lorsque les valeurs sont affectées aux différentes listes, il faut « Actualiser » le sous-formulaire et la zone de liste. Nous allons donc ajouter deux actions à notre macro comme indiqué ci-dessous.

Action

Nom du contrôle

Actualiser

SF_FiltreSansCode

Actualiser

lstEmploye

Il faut maintenant enregistrer la macro. Cliquez sur « Enregistrer » puis saisissez le nom « M_ManipulationsControles »

Il ne nous reste plus qu'à implanter la macro sur le bouton.
- Cliquez droit sur le bouton.
- Affichez les propriétés.
- Cliquez sur l'onglet « Évènements ».
- Cliquez dans la propriété : Sur clic.
- Cliquez sur la liste déroulante à droite (voir la loupe).
- Choisissez dans la liste proposée la macro à implanter (ici : M_ManipulationsControles.Initialisation).

III-A-7 - Le bouton « Imprimer le filtre »

III-A-7-a - Implantation du bouton

Ce bouton est bâti de la même façon que le précédent. Je vous laisse donc le réaliser.
Les propriétés modifiées seront :
- Nom (onglet « Autres ») : btnImprimerFiltre ;
- Légende (onglet : « Format ») : Imprimer le filtre.

III-A-7-b - La macro « Imprimer le Filtre »

Cette macro aura pour but de lancer l'impression en mode Aperçu. Elle se construira de la même façon que la précédente.
- Cliquez sur l'objet Macros.
- Cliquez sur Nouveau.
- Afficher la colonne Nom de la macro en cliquant sur l'outil concerné.
- Tapez Imprimer. Ce sera le nom de notre macro.
- Saisissez les différents éléments de la macro repris dans le tableau ci-dessous.
- Puis enregistrez la macro sous le nom « M_ImprimerFiltre » et fermez.

Action

Nom de l'état

Affichage

OuvrirEtat

E_ListePersonnelFiltreeSansCode

Aperçu avant impression

Il faut donc maintenant implanter la macro sur l'évènement Sur clic du bouton.
Pour réaliser cette opération, nous passerons par la fenêtre des propriétés.
- Cliquez droit sur le bouton.
- Cliquez sur l'onglet Évènements.
- Cliquez dans l'évènement Sur clic.
- Ouvrez la liste déroulante et choisissez la macro M_ImprimerFilte.Imprimer.

III-A-8 - La macro de « Mise à jour des listes »

Nous avons donc nos quatre listes qui nous permettront de faire le choix de nos différents critères.
Après chaque choix, il est donc nécessaire que les enregistrements répondant aux critères choisis s'affichent dans le sous-formulaire et que conjointement, les listes se mettent à jour puisqu'elles sont toutes dépendantes les unes des autres.
Nous allons donc écrire une nouvelle macro que nous irons implanter dans la feuille « M_ManipulationsControles ».

Commençons par ouvrir la feuille « M_ManipulationsControles » en cliquant sur le groupe « Macros ».
Cliquez droit sur le nom de la feuille puis cliquez sur « Modifier ».

III-A-8-a - Composition de la macro « MiseAJour »

Positionnez-vous sous la dernière ligne de la macro précédente en laissant une ligne vierge. (Cette ligne vide n'a pas de rôle particulier, si ce n'est simplement pour bien structurer notre feuille. Le nom de la macro suffit à Access pour interrompre le déroulement de la macro précédente.) Composez la macro en reprenant les actions et les arguments décrits dans le tableau ci-dessous.

Nom de la macro

Action

Argument

Commentaire

MiseAJour

Actualiser

cboAgence

Actualise la liste « cboAgence »

Actualiser

cboDiplome

Actualise la liste « cboDiplome »

Actualiser

cboPosteOccupe

Actualise la liste « cboPosteOccupe »

Actualiser

cboSituationFamille

Actualise la liste « cboSituationFamille »

Actualiser

SF_FiltreSansCode

Actualise le sous-formulaire « SF_FiltreSansCode »

Actualiser

lstEmploye

Actualise la liste « lstEmploye »

III-A-8-b - Implantation de la macro

Il va falloir maintenant implanter sur chaque liste déroulante la macro que nous venons d'écrire.
- Ouvrez le formulaire « F_FiltreSansCode » en mode Création.
- Cliquez droit sur la première liste et affichez les propriétés.
- Cliquez dans l'onglet « Évènements » puis dans l'évènement « Après MAJ ».
- Ouvrez la liste déroulante et choisissez la macro.« M_ManipulationsControles.MiseAJour ».

Répétez cette opération sur les quatre listes déroulantes du formulaire.

III-A-9 - Ouverture du formulaire « Fiche Employé »

III-A-9-a - Présentation de la fiche détaillée

Comme prévu dans le cahier des charges, les critères posés dans les listes entrainent l'affichage d'un certain nombre de prénoms dans la liste « lstEmploye ».
En double-cliquant sur un des prénoms, Access doit nous afficher le formulaire ci-contre.
Ce formulaire ne présentant pas de difficultés particulières, je vous laisse le soin de créer la fiche détaillée comme vous l'entendez.
Il faut néanmoins écrire une macro qui permettra d'ouvrir celui-ci.

III-A-9-b - Création et implantation de la macro « M_OuvrirFiche »

Dans le groupe « Macros », faites « Nouveau » et composez la macro comme détaillée dans le tableau ci-dessous. Cette fois-ci, il ne sera pas nécessaire de faire afficher la colonne des noms puisque notre feuille ne contiendra qu'une seule macro.

Action

Commentaire

OuvrirFormulaire

Ouvre le formulaire F_FicheDetaillee sur l'enregistrement sélectionné

L'action « OuvrirFormulaire » demande plusieurs arguments. Voir le tableau ci-dessous.

Argument

Expression

Nom Formulaire

F_FicheDetaillee

Affichage

Formulaire

Nom Filtre

Condition WHERE

[CodeEmploye]=[Formulaires]![F_FiltreSansCode]![lstEmploye]

Mode Données

Mode Fenêtre

Standard

Fermez et enregistrez la macro sous le nom de « M_OuvrirFiche ».
Pour implanter la macro, il nous faut faire appel aux propriétés de la liste « lstEmploye ».
- Cliquez droit sur le contrôle.
- Affichez les propriétés et cliquez dans l'onglet « Évènements ».
- Cliquez dans la propriété « sur Double Clic ».
- Dans la liste déroulante, choisissez la macro « M_OuvirFiche ».
- Enregistrez le formulaire.

III-A-10 - Initialisation du formulaire principal

Pour en terminer avec le formulaire de filtre sans code, il nous reste à l'initialiser à l'ouverture.
Nous procèderons comme pour les listes déroulantes.
- Affichez les propriétés du formulaire.
- Cliquez dans l'onglet « Évènements ».
- Cliquez dans la propriété : « Sur Ouverture ».
- Ouvrez la liste déroulante et choisissez la macro « M_ManipulationsControles.Initialisation ».
- Enregistrez puis fermez votre formulaire.

Voilà, notre formulaire de consultation multicritère sans code est terminé !

III-B - La recherche avec code VBA

La composition de ce formulaire se déroule en six étapes.
1) La création et l'implantation du sous-formulaire.
2) La création de la liste des employés.
3) L'implantation et la programmation du bouton « Effacer tout ».
4) L'implantation et la programmation du bouton « Imprimer le filtre ».
5) La récupération des données statistiques.
6) Le code VBA.

III-B-1 - Le sous-formulaire « SF_FiltreAvecCode »

III-B-1-a - Création et propriétés du sous-formulaire

Ce formulaire ne présente pas de difficultés majeures.

La source de celui-ci est une requête composée de :
- T-Employes (Récupération du CodeEmploye) ;
- T_Agences (Récupération du nom de l'Agence) ;
- T_Diplomes (Récupération du libellé du diplôme) ;
- T_PosteOccupe (Récupération de la fonction occupée) ;
- T_SituationFamille (Récupération du libellé de la situation de famille).

L'organisation des contrôles
. Les étiquettes ont été placées dans l'entête de formulaire et alignées avec les zones de texte dans la section « Détail ».

Le pied de formulaire
Dans le pied de formulaire, déposez une zone de texte indépendante.
Modifiez les propriétés de ce contrôle comme ci-dessous :
- Nom : txtMasseSalarialeSF ;
- Source du contrôle : =Somme([SalaireActuel]).

Ce sous-formulaire sera implanté dans le formulaire principal.

Les propriétés du sous-formulaire
Voici ci-contre les différentes propriétés à modifier dans le sous-formulaire.

Pensez à positionner la propriété « Visible » du pied de formulaire sur « NON »

III-B-1-b - Implantation du sous-formulaire

Le formulaire principal n'est attaché à aucune source (Formulaire indépendant). Pour implanter le sous-formulaire, affichez en côte à côte un nouveau formulaire et la fenêtre « Base de données ».
Cliquez maintenu sur le sous-formulaire « SF_FiltreAvecCode » et glissez sur le nouveau formulaire.
Nous n'aurons pas à gérer de Champ Père, ni de Champ Fils.

III-B-2 - La liste « lstEmploye »

Créez la zone de liste qui va afficher le nom des salariés sélectionnés. Celle-ci sera totalement gérée par le code. Modifiez les propriétés comme ci-contre. Positionnez-la de manière harmonieuse sur votre formulaire (à chacun ses goûts !!!).

III-B-3 - Le bouton « Effacer tout »

En mode « Création », dessinez un bouton de commande après avoir désactivé l'assistant.
Affichez les propriétés de celui-ci et modifiez comme ci-dessous :
- Nom : btnEffacerLesCriteres ;
- Légende : Effacer tout.

III-B-4 - Le bouton « Imprimer le filtre »

On procèdera comme ci-dessus. Les propriétés à modifier sont :
- Nom : btnImprimerFiltre ;
- Légende : Imprimer le Filtre.

III-B-5 - La récupération des statistiques

Afin de récupérer la masse salariale calculée dans le pied du sous-formulaire, implantez une zone de texte dans le formulaire principal sous le sous-formulaire.
Créez l'expression de calcul dans la source contrôle de celui-ci par le générateur d'expression ou en tapant : =SF_FiltreAvecCode.Formulaire!txtMasseSalarialeSF.

III-B-6 - Le code VBA dans le formulaire

On trouvera du code sur plusieurs évènements du formulaire et de ses objets :
- sur Ouverture du formulaire principal ;
- sur Chargement du sous-formulaire ;
- sur Clic du bouton « Effacer tout » ;
- sur Clic du bouton « Imprimer le filtre » ;
- sur Double clic de la zone de liste « Employés ».

III-B-6-a - La procédure « sur Ouverture » du formulaire principal

Cette procédure doit, en fait, afficher tout le contenu. Elle sera donc également utilisée sur le « clic » du bouton « Effacer tout ». Nous écrirons une routine qui pourra être appelée plusieurs fois

Sub InitialisationFormulaire()
' Initialisation des variables
p_strSqlWhere = ""

'Réinitialisation du tableau Critères
For p_intCompteur = 0 To UBound(p_tabCriteres, 2)
p_tabCriteres(1, p_intCompteur) = "Pas de critère pour ce champ"
Next

' Initialisation du sous-formulaire et réinitialisation
Me.SF_FiltreAvecCode.Form.RecordSource = cstSourceFiltre
Me.SF_FiltreAvecCode.Requery
' Initialisation de la zone de liste et réinitialisation
Me.lstEmploye.RowSource = cstSourceFiltre
Me.lstEmploye.Requery
End Sub

Dans ce code, on peut remarquer la partie « Réinitialisation des variables »

' Initialisation des variables
p_strSqlWhere = ""
'Réinitialisation du tableau Critères
For p_intCompteur = 0 To UBound(p_tabCriteres, 2)
p_tabCriteres(1, p_intCompteur) = "Pas de critère pour ce champ"
Next

Ces variables ont été déclarées en public, car elles sont réutilisées dans différentes procédures du projet.
La variable p_strSqlWhere est de type string, elle représente la clause WHERE qui sera appliquée à la source du sous-formulaire et récupère par concaténation, les différents critères sélectionnés.
D'autre part, la variable p_tabCritères() est une variable tableau dont l'objet est de stocker les valeurs de critères choisies pour les imprimer dans l'état (voir cahier des charges).

' Initialisation du sous-formulaire et réinitialisation
Me.SF_FiltreAvecCode.Form.RecordSource = cstSourceFiltre
Me.SF_FiltreAvecCode.Requery
' Initialisation de la zone de liste et réinitialisation
Me.lstEmploye.RowSource = cstSourceFiltre
Me.lstEmploye.Requery

Il s'agit là de l'affectation de la source « cstSourceFiltre » au sous-formulaire et à la zone de liste. Cette constante est déclarée « Public » puisqu'elle sera réutilisée comme source de l'état.
Cette procédure étant mise en place, on peut l'implanter sur l'évènement « Sur Ouverture » du formulaire

Private Sub Form_Open(Cancel As Integer)
' Initialisation du formulaire
InitialisationFormulaire
End Sub

III-B-6-b - La procédure « Sur clic » du bouton « Effacer tout »

En fait, il s'agit ici de réinitialiser tous les objets du formulaire principal.

Private Sub btnEffacerLesCriteres_Click()
' Initialisation du formulaire
InitialisationFormulaire
End Sub

III-B-6-c - La procédure « Sur Chargement » du sous-formulaire

Il s'agit en fait d'une procédure qui permettra d'appliquer facilement le même process sur tous les contrôles du sous-formulaire. En effet, chaque contrôle du sous-formulaire est censé réagir à un double clic récupérant la valeur du champ pour l'utiliser en tant que critère. Cela sous-entend qu'il faut répéter la même procédure pour chaque contrôle (ouf !!!, il n'y en aurait que cinq).
Afin d'éviter un travail fastidieux, nous allons implanter par programmation, dans la propriété « sur double clic » de chaque contrôle concerné, une fonction qui récupèrera notre critère sélectionné.

Private Sub Form_Load()
' Déclaration de la variable
Dim ctlEnCours As Control
' Redimensionnement du tableau
ReDim p_tabCriteres(1, p_intCompteur)
' Boucle sur toutes les zones de texte pour affecter la procédure de filtrage sur l'évènement DoubleClic
For Each ctlEnCours In Me.Controls
If ctlEnCours.ControlType = acTextBox Then
If Left(ctlEnCours.Name, 3) <> "txt" Then
ctlEnCours.Properties("onDblClick") = "=FiltreDonnees('" & ctlEnCours.Name & "' , " & ctlEnCours.Name & ")"
' renseigne le tableau avec les noms des champs
RemplirTabCriteres (ctlEnCours.Name)
End If
End If
Next ctlEnCours
' Désactivation de la variable
Set ctlEnCours = Nothing
End Sub

Dans cette procédure on remarquera :
- la déclaration de la variable ctlEnCours ;
- le redimensionnement du tableau dynamique p_tabCriteres.

' Déclaration de la variable
Dim ctlEnCours As Control
' Redimensionnement du tableau
ReDim p_tabCriteres(1, p_intCompteur)

Dans le redimensionnement du tableau, on trouve une variable publique p_intCompteur qui est incrémentée dans un sous-programme : RemplirTabCriteres que l'on verra un peu plus loin dans l'article.

' Boucle sur toutes les zones de texte pour affecter la procédure de filtrage sur l'évènement Double clic
For Each ctlEnCours In Me.Controls
If ctlEnCours.ControlType = acTextBox Then
If Left(ctlEnCours.Name, 3) <> "txt" Then
ctlEnCours.Properties("onDblClick") = "=FiltreDonnees('" & ctlEnCours.Name & "' , " & ctlEnCours.Name & ")"
' renseigne le tableau avec les noms des champs
RemplirTabCriteres (ctlEnCours.Name)
End If
End If
Next ctlEnCours

Ci-dessus la boucle qui teste le type de contrôle et qui implante la fonction FiltreDonnees sur l'évènement « sur Double clic ».
Au fur et à mesure de la boucle, on renseigne le tableau p_tabCriteres par l'appel de la procédure :

' renseigne le tableau avec les noms des champs
RemplirTabCriteres (ctlEnCours.Name)

Dans le cahier des charges, on nous demande de faire apparaître les critères sélectionnés dans l'entête de l'état. Pour pouvoir récupérer les valeurs, je les stocke dans un tableau.

Sub RemplirTabCriteres(ByVal strNomChamp As String)
' Procédure qui permettra de respecter la consigne du cahier des charges : Imprimer les critères sélectionnés dans l'état.
' On récupère les noms des champs dans un tableau avec comme valeur par défaut : "Pas de critère pour ce champ"
If p_intCompteur > 0 Then
ReDim Preserve p_tabCriteres(1, p_intCompteur)
End If
' renseigne le tableau avec les noms des champs et la mention par défaut
' qui seront affichés dans l'état
p_tabCriteres(0, p_intCompteur) = strNomChamp
p_tabCriteres(1, p_intCompteur) = "Pas de critère pour ce champ"
p_intCompteur = p_intCompteur + 1
End Sub

Dans cette procédure, je ferai simplement un commentaire :

If p_intCompteur > 0 Then
ReDim Preserve p_tabCriteres(1, p_intCompteur)
End If

Ce test vérifie que le tableau contient des valeurs et redimensionne celui-ci tout en préservant le contenu déjà stocké.
Enfin, il nous reste à découvrir la procédure FiltreDonnees

Function FiltreDonnees(ByVal strNomChamp As String, ByVal varValeurChamp As Variant)
' Teste le contenu du contrôle
If IsNumeric(varValeurChamp) Then
' Initialisation du contenu de p_strSqlWhere sans ajout de quotes si le contenu est ALPHA
If p_strSqlWhere = "" Then
p_strSqlWhere = "WHERE " & strNomChamp & " = " & varValeurChamp
Else
p_strSqlWhere = p_strSqlWhere & " AND " & strNomChamp & " = " & varValeurChamp
End If
Else
' Initialisation du contenu de p_strSqlWhere avec ajout des quotes si le contenu est ALPHA
If p_strSqlWhere = "" Then
p_strSqlWhere = "WHERE " & strNomChamp & " = '" & varValeurChamp & "'"
Else
p_strSqlWhere = p_strSqlWhere & " AND " & strNomChamp & " = '" & varValeurChamp & "'"
End If
End If

' Récupération de critère sélectionné afin de reporter ces données dans l'état
For p_intCompteur = 0 To UBound(p_tabCriteres, 2)
If p_tabCriteres(0, p_intCompteur) = strNomChamp Then
p_tabCriteres(1, p_intCompteur) = varValeurChamp
Exit For
End If
Next

' réactualisation du sous-formulaire
Me.RecordSource = cstSourceFiltre & p_strSqlWhere
Me.Requery
' réactualisation de la zone de liste du formulaire principal
With Forms("F_FiltreAvecCode").Controls("lstEmploye")
.RowSource = cstSourceFiltre & p_strSqlWhere
.Requery
End With
End Function

Comme vous pourrez le constater, il s'agit d'une fonction.
Or une fonction est censée retourner une valeur, mais pour ce cas, j'ai écrit une procédure « Function », car je ne peux pas faire appel à une procédure de type Sub directement dans une propriété du formulaire

' Teste le contenu du contrôle
If IsNumeric(varValeurChamp) Then
' Initialisation du contenu de p_strSqlWhere sans ajout de quotes si le contenu est ALPHA
If p_strSqlWhere = "" Then
p_strSqlWhere = "WHERE " & strNomChamp & " = " & varValeurChamp
Else
p_strSqlWhere = p_strSqlWhere & " AND " & strNomChamp & " = " & varValeurChamp
End If
Else
' Initialisation du contenu de p_strSqlWhere avec ajout des quotes si le contenu est ALPHA
If p_strSqlWhere = "" Then
p_strSqlWhere = "WHERE " & strNomChamp & " = '" & varValeurChamp & "'"
Else
p_strSqlWhere = p_strSqlWhere & " AND " & strNomChamp & " = '" & varValeurChamp & "'"
End If
End If

Cette partie de la procédure permet de tester le type de valeur récupérée afin d'ajouter les « ' » autour de la valeur critère si celle-ci est alpha.

' Récupération de critère sélectionné afin de reporter ces données dans l'état
For p_intCompteur = 0 To UBound(p_tabCriteres, 2)
If p_tabCriteres(0, p_intCompteur) = strNomChamp Then
p_tabCriteres(1, p_intCompteur) = varValeurChamp
Exit For
End If
Next

Cette partie du code complète le tableau en stockant la valeur du critère sélectionné.

III-B-6-d - La procédure « Sur clic » du bouton « Imprimer le filtre »

Private Sub btnImprimerFiltre_Click()
On Error GoTo Err_btnImprimerFiltre_Click

Dim stDocName As String

stDocName = "E_ListePersonnelFiltreeAvecCode"
DoCmd.OpenReport stDocName, acPreview

Exit_btnImprimerFiltre_Click:
Exit Sub
Err_btnImprimerFiltre_Click:
MsgBox Err.Description
Resume Exit_btnImprimerFiltre_Click
End Sub

Il s'agit d'une procédure simple, créée à partir de l'assistant lors de la génération du bouton.

III-B-6-e - La procédure « sur Double Clic » de la liste lstEmploye

Private Sub lstEmploye_DblClick(Cancel As Integer)
DoCmd.OpenForm "F_FicheDetaillee",,, "CodeEmploye = " & lstEmploye
End Sub

Il s'agit de l'ouverture du formulaire « F_FicheDetaillee » en fonction de CodeEmploye récupéré dans la zone de liste.

IV - LES ÉTATS

IV-A - L'état sans code

IV-A-1 - Description de l'état

Cet état ne présente pas de difficultés particulières dans sa réalisation. Pour plus d'informations sur la création d'un état, je vous encourage à lire : Description de la création d'un état.
On remarquera cependant que dans l'entête d'état, on retrouve quatre contrôles indépendants dénommés :
- txtAgence ;
- txtDiplome ;
- txtPosteOccupe ;
- txtSituationFamille.

Ces champs seront renseignés par une macro placée sur l'évènement « au formatage » de la section « Entête d'état ».

IV-A-2 - La macro « MiseAJourControle »

Pour créer cette macro, nous allons ouvrir la feuille macro « M_ImprimerFiltre ».
- Cliquez sur le groupe « Macros ».
- Cliquez droit sur la macro « M_ImprimerFiltre ».
- Cliquez « Mode Création ».
- Dans la colonne « Nom macro », tapez « MiseAJourControle ».
Renseignez les actions et les arguments comme dans le tableau ci-dessous :

Nom Macro

Action

Elément

Expression

MiseAjourControle

DéfinirValeur

[États]![E_ListePersonnelFiltreeSansCode]![txtAgence]

VraiFaux([Formulaires]![F_FiltreSansCode]![cboAgence]=0;"Pas de critère sur ce champ";[Formulaires]![F_FiltreSansCode]![cboAgence].[column](0))

DéfinirValeur

[États]![E_ListePersonnelFiltreeSansCode]![txtDiplome]

VraiFaux([Formulaires]![F_FiltreSansCode]![cboDiplome]=0;"Pas de critère sur ce champ";[Formulaires]![F_FiltreSansCode]![cboDiplome].[column](0))

DéfinirValeur

[États]![E_ListePersonnelFiltreeSansCode]![txtSituationFamille]

VraiFaux([Formulaires]![F_FiltreSansCode]![cboSituationFamille]=0;"Pas de critère sur ce champ";[Formulaires]![F_FiltreSansCode]![cboSituationFamille].[column](0))

DéfinirValeur

[États]![E_ListePersonnelFiltreeSansCode]![txtPosteOccupe]

VraiFaux([Formulaires]![F_FiltreSansCode]![cboPosteOccupe]=0;"Pas de critère sur ce champ";[Formulaires]![F_FiltreSansCode]![cboPosteOccupe].[column](0))

Fermez la macro et enregistrez.

IV-A-3 - Implantation de la macro

Pour implanter la macro, il nous faut afficher les propriétés de l'entête d'état.
- Affichez l'état en mode création.
- Cliquez droit dans la section « Entête d'état ».
- Affichez la fenêtre des « Propriétés ».
- Cliquez dans l'onglet « Évènements ».
- Cliquez dans l'évènement « Au formatage ».
- Choisissez dans la liste déroulante, la macro « M_ImprimerFiltre.MiseAjourControle ».

Fermez l'état et enregistrez.

IV-B - L'état avec Code

IV-B-1 - Description de l'état

Cet état est la copie conforme de l'état sans code, cependant la source de celui-ci variera en fonction des critères qui seront sélectionnés.
On aura donc deux procédures dans cet état :
- A l'ouverture de l'état qui récupéra une source.
- Au formatage de l'entête d'état pour récupérer les critères sélectionnés.

IV-B-2 - Le Code VBA

IV-B-2-a - La procédure « Sur Ouverture » de l'état

Private Sub Report_Open(Cancel As Integer)
Me.RecordSource = cstSourceFiltre & p_strSqlWhere

Il s'agit ici, de l'affectation de la source qui est composée de la concaténation de la constante cstSourceFiltre et de la variable p_strSqlWhere.

IV-B-2-b - La procédure « Au Formatage » de l'entête de l'état

Private Sub EntêteÉtat_Format(Cancel As Integer, FormatCount As Integer)
For p_intCompteur = 0 To UBound(p_tabCriteres, 2)
Me.Controls("lblCritere" & p_intCompteur).Caption = p_tabCriteres(0, p_intCompteur)
Me.Controls("txtCritere" & p_intCompteur) = p_tabCriteres(1, p_intCompteur)
Next
End Sub

Il s'agit d'une boucle qui relit le tableau p_tabCriteres et qui affecte, aux différents contrôles en fonction de leur type :
- soit le nom du champ ;
- soit la valeur du critère ou la mention par défaut.

V - CONCLUSION

J'ai voulu montré par cet article, deux façons d'appréhender une recherche multicritère.
Elle vient compléter les méthodes déjà décrites par :
- Caféine dans son article : Recherche Multicritère
- Loufab dans son article : Formulaire de recherche prêt à l'emploi.

VI - TÉLÉCHARGEMENT

La base exemple : Recherche Multicritère

VII - REMERCIEMENTS

Je voudrais remercier l'ensemble de l'équipe DVP qui fait un travail énorme qui a fait de Développez.com ce qu'il est aujourd'hui et qui nous tire toujours vers le haut.
Merci beaucoup également à :
- Philippe Jochmans pour ses conseils avisés.
- Heureux-Oli et Claude Leloup pour leur relecture attentive.