Access - Trucs et astuces

Syntaxes d'appel aux champs de formulaires et sous-formulaires

Dans ce didacticiel, nous allons apprendre à utiliser les différentes syntaxes (c'est parfois un casse-tête !) pour accéder aux champs de formulaires et sous-formulaires.

Vous pouvez télécharger ici la base de données qui a servi de base à cet exemple

 
Sommaire

Base de données préliminaire

Pour expliquer les choses simplement, imaginons une base de données d'agence matrimoniale. Il y a une table T_Candidat, et deux sous-tables T_Qualite et T_Defaut, qui sont respectivement les listes de qualités et de défauts de nos candidats.

Structure de cette base

Voici à quoi ressemble cette base de données. Les captures d'écran datent d'Access 2003, mais les principes sont identiques avec 2007 et 2010:

Création du Formulaire et des sous-formulaires

n mode création du formulaire "F_Candidat", constatons que les deux sous-formulaires s'appellent SFQualite et SFDefaut, et sont basés sur deux formulaires nommés F_QualiteSF et F_DefautSF, comme ceci :

Création d'un bouton de test

Maintenant, créons un bouton dans le formulaire principal, appelé BDCTest, qui renvoie à un événement Sur Clic, comme ceci :

Dans l'environnement VBA, allez dans le menu Affichage, et choisissez Fenêtre exécution. ça va ouvrir une petite fenêtre dans laquelle on pourra simplement écrire des choses à l'aide de l'instruction Debug.Print.

C'est dans ce bouton que nous allons faire tous nos tests.

Test préliminaire du bouton

Essayons maintenant les différentes syntaxes d'appel à de simples champs du formulaire principal, sans parler encore de
sous-formulaires.

Positionnez-vous sur Pauline Martin, arrangez-vous pour avoir Access d'un côté de l'écran, et l'environnement VBA de l'autre. Ecrivez la première syntaxe proposée dans BDCTest_Click, et cliquez ensuite sur le bouton du formulaire.

Regardez alors le résultat dans la fenêtre exécution :

Syntaxes d'accès aux champs du formulaire principal

Syntaxe complète

Debug.Print Forms("F_Candidat").Controls("IDCandidat").Value

Debug.Print affiche donc ce qu'on lui demande dans la petite fenêtre exécution. En l'occurrence, juste le chiffre 2.

Forms est une collection. Par exemple, l'appartement dans lequel vous vivez est une collection de pièces

Au lieu de Forms("F_Candidat"), on pourrait dire : Pièces("Salon")

Bien. A l'intérieur de ce formulaire, il y a des Contrôles. En fait, il s'agit des étiquettes (IDCandidat:, NomCandidat, ...), des champs (2, Martin, Pauline, ...), mais aussi du bouton Test lui même, ainsi que des deux sous-formulaires. Chaque chose s'appelle un Contrôle.

Dans votre salon, vous avez aussi des Contrôles (des meubles, en fait) : Des chaises, une table, un buffet...

Ici, nous nous intéressons au contrôle IDCandidat, qui est le champ IDCandidat (2) (Et pas son étiquette : son étiquette ne s'appelle pas IDCandidat, mais Étiquette0, si vous allez voir dans ses propriétés).

Si on parlait du tapis de votre salon, on dirait :

Pièces("Salon").Meubles("Tapis")

Et, finalement, ce qui nous intéresse, c'est la valeur, le contenu de IDCandidat, c'est à dire 2. Nous allons donc utiliser la propriété Value.

Forms("F_Candidat").Controls("IDCandidat").Value

Si c'était la propriété prix de votre tapis qui vous intéressait, vous écririez :

Pièces("Salon").Meubles("Tapis").Prix

Numérotation des formulaires

La collection Forms numérote les formulaires OUVERTS de 0 à NombreFormulairesOuverts -1

(Le nombre de formulaires ouverts et fermés s'obtiendrait par Debug.Print CurrentProject.AllForms.Count)

Debug.Print Forms(0).Controls("IDCandidat").Value

Le formulaire sur lequel vous êtes est systématiquement le numéro 0.

Pour reprendre l'histoire de votre appartement, Pièces est la collection de pièces allumées, et Pièces(0) est la pièce dans laquelle vous vous trouvez actuellement.

Pièces(0).Meubles("Tapis").Prix

On suppose qu'il y a un tapis dans la pièce allumée dans laquelle vous vous situez.

On pourrait faire un truc idiot, mais qui fonctionne :

Debug.Print Forms(0).Controls(0).Value

Ca affiche la valeur du contrôle sur lequel on est. Oui, ben on est sur un bouton, puisqu'on clique dessus... Du coup, il affiche la valeur du
bouton : 1 (Pourquoi 1 et pas autre chose, je ne sais pas, et je m'en fiche un peu, à vrai dire...)

Le formulaire courant peut être représenté par Me

Debug.Print Me.Controls("IDCandidat").Value

A la place de Forms(0), on peut utiliser le charmant diminutif Me (Qui veut dire Moi en anglais)

Ainsi, pour revenir à notre appartement, voici le prix du tapis qui se trouve dans la pièce ou je suis :

CettePièce.Meubles("Tapis").Prix

L'objet Controls est optionnel

Il n'est pas nécessaire de préciser qu'IDCandidat est un contrôle, car Control est la collection par défaut de la collection Forms.

Debug.Print Me("IDCandidat").Value

"Par défaut" (qui n'a rien à voir avec la liste des défauts que nous verrons plus tard), c'est ce qu'on répond quand on ne précise rien. Par exemple, si on vous demande "Qui êtes-vous ?", vous donnez votre nom, pas votre pointure de chaussures ! ... C'est seulement quand on vous demande votre pointure de chaussures que vous la donnez. Mais qu'on vous demande "Qui êtes-vous ?" ou "Quel est votre nom ?", dans les deux cas, vous donnez votre nom. Nom est votre propriété par défaut, en quelque sorte.

Reprenons l'exemple de notre appartement, nous pouvons écrire :

CettePièce("Tapis").Prix

à la place de

CettePièce.Meubles("Tapis").Prix

On peut remplacer ("") par ![ ].

Debug.Print Me![IDCandidat].Value
Debug.Print Me.Controls![IDCandidat].Value

C'est une manière différente, mais équivalente d'écrire les choses... Tout comme en français, il existe plusieurs orthographes différentes... Par exemple, on peut dire un après-midi ou une après-midi, les deux sont justes.

Les points d'exclamation ! ne s'écrivent qu'avec la syntaxe des [crochets]. Il n'y a jamais de points d'exclamation avec
la syntaxe des ("parenthèses-guillemets")

Revoici le tapis de notre appartement :

CettePièce![Tapis].Prix
CettePièce.Meubles![Tapis].Prix

Il est possible d'utiliser la syntaxe ("Parenthèses-Guillemets") de cette manière :

Debug.Print Forms("F_Candidat")("IDCandidat")
' Pas de points ni de point d'exclamation :
Debug.Print Forms("F_Candidat")!("IDCandidat")
Debug.Print Forms("F_Candidat")!("IDCandidat")

Que l'on pourrait comparer à la syntaxe identique, avec les ![crochets] :

Debug.Print Forms![F_Candidat]![IDCandidat]
' Ici, le point d'exclamation est obligatoire !
Debug.Print Forms![F_Candidat][IDCandidat]
Pièces("Salon")("Tapis")
Pièces![Salon]![Tapis]

Il est aussi possible  - même si on se demande l'utilité - de mettre des crochets à l'intérieur de champs eux-mêmes écrits entre guillemets et parenthèses, comme ceci :

Debug.Print Forms("[F_Candidat]")("[IDCandidat]")

L'inverse ne fonctionne pas :

Debug.Print Forms!["F_Candidat"]!["IDCandidat"]
Debug.Print Forms![("F_Candidat")][("IDCandidat")]

Substitution des Points d'exclamation ! et des points.

Tant qu'il s'agit de propriétés (.Value par exemple), le point . est obligatoire. Par contre, lorsque nous évoquons l'élément d'une collection, les ! et les . sont parfois également valides.

Les deux lignes suivantes sont correctes :

Debug.Print Forms![F_Candidat]![IDCandidat]
Debug.Print Forms![F_Candidat].[IDCandidat]

Mais, étrangement, les deux suivantes ne fonctionnent pas :

Debug.Print Forms.[F_Candidat]![IDCandidat]
Debug.Print Forms.[F_Candidat].[IDCandidat]

Aussi, je vous recommande donc de n'utiliser que les ! pour séparer les collections des objets, et le point . pour préciser une propriété, ou une collection, comme ceci :

Debug.Print Forms![F_Candidat].Controls![IDCandidat].Value

Analysons :

  1. F_Candidat est un élément de la collection Forms, donc point d'exclamation ! :
    Forms![F_Candidat]
  2. De cet élément de collection F_Fandidat, je parle de l'objet-collection Controls, donc point . :
    [F_Candidat].Controls
  3. De cette "sous-collection" controls, je m'intéresse à l'élément de collection IDCandidat, donc point d'exclamation ! :
    Controls![IDCandidat]
  4. De cet élément de collection IDCandidat, je m'intéresse a la propriété Value, donc point . :
    [IDCandidat].Value

Vous pourriez me répondre : "Oui, mais Value, c'est une des multiples propriétés de l'élément de collection IDCandidat. Il y en a plein d'autres des propriétés : La couleur, la taille, la police de caractères...), c'est donc a nouveau un élément de collection, et il faudrait utiliser également le point d'exclamation !, comme ceci :

Forms![F_Candidat].Controls![IDCandidat]![Value] 

Ou même comme ceci :

Forms![F_Candidat].Controls![IDCandidat].Properties[Value]

Il semble que le point d'exclamation ! permet uniquement de référencer des éléments de collection défini par l'utilisateur (Collection de formulaires, de champs), tandis que lorsqu'il s'agit de collections internes, dont le nombre est fixe et connu par Access à la base (Collection de propriétés, par exemple), on doit utiliser un point .

... mais cette dernière explication est une hypothèse, car elle n'explique pas pourquoi l'instruction
Debug.Print Forms![F_Candidat].[IDCandidat]
fonctionne...

Absence de [crochets]

On peut se permettre de ne pas mettre les crochets si le nom du champ ne contient pas d'espaces

Debug.Print Me!IDCandidat.Value
Debug.Print Forms!F_Candidat.Controls!IDCandidat.Value
Debug.Print Forms!F_Candidat!IDCandidat.Value
Debug.Print Forms!F_Candidat!IDCandidat

Et dans notre appartement :

CettePièce!Tapis.Prix
Pièces!Salon.Meubles!Tapis.Prix
Pièces!Salon!Tapis.Prix
Pièces!Salon!Tapis

Propriété par défaut Value (optionnel)

Tout comme on n'était pas obligé de préciser la collection Controls, plus haut, la propriété Value est la propriété par défaut des contrôles, et on n'est donc pas obligé de l'indiquer :

Debug.Print Me!IDCandidat      

Si la valeur par défaut des objets des pièces de votre appartement était le prix, voici comment parler du prix du tapis qui se trouve dans la pièce dans laquelle vous êtes :

CettePièce.Tapis

L'indication de Me ou Forms(0) est optionnel

Il est également possible d'omettre carrément le mot-clé Me. Access comprend que nous parlons du contrôle IDCandidat :

Debug.Print IDCandidat

En finalité, il ne reste plus grand chose. Nous sommes passés de :

Debug.Print Forms("F_Candidat").Controls("IDCandidat").Value

à

Debug.Print IDCandidat

Pareil pour notre appartement. Nous sommes passés de :

Pièces("Salon").Controls("Tapis").Prix

à

Tapis

Mélange de syntaxes

Il est possible de mixer les syntaxes, mais ça donne évidemment lieu à du code fort peu lisible. de plus, pas tout fonctionne !

' Syntaxes correctes :
Debug.Print Forms("F_Candidat")("IDCandidat")
Debug.Print Forms!F_Candidat!IDCandidat
Debug.Print Forms("F_Candidat")![IDCandidat]
Debug.Print Forms("F_Candidat")!IDCandidat
' Syntaxes incorrectes :
Debug.Print Forms[F_Candidat]!("IDCandidat")
Debug.Print Forms[F_Candidat].("IDCandidat")
Debug.Print Forms[F_Candidat]("IDCandidat")

Syntaxes d'accès aux sous-formulaires

Enregistrement courant des sous-formulaires

Nous allons commencer par afficher le premier défaut de Pauline Martin : Snobinarde. Voici le code du bouton Test qui se trouve toujours dans le formulaire principal :

Debug.Print SFDefaut.Form!Defaut

Il va afficher Snobinarde. C'est le premier enregistrement, le premier défaut en haut du sous-formulaire SFDefaut de Pauline Dupont.

Pourquoi le premier ? ... Dès l'arrivée dans un enregistrement du formulaire principal, Access détermine que tous les enregistrements courants de tous les sous-formulaires sont le premier : Snobinarde pour les défauts, et Belle pour les qualités.

Maintenant :

  1. Cliquez sur un autre défaut de Pauline (Fainéante par exemple)
  2. Cliquez sur le bouton Test

Il va afficher Fainéante, cette fois. Fainéante est donc l'enregistrement actif du sous-formulaire, même si nous sommes dans le formulaire principal.

Nous verrons plus tard comment se déplacer, ajouter, modifier et supprimer des défauts.

Le contrôle de sous formulaire

La zone de sous-formulaire est un contrôle comme un autre. Ainsi, le formulaire principal est constitué d'étiquettes, de champs, et de zones de sous-formulaires.

Si vous affichez les propriétés de la zone d'accueil du sous-formulaire (En mode création, vous cliquez juste une fois sur la zone de sous-formulaire, et bouton droit de la souris : Propriétés. Vous y verrez alors le nom : SFDefaut (C'est moi qui lui ai imposé ce nom - SF comme Sous-Formulaire). Attention : Si vous cliquez une fois de trop sur la zone d'accueil du sous formulaire, les petites poignées disparaîtront. Il faudra alors cliquer complètement à côté, et recliquer une seule fois dans votre zone de sous-formulaire. C'est subtil : il y a une zone d'accueil qui contient à l'intérieur d'elle-même : un formulaire.

Si on voulait reprendre la métaphore de l'appartement, c'est un peu comme si, parmi les meubles de vos pièces, il y avait des meubles qui peuvent contenir des collections de choses. Un tapis ou une chaise, non, mais un buffet, oui !

Propriétés des zones d'accueil des sous-formulaires

Comme SFDefaut est un contrôle, on peut lui demander ses propriétés, comme un champ :

Debug.Print NomCandidat.Value ' Affiche : Martin
Debug.Print SFDefaut.Name ' Affiche : SFDefaut
Debug.Print SFDefaut.Value ' Provoque une erreur, car il n'y a pas
' de propriété Valeur pour une zone d'accueil de sous-formulaire

Nous pouvons l'appeler en utilisant des syntaxes plus complètes, comme nous avons vu au début. Comme la propriété Value n'existe pas, je vais en profiter pour vous en montrer d'autres, en même temps que les différentes propriétés possibles. (Je ne les explique pas, car elles ne sont pas forcément utiles en VBA, et ça nous ferait trop sortir du sujet)

Debug.Print Forms("F_Candidat").Controls("SFDefaut").Name ' SFDefaut
Debug.Print          Forms("F_Candidat")("SFDefaut").SourceObject ' F_DefautSF
Debug.Print       Forms!F_Candidat.Controls!SFDefaut.Locked ' Faux
Debug.Print                     Me.Controls!SFDefaut.Visible ' Vrai
Debug.Print                              Me!SFDefaut.Width ' 4935

Pour notre buffet, ce serait :

Pièces("Salon").Meubles("Buffet").Couleur ' Marron
        Pièces("Salon")("Buffet").Poids ' 175
      Pièces!Salon.Meubles!Buffet.Hauteur ' 155
        CettePièce.Meubles!Buffet.Prix ' 976.5
                CettePièce!Buffet.Matière ' Chêne

C'est donc à partir de ce SFDefaut que nous allons travailler.

Accès a un champ de l'enregistrement actif d'un sous-formulaire

Si nous voulons accéder au champ Defaut du sous-formulaire SFDefaut, on écrit ceci :

debug.print Me!SFDefaut!Defaut.Value

Ce qui équivaudrait à ceci, si on voulait parler de la couleur d'une chemise rangée dans le buffet :

CettePièce!Buffet!Chemise.Couleur

Le sous-objet Form

Toutefois, normalement, la collection des champs ne se trouve pas directement dans la zone d'accueil du sous-formulaire nommé SFDefaut, mais à l'intérieur d'un objet Form qui est l'objet global directement inclus dans la zone d'accueil.

Un petit exemple vaut mieux qu'on long discours :

' Première possibilité :
Debug.Print Me!SFDefaut.Form!Defaut.Value
' Seconde possibilité :
Debug.Print Me!SFDefaut!Defaut.Value

Je regrette que cet objet Form ne se soit pas appelé SubForm, ce qui m'aurait paru plus juste et plus simple.

Il ne faut pas confondre la collection de formulaires ouvertes (forms avec un s) et le sous-formulaire qui se trouve dans la zone d'accueil du sous formulaire (Form sans s) :

Debug.Print Forms!F_Candidat.Controls!SFDefaut.Form!Defaut

Pourquoi compliquer les choses avec l'ajout de ce Form au milieu de notre ligne VBA ? ... Si ça marche sans, quel est l'intérêt ? Je me suis parfois trouvé dans des cas ou l'omission de ce mot-clé Form provoque une erreur, pure et simple. Gardez-le simplement dans un coin de votre esprit.

Toutes les syntaxes d'accès aux champs du formulaire principal que nous avons vu en début de ce didacticiel sont transposables sur les champs du sous-formulaire.

Nous avons vu que nous pouvons accéder aux champs du formulaire principal en écrivant juste leur nom :

Debug.Print Forms("F_Candidat").Controls("IDCandidat")
' équivaut à :
Debug.Print IDCandidat

Sur le même principe, nous pouvons détailler plus ou moins les champs du sous-formulaire :

Debug.Print SFDefaut.Form.Controls("Defaut").Value

Syntaxes extrêmes d'appel à un champ de sous-formulaire

Ainsi, voici deux syntaxes équivalentes, mais extrêmes :

' La première, réduite au plus court :
Debug.Print SFDefaut!Defaut
' La seconde, la plus longue possible :
Debug.Print Forms("[F_Candidat]").Controls("[SFDefaut]").Form.Controls("[Defaut]").Value

La syntaxe plus longue n'est pas à négliger dans le cas ou, d'un sous-formulaire, on accède à un autre sous-formulaire. Par exemple, imaginons que nous sommes dans le sous-formulaire SFQualite, et qu'il y a un bouton dans ce sous-formulaire, qui affiche le Défaut actif de l'autre sous-formulaire. Dans ce cas, il faudra bien préciser le point de départ de notre recherche, comme ceci, par exemple :

Debug.Print Forms!F_Candidat.SFDefaut!Defaut

Ajout d'un défaut pour Pauline Martin

Admettons qu'on veuille, lorsqu'on clique sur le bouton Test du formulaire principal , lui ajouter le défautRancunière :

Pour commencer, il s'agit d'ajouter un nouvel enregistrement au sous-formulaire SFDefaut, comme ceci :

SFDefaut.Form.Recordset.AddNew

Cette fois, par contre, il est indispensable d'indiquer l'objet Form.

SFDefaut.Recordset.AddNew
' Ne fonctionne pas.

Ecrivons Rancunière comme nouveau défaut (Et cette fois, on peut donc se passer de Form) :

SFDefaut.Form.Recordset.AddNew
SFDefaut!Defaut = "Rancunière"

Voici d'autres syntaxes plus complètes :

Me.Form.SFDefaut.Form.Recordset.AddNew
Me.Form!SFDefaut.Form.Recordset.AddNew
Me.Form("SFDefaut").Form.Recordset.AddNew

Comme Form est un type de contrôle, on peut substituer Form à Controls, comme ceci :

Me.Controls!SFDefaut!Defaut = "Rancunière"

Si le bouton de test avait été dans un autre formulaire ouvert, ou dans le sous-fomulaire des qualités, On aurait donc dû remplacer Me par le nom du formulaire complet :

Forms!F_Candidat.SFDefaut.Form.Recordset.AddNew
Forms!F_Candidat.SFDefaut!Defaut = "Rancunière"

Les syntaxes utilisant ("Parenthèses-guillemets") fonctionnent aussi bien :

Forms("F_Candidat").Form("SFDefaut")("Defaut") = "Rancunière"
' Cette écriture me parait particulièrement lisible :
Forms("F_Candidat")("SFDefaut")("Defaut") = "Rancunière"
'Ainsi que les syntaxes "mixtes" (Particulièrement illisibles, a contrario) :
Forms!F_Candidat.Form("SFDefaut")("Defaut") = "Rancunière"
Forms![F_Candidat].Form("SFDefaut")!Defaut = "Rancunière"

Par contre, il faut choisir et ne pas confondre :

Me.Form![SFDefaut][Defaut] = "Rancunière"
' Ne fonctionne pas, car il manque le ! entre [SFDefaut] et [Defaut] 
Me.Form("SFDefaut").("Defaut") = "Rancunière"
Me.Form("SFDefaut")!("Defaut") = "Rancunière"
' Ne fonctionnent pas, car cette fois, on ne peut rien mettre comme signe
entre les parenthèses-guillemets

Il est également possible de n'utiliser ni les [crochets], ni les ("Parenthèses guillemets"). Attention : Cette technique ne fonctionne QUE si les noms des formulaires et des champs ne comportent pas d'espace !

Forms!F_Candidat.Form!SFDefaut!Defaut = "Rancunière"
              Me.Form!SFDefaut!Defaut = "Rancunière"
                 Form!SFDefaut!Defaut = "Rancunière"
                   Me.SFDefaut!Defaut = "Rancunière"
                      SFDefaut!Defaut = "Rancunière"
                               Defaut = "Rancunière"

Déplacements dans un sous-formulaire depuis le formulaire principal

Voici l'état du sous-formulaire SFDefaut, pour Pauline Martin :

Un bouton dans le formulaire principal permet de se déplacer dans les enregistrements. Nous allons utiliser l'objet RecordSet (Ensemble d'enregistrements).

Voici les différents déplacements, illustrés avec différentes syntaxes possibles :

                                SFDefaut.Form.Recordset.MoveFirst ' Dépensière
                       Forms(0)!SFDefaut.Form.Recordset.MoveNext ' Snobinarde
            Forms("F_Candidat").SFDefaut.Form.Recordset.MoveLast ' Rancunière
Forms("F_Candidat").Controls("SFDefaut").Form.Recordset.MovePrevious ' Fainéante
    Forms![F_Candidat].Controls!SFDefaut.Form.Recordset.AddNew ' Sous Rancunière

Admettons un bouton situé dans le sous-fomulaire F_Qualité. Quand on clique dessus, on veut remplacer le 2ème défaut de Pauline Martin par Hautaine (On remplace donc le mot snobinarde par Hautaine) :

' 1. On se place sur le premier défaut :
Forms("F_Candidat")("SFDefaut").Form.Recordset.MoveFirst
' 2. On descend d'un défaut :
Forms("F_Candidat")("SFDefaut").Form.Recordset.MoveNext
' 3. On écrit simplement Hautaine dans le champ Défaut :
Forms("F_Candidat")("SFDefaut")("Defaut") = "Hautaine"

Recherche d'un enregistrement avec FindFirst

Nous allons utiliser la méthode FindFirst. Si nous sommes dans un bouton situé sur le formulaire principal, et que nous recherchons une personne qui s'appelle Muller :

Me.Recordset.FindFirst "NomCandidat = 'Muller'"
' Ou
Forms("F_Candidat").Recordset.FindFirst "[NomCandidat] = 'Muller'"
'ou, on peut même se permettre d'omettre le mot-clé Me. :
Recordset.FindFirst "NomCandidat = 'Muller'"

Notez les 'apostrophes' au sein des "guillemets". Ils sont indispensables en cas de recherche de texte. Si nous avions recherché un champ numérique, ils auraient été interdits :

Me.Recordset.FindFirst "IDCandidat = 3"

Si le nom contient une apostrophe ', il faut la doubler :

Recordset.FindFirst "NomCandidat = 'D''Artagnan'"

Mais nous nous éloignons de notre sujet !!!

Si nous sommes dans le sous-formulaire SFQualite, et que nous cherchons le défaut Fainéante dans le sous formulaire SFDefaut, il faut toujours tout détailler :

Forms("F_Candidat")("SFDefaut").Form.Recordset.FindFirst "Defaut = 'Fainéante'"

L'exemple suivant suppose que nous soyions sur le premier enregistrement (Jean Dupont), dans le sous-formulaire SFQualite. Nous commençons par rechercher l'IDCandidat2 (Pauline Martin), et, une fois sur cet enregistrement, on recherche son défaut Fainéante :

Forms("F_Candidat")                 .Recordset.FindFirst "IDCandidat =  2"
Forms("F_Candidat")("SFDefaut").Form.Recordset.FindFirst "Defaut = 'Fainéante'"

Notez que si vous cherchez quelque chose qui n'existe pas, aucune erreur ne sera générée !!! Notez que la méthode s'appelle FindFirst et pas simplement Find, car il s'arrête dès qu'il a trouvé le premier (first) enregistrement qui correspond au critère. S'il y avait plusieurs candidats qui portaient le N°2, FindFirst se serait arrêté au premier.

Effacement d'un enregistrement d'un sous-formulaire

Si nous sommes dans le formulaire principal, nous pouvons effacer carrément tout l'enregistrement courant : Si on est Sur Pauline Martin, ce sont toutes ses données, ses qualités et ses défauts qui vont être supprimées :

Me.Recordset.Delete
' ou :
Recordset.Delete

Si des Défauts ou des Qualités existent pour une personne, cette méthode ne fonctionnera pas. Il faut préalablement aller dans les relations, et double-cliquer sur la relation qui joint T_Candidat et T_Qualite, et cocher la case "Effacer en cascade les enregistrements correspondants.

Faites la même chose pour la relation T_Candidat - T_Defaut.

La suppression d'un enregistrement dans le sous-formulaire SFDefaut ne pose, quant à elle, pas de souci.

Voici comment supprimer Hautaine de la liste des défauts de Pauline Partin :

Me.Recordset.FindFirst "NomCandidat = 'Martin'"
SFDefaut.Form.Recordset.FindFirst "Defaut = 'Hautaine'"
SFDefaut.Form.Recordset.Delete

Tris dans les sous formulaires

Imaginons un bouton defaut placé dans le sous-formulaire SFDefaut, comme ceci :

Quand on clique dessus, il faudrait que le sous-formulaire trie par "Défaut". Il suffit de mettre le code suivant sur Clic du bouton :

Me.OrderBy = "Defaut"
Me.OrderByOn = True

Attention : Le nom du champ Defaut est "entre guillemets". Pas True !

Si on omet OrderByOn, ça ne marche pas. Il mémorise le tri, mais ne l'applique pas.

Si on voulait qu'il trie par ordre alphabétique inverse, on aurait écrit :

OrderBy = "Defaut DESC"

Tri alternatif

Si on veut qu'il trie une fois dans l'ordre alphabétique,et, si on reclique sur le même bouton, qu'il trie dans l'ordre inverse, etc. Il faudrait écrire :

If OrderBy = "Defaut" Then
   OrderBy = "Defaut DESC"
Else
   OrderBy = "Defaut"
End If
OrderByOn = True

Si le bouton de demande de tri dans le sous-formulaire avait été dans le formulaire principal, on aurait dû écrire :

                 SfDefaut.Form.OrderBy = "Defaut"
                 SfDefaut.Form.OrderByOn = True

Et si le bouton de demande de tri de Defaut était carrément placé dans le sous-formulaire SFQualite :

Forms!F_Candidat!SfDefaut.Form.OrderBy = "Defaut"
Forms!F_Candidat!SfDefaut.Form.OrderByOn = True

Filtre dans les sous-formulaires

La recherche trouve un enregistrement, sans masquer les autres. Le filtre masque les enregistrements qui ne correspondent pas.

Vous avez sans doute compris le principe de la syntaxe, toujours le même :

Si je suis dans le formulaire principal, et que je veux filtrer des enregistrements du formulaire principal, par exemple, l'ensemble de tous les candidats dont le nom est Martin:

Me.Filter = "NomCandidat = 'martin'"
' Tout comme pour le tri, il faut activer le filtre :
Me.FilterOn = True
' (ici aussi, le Me. est facultatif)

Si je suis dans le formulaire principal et que je veux filtrer tous les défauts d'une personne qui contiennent la lettre è (e accent grave):

SFDefaut.Form.Filter = "Defaut like '*è*'"
SFDefaut.Form.FilterOn = True

Et si je suis dans un autre formulaire qui n'a rien à voir, ou dans le sous-formulaire SFQualite, et que je désire tous les défauts qui commencent par la lettre g, jusqu'à z (Plus grand que g, en d'autres mots):

Forms!F_Candidat!SFDefaut.Form.Filter = "Defaut > 'g'"
Forms!F_Candidat!SFDefaut.Form.FilterOn = True

Cas pratique : Zone de filtre dynamique

Je suis dans SFDefaut.

Soit une zone d'édition, que nous appelleront EDIFiltre, et un bouton BDCOK,
comme ceci :

 

Dans cet exemple, j'ai écrit sièr dans la zone de filtre. Si je clique sur OK, je veux voir tous les défauts qui contiennent sièr (En l'occurrence, Dépensière). Sur le bouton OK, je copie ce code :

Filter = "Defaut like '*sièr*'"
FilterOn = True

Mais ce n'est pas dynamique. Pour qu'il prennent le contenu de la zone de texte dynamiquement, il faut écrire :

Filter = "Defaut like '*" & EDIFiltre & "*'"

Attention : A cause des apostrophes, ce code ne fonctionnera pas en recherchant des défauts qui contiendraient eux-même une apostrophe. Bon, on n'imagine pas un tel défaut, mais selon les zones a chercher, ça peut poser problème.

Concernant les filtres et les tris, ces astuces font un peu double emploi avec les menus contextuels d'Access, qui proposent ces filtres et ces tris. Mais si vous installez votre application Access sur un poste client qui n'a que le run-time Access, ces menus contextuels n'existent plus : il faut le savoir !

Cas pratique : Transformation d'une qualité en défaut

Voyons comment déplacer un enregistrement d'un sous-formulaire à l'autre.

Imaginons que Blonde est finalement plutôt un défaut qu'une qualité. Il faudrait pouvoir transférer cette donnée comme ceci :

 

On crée un bouton dans SFQualite, comme ceci :

Et on y copie le code suivant (Rien de nouveau, nous avons étudié tous les concepts nécessaires) :

' On ajoute un nouvel enregistrement dans SFDefaut :
Forms!F_Candidat.Form!SFDefaut.Form.Recordset.AddNew
' On y place la qualité sur laquelle on se trouve :
Forms!F_Candidat.Form!SFDefaut!Defaut = Qualite
' On efface la qualité sur laquelle on se trouve :
Recordset.Delete