Formulaires gigognes

Un moyen pour présenter les détails d'une structure avec Access

Claude Leloup

Ce tutoriel propose un moyen de présenter le détail d'une arborescence.

En quelque sorte, des formulaires qui s'emboitent l'un dans l'autre : chacun affichant les éléments qui dépendent de l'enregistrement actif dans le formulaire à sa gauche.

Une technique qui permet de contourner la limite qui veut qu'un formulaire « père » ne peut avoir de « fils » que s'il est affiché en « mode simple » (un enregistrement à la fois).

L'occasion d'exploiter cette contribution d'Arkham46 qui permet d'ouvrir un formulaire à un endroit déterminé par la position d'un contrôle dans un autre formulaire.

Nous proposerons aussi une routine qui ajuste la taille d'un formulaire en « Mode continu » au nombre de ses enregistrements.

Si vous souhaitez réagir à cet article, une discussion est ouverte dans le forum : Commentez

- Titre : Formulaires gigognes

- Auteur : Claude Leloup

- Parution : 19 juin 2016

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2016 Claude Leloup. 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. « Des formulaires gigognes » : de quoi s'agit-il ?

- II. Le nœud de la solution : la routine PositionForm()

- III. Les particularités des formulaires

- IV. Le code associé à fRegions (premier formulaire de la cascade)

- IV-A. À l'ouverture

- IV-B. À la fermeture

- IV-C. À chaque lecture d'un enregistrement

- V. Le code associé aux formulaires suivants de la cascade

- V-A. À l'ouverture

- V-B. À chaque lecture d'un enregistrement

- VI. Adapter la hauteur d'un formulaire continu au nombre de ses enregistrements

- VII. Téléchargement

- VIII. Remerciements

I - « Des formulaires gigognes » : de quoi s'agit-il ?

Imaginez une structure (hexagonale ?) dans laquelle il y aurait des régions, des départements et des communes :

L'idée est de présenter cette structure avec des formulaires qui s'ouvrent en cascade et s'emboitent l'un dans l'autre… comme des tables gigognes :

Ce que nous appelons « formulaire mère gigogne », c'est un formulaire affiché en mode continu dans lequel chaque enregistrement activé provoque l'ouverture instantanée à sa droite d'un autre formulaire juste à la hauteur de l'enregistrement actif.

À l'ouverture :

et si on navigue dans l'un des formulaires, ceux à sa droite s'adaptent instantanément :

II - Le nœud de la solution : la routine PositionForm()

Cette routine est proposée à cette adresse par Arkham46.

Même si vous ne comprenez pas à quoi servent toutes les API auxquelles il fait appel dans son code, faites confiance : ça marche au poil !

Il suffit de copier le code proposé dans un module (dans notre base exemple, c'est le module mArkham46).

Et pour l'utiliser, deux lignes de code :

DoCmd.OpenForm "FormulaireAPositionner", , , , , acHidden
PositionForm Forms("FormulaireAPositionner"), Me.UneZoneDeTexte

Deux précautions :

- le formulaire à ouvrir doit être en fenêtre indépendante :

- en cas d'appels successifs, refermez d'abord le formulaire précédemment ouvert avant de l'ouvrir une nouvelle fois.

III - Les particularités des formulaires

L'en-tête s'adapte pour afficher le texte de l'enregistrement actif dans le formulaire mère gigogne.

L'enregistrement actif s'affiche dans une couleur distincte.

Le pied du formulaire s'affiche au raz du dernier enregistrement.

La hauteur du formulaire s'adapte au nombre d'enregistrements à afficher.

La source contient toute la table des départements, nous monterons plus loin comment passer le filtre qui limitera la liste aux départements de cette région.

Fenêtre indépendante, routine PositionForm() oblige !

Pas de possibilité de fermer ni déplacer : c'est le 1er formulaire de la cascade qui commande l'ouverture , la fermeture et la position de l'ensemble.

Une étiquette, à l'origine vierge, que nous complèterons à la volée dans l'événement ouverture du formulaire.

Une zone de texte visible, mais non perceptible par l'utilisateur (hauteur = 0 !). Elle sera aménagée à chaque lecture d'enregistrement, pour déclencher la mise en forme conditionnelle (changement de couleur pour mettre l'enregistrement actif en évidence).
Accessoirement, ce contrôle qui est le seul avec « Arrêt sur tabulation » servira de piège à focus : on évite ainsi que le curseur se positionne sur un autre contrôle et en change l'aspect.

IV - Le code associé à fRegions (premier formulaire de la cascade)

IV-A - À l'ouverture

Private Sub Form_Open(Cancel As Integer)
DoCmd.MoveSize 500, 500
'Ajuster la hauteur
Call AmenagerTailleFormulaire(5000)
End Sub

La méthode MoveSize de l'objet DoCmd permet de positionner un formulaire, ici nous l'ouvrons à 500 twips (à la grosse louche : 1 cm) plus à droite et plus bas que le coin supérieur gauche de la fenêtre Access. Soit à un endroit qui nous laissera de l'espace pour déployer la cascade.

Nous appelons ensuite la routine AmenagerTailleFormulaire() pour limiter sa hauteur à un maximum de 5000 twips (cette routine est décrite plus bas dans ce tutoriel).

IV-B - À la fermeture

Private Sub Form_Close()
'Refermer tous les autres formulaires de la cascade
If CurrentProject.AllForms("fDepartements").IsLoaded Then DoCmd.Close acForm, "fDepartements"
If CurrentProject.AllForms("fCommunes").IsLoaded Then DoCmd.Close acForm, "fCommunes"
If CurrentProject.AllForms("fDetails").IsLoaded Then DoCmd.Close acForm, "fDetails"
End Sub

Nous refermons aussi tous les formulaires de la cascade.

IV-C - À chaque lecture d'un enregistrement

1. Private Sub Form_Current()

2. 'Pour mettre la ligne active en surbrillance

3. Me.txtActif = Me.txttRegionsPK

4. 'Ouvrir fDepartements (suivant de la cascade)

5. 'd'abord le refermer s'il a déjà été ouvert

6. If CurrentProject.AllForms("fDepartements").IsLoaded Then DoCmd.Close acForm, "fDepartements"

7. DoCmd.OpenForm "fDepartements", , , "tRegionsFK =" & Me.txttRegionsPK, , acHidden

8. PositionForm Forms("fDepartements"), Me.txtActif

9. 'Ajuster toutes les mères gigognes à sa droite

10. Call Forms("fDepartements").Form_Current

11. Call Forms("fCommunes").Form_Current

12. End Sub

Ligne 3 : on copie la valeur de la clé dans txtActif. Comme la clé est unique, l'enregistrement en cours est donc le seul pour lequel on a l'égalité entre txttRegionsPK et txtActif (dans tous les autres, txtActif a la valeur Null). Cette astuce permet d'exploiter la mise en forme conditionnelle pour allumer les contrôles d'une couleur particulière seulement sur la ligne active :

Lignes 7 et 8 : on provoque l'ouverture du formulaire suivant de la cascade.

Remarque

Lignes 10 et 11 : pour positionner correctement les formulaires qui s'ouvriront à la droite de celui-ci.

Faites l'expérience de supprimer ces instructions (en les mettant en commentaires),

vous obtiendrez ceci :

On peut faire appel à une routine d'un autre formulaire pour autant que celle-ci soit déclarée « Public ».

V - Le code associé aux formulaires suivants de la cascade

V-A - À l'ouverture

Private Sub Form_Open(Cancel As Integer)
'Compléter le sous-titre
Me.EtiSousTitre.Caption = "de la région " & Forms!fRegions!txtRegion
'Ajuster la hauteur
Call AmenagerTailleFormulaire(5000)
End Sub

On complète l'étiquette sous-titre en copiant le texte affiché dans l'enregistrement actif du formulaire situé à sa gauche.

V-B - À chaque lecture d'un enregistrement

Public Sub Form_Current()
'Pour mettre la ligne active en surbrillance
Me.txtActif = Me.txttDepartementsPK
'Ouvrir fCommunes (suivant de la cascade)
'Vérifier qu'il y a au moins une commune
If DCount("tDepartementsFK", "tCommunes", "tDepartementsFK=""" & Me.txttDepartementsPK & """") = 0 Then Exit Sub
'Ouvrir
'D'abord le fermer s'il est ouvert
If CurrentProject.AllForms("fCommunes").IsLoaded Then DoCmd.Close acForm, "fCommunes"
DoCmd.OpenForm "fCommunes", , , "tDepartementsFK =""" & Me.txttDepartementsPK & """", , acHidden
PositionForm Forms("fCommunes"), Me.txtActif
'Ajuster toutes les mères gigognes à sa droite
Call Forms("fCommunes").Form_Current
End Sub

Comme annoncé plus haut, cette Sub est déclarée « Public ».

C'est la seule différence (mutatis mutandis) par rapport au premier formulaire de la cascade.

VI - Adapter la hauteur d'un formulaire continu au nombre de ses enregistrements

1. Public Sub AmenagerTailleFormulaire(HauteurMax As Long)

2. Dim EspaceNecessaire As Long

3. Dim i As Integer

4. Dim lngHauteurAjustee As Long

5. On Error GoTo GestionErreurs

6. With CodeContextObject

7. 'Vérifier qu'il s'agit d'un formulaire qui affiche plusieurs enregistrements à la fois

8. If .DefaultView = 0 Then

9. MsgBox "Ce formulaire affiche un seul enregistrement à la fois, la routine ne s'applique pas"

10. Exit Sub

11. End If

12. 'Calculer l'espace nécessaire pour afficher tous les enregistrements

13. .RecordsetClone.MoveLast 'pour pouvoir ensuite compter le nbre d'enregistrements

14. EspaceNecessaire = .Section(acHeader).Height _

15. + .Section(acFooter).Height _

16. + .Section(acDetail).Height _

17. * (.RecordsetClone.RecordCount - .AllowAdditions) 'Ceci revient à ajouter 1 si ajout autorisé

18. 'Limitation au maximum possible

19. If EspaceNecessaire > HauteurMax Then  'trop pour tout afficher

20. .ScrollBars = 2 'pour afficher une barre de défilement verticale

21. 'Ajuster la hauteur (pour éviter que le dernier enregistrement ne soit amputé)

22. Do Until lngHauteurAjustee > HauteurMax

23. lngHauteurAjustee = .Section(acHeader).Height _

24. + .Section(acFooter).Height _

25. + .Section(acDetail).Height _

26. * (i - .AllowAdditions)

27. i = i + 1

28. Loop

29. .InsideHeight = .Section(acHeader).Height _

30. + .Section(acFooter).Height _

31. + .Section(acDetail).Height _

32. * (i - 1 - .AllowAdditions)

33. Else

34. .ScrollBars = 0  ' pas de barre de défilement verticale

35. .InsideHeight = EspaceNecessaire

36. End If

37. End With

38. GestionErreurs:

39. Select Case Err.Number

40. Case 0        'Pas d'erreur

41. Case 3021 'Pas d'enregistrement

42. DoCmd.MoveSize 0, 0, 0, 0  'ce qui a pour conséquence de cacher le formulaire

43. Case Else

44. MsgBox "Erreur dans AmenagerTailleFormulaire : " & vbLf & "N° erreur : " & Err.Number & " " & Err.Description, vbCritical

45. End Select

46. End Sub

Commentaires du code

6 : la propriété CodeContextObject permet de déterminer l'objet pour lequel du code Visual Basic est en cours d'exécution.

Quand la routine AmenagerTailleFormulaire () s'exécute, CodeContextObject représente l'objet (ici le formulaire) qui a déclenché l'exécution. Il hérite alors de toutes les propriétés de ce dernier.

Concrètement, si à son ouverture, le formulaire fRegions appelle AmenagerTailleFormulaire(), nous aurons accès à toutes les propriétés du formulaire fRegions. Par exemple :

CodeContextObject.DefaultView donnera la valeur du mode d'affichage (0 pour Mode simple, 1 pour mode continu…) ;

CodeContextObject.Section(acHeader).Height donnera la hauteur (en twips) de l'en-tête ;

CodeContextObject.RecordsetClone.RecordCount donnera le nombre d'enregistrements…

Remarquez l'instruction ligne 6 vb With => ligne 37 End With.

L'instruction With permet d'appliquer une série d'instructions à l'objet indiqué (ici le formulaire), sans devoir le qualifier à chaque fois. Cela permet d'alléger l'écriture du code : on pourra écrire .DefaultView au lieu de CodeContextObject.DefaultView.

8 - 10 : si le formulaire est affiché en « Mode simple » (un enregistrement à la fois), l'appel à cette routine n'a pas de sens : on sort.

13 - 17 : l'espace nécessaire pour tout afficher, c'est la somme des hauteurs de l'en-tête, du pied et autant de fois la hauteur d'une section détail qu'il y a d'enregistrements, plus éventuellement la dernière section détail réservée à l'encodage d'un ajout (la propriété « Ajout autorisé » a alors la valeur True, c'est-à-dire -1, sinon elle vaut False, donc 0).

N.B. Si le formulaire appelant est vide (zéro enregistrement) l'instruction 13 va provoquer une erreur 3021 qui sera traitée aux instructions 42 - 43 (on rend le formulaire imperceptible : il est visible, mais toutes ses dimensions sont à 0).

19 - 36 : de deux choses l'une :

- l'espace nécessaire est plus grand que l'espace maximum demandé (19 - 32) ;

- on a assez de place pour tout montrer (34 - 35).

19 - 32 : on installe une barre de défilement verticale et ensuite, on fait en sorte que seules des sections « Détail » entières soient affichées. On veut par exemple éviter ceci :

On ajoute autant de sections Détail pour dépasser la limite et on en retranche une pour obtenir ceci :

VII - Téléchargement

La base exemple au format Access2000 se trouve ici.

VIII - Remerciements

Reconnaissance à Thierry GASPERMENT (Arkham46) pour ses nombreuses contributions.

Merci à Malick Seck (milkoseck) et Winjerome pour la relecture.