Lorsque vous utilisez des données relationnelles, il est souvent nécessaire d’afficher les données de plusieurs tables ou requêtes dans le même formulaire. Le sous-formulaire est un outil pratique qui permet de réaliser cette opération. Microsoft Access propose plusieurs méthodes pour créer rapidement des sous-formulaires.
Un sous-formulaire est un formulaire inséré dans un autre formulaire Microsoft Access. Le formulaire de base est appelé formulaire principal et le formulaire qu’il contient, sous-formulaire. Une combinaison formulaire/sous-formulaire est parfois nommée formulaire hiérarchique ou formulaire père/fils.
Problèmes avec modèle actuel
- Table Facture contient trop de répétitions – chaque enregistrement représente un film loué – on doit répéter Numéro de client, date sortie, etc.
- Très difficile de produire une seule facture pour plusieurs films loués – on pourrait créer un formulaire tabulaire mais, il ne sera pas pratique: comment choisir le client? quelle date? quels films vont sur quelle facture? C’est possible en codant la Requête appropriée et en manipulant les propriétés de l’État pour obtenir un résultat passable – mais il manque toujours les taxes, les totaux, etc.
Nouveau modèle
- D’abord, revoyons le modèle élémentaire que nous avons développé pour VIDEO:
Comment est programmée la relation “Loue” entre Client et Film? Pouvons-nous ajouter un champ pour “Numéro de film loué” à Client? Un champ pour “Client qui loue le film” à Film? La solution retenue consiste à créer une nouvelle Table: Location – mais on a vu que ce modèle aussi a des faiblesses Meilleure solution: briser la table Location en deux: Location_Maitre et Location_Detail– la table Location_Maitre contient toutes les informations sur la transaction au complet et la table Location_Detail contient les détails des films loués
- Le dessin a été créé avec le logiciel de modélisation PowerDesigner de Sybase – Platinum Technology vendent un autre outils du même genre bien connu: ERWin – ce genre de logiciel nous permet de dessiner un modèle et, à partir du modèle, il peut créer les tables, relations, etc., automatiquement – on peut aussi faire du “reverse engineering” où on dessine un modèle à partir d’une base existante (souvenez-vous en lorsqu’on vous demandera de faire des modifications sur une bd où le concepteur n’a pas tenu sa documentation à jour!)
- Notez: il est nécessaire de créer une nouvelle base de données pour passer d’un modèle à l’autre mais, il n’est pas nécessaire de refaire toutes les tables – on crée une nouvelle bd, VIDEO2, par exemple, et on importe les tables qu’on veut garder de VIDEO – dans les vraies applications on parle de différentes versions de la bd – on garde l’ancienne version comme backup au cas où il faudrait y revenir
Nouveau formulaire
- Pour saisir les détails de la location de films par le client, nous allons créer un formulaire d’après un modèle très commun qu’on appelle “Maître/détails” ou “Master/Detail” – ce modèle est utilisé pour toutes sortes de situations fréquentes dans un système de gestion: bon de commande, bon d’achat, facture, liste de cours d’un étudiant, etc.
- La partie Maître du formulaire contient toutes les informations communes à la transaction: nom et adresse du client, livraisons, etc.
- La partie Détails contient tous les détails des items de la transaction: nom de chaque produit vendu, prix, quantité, etc.
- Par exemple, dans la bd démo, COMPTOIR.MDB, le formulaire “Commande” sert à saisir les produits commandés par le client – on utilise un formulaire principal pour la partie Maître et un sous-formulaire pour la partie Détails:
Pour pouvoir atteindre ce résultat, il faut avoir la structure appropriée.
Il faut avoir une table Maitre et une table Détails dans la base de données. Dans Northwind, ce sont les tables Orders et Order Details:
- Comment cela se compare-t-il avec la location de films? À une date et une heure spécifique, un client qui a un nom et une adresse loue un certain nombre (1-n) de films; chaque film est loué à un prix quelconque et a une date de retour différente; après avoir saisi tous les films on calcule le sous-total, on ajoute les taxes et on collecte le total dû
Technique de création du formulaire
- Évidemment, il faut avoir créer les tables et les relations comme spécifié dans le modèle.
- Créer une requête pour le formulaire-maître – cette requête est basée sur les tables Location_Maitre et Clients – doit absolument contenir le champ commun “Numéro de location” – peut aussi contenir les champs calculés dont on aura besoin plus tard – par exemple, faire une concaténation de nom et prénom:
Créer une requête pour le formulaire-détails – cette requête est basée sur les tables Location_Details et Films – doit absolument contenir le champ commun “Numéro de location” Créer le formulaire-maître avec l’Assistant-formulaire – en choisissant les champs des deux requêtes, on peut créer automatiquement un Formulaire/Sous-formulaire
Modifier le Sous-formulaire pour y ajouter un champ pour “Sous-total” dans le Pied de formulaire: Dans le formulaire-maître, modifier l’apparence, mettre les “Arrêt Tab = Non” aux bons endroits, modifier l’Ordre de tabulation. Ajouter les contrôles pour Sous-total, Taxes et Total-final en utilisant le générateur d’expressions
Exemple Formulaire et sous-Formulaire Du côté du programmeur…
- Lorsqu’un formulaire simple est ouvert (par un double-clic ou par un
DoCmd.OpenForm
, notamment), il peut être référencé en VBA parForms![frm Clients]
ouForms("frm Clients")
. Il est également décompté dans la liste des formulaires ouverts (la “collection”Forms
), dont le nombre est obtenu parForms.Count
. - Suite à ce qui a été dit plus haut, on est d’accord que le champ Nom Client du formulaire sera désigné par
Forms![frm Clients]![Nom Client]
, ou sa varianteForms("frm Clients")("Nom Client")
. - Maintenant, si le formulaire Clients contient un sous-formulaire, le “formulaire embarqué” n’est pas ouvert en tant que formulaire autonome. Ce qui veut dire que si vous ouvrez frm Clients (et aussi son sous-formulaire, nécessairement),
Forms.Count
vaut1
, et non pas2
. - Ceci implique que vous ne pouvez pas référencer le formulaire embarqué par
Forms![frm Clients - Locations]
, ni parForms("frm Clients - Locations")
. Il faut au contraire pointer l’objet embarqué dans le rectangle container du formulaire parent (ouf !). D’où la syntaxe :Forms![frm Clients]![sfm Locations].Form
. Pour compléter l’exemple plus haut, la date de location ne peut pas s’obtenir parForms![frm Clients - Locations]![Date de location]
, mais bien parForms![frm Clients]![sfm Locations].Form![Date de location]
. - A noter, tant qu’à être précis (!), que la date de location du sous-formulaire peut prendre plusieurs valeurs (il y a plusieurs enregistrements dans la grille des locations). Du coup, la formule ci-dessus renvoie la date de location de la ligne active, dans le formulaire embarqué…
La classe !
Si on se penche un peu plus en détail sur les classes d’objets impliquées…
- Le formulaire est une instance de la classe
Form
. - Le rectangle container est une instance de la classe
SubForm
(encore une fois, il s’agit du rectangle uniquement, pas du formulaire embarqué dans ce rectangle). - Ce rectangle container fournit deux informations sur l’objet embarqué :
le nom (chaîne de caractères) du formulaire embarqué est donné par la propriétéSourceObject
de la classeSubForm
;
l’instance elle-même du formulaire embarqué est donnée par la propriétéForm
. C’est par cette instance que vous accédez aux données du formulaire embarqué, d’où la syntaxe.Form![Date de location]
. - Par extension, si modifiez la propriété
SourceObject
du rectangle container, vous remplacez le formulaire embarqué par un autre. Du style :Forms![frm Clients]![sfm Location].SourceObject = "frm Test".