I. Prérequis▲
Ce tutoriel s'adresse à des utilisateurs qui maitrisent déjà les bases du logiciel Access ou qui veulent faire l'effort pour progresser : l'utilisation du VBA décuple le potentiel de solutions !
![]() |
Pour vérifier votre niveau, parcourez ces tutoriels : si vous les comprenez facilement, vous êtes OK et si ce n'est pas le cas, insistez : - pour commencer : Maxence Hubiche Access - Les Bases ; - pour construire des requêtes : Jean Ballat Créer des requêtes simples ; - pour construire un formulaire :Jean-Philippe Ambrosino le chapitre 2-1-2 de Mise en surbrillance d'un enregistrement dans un formulaire ; - pour le VBA : Olivier Lebeau Initiation au VBA Office. |
![]() |
D'une manière générale, pour vous documenter sur les propriétés d'un formulaire ou d'un état, ou de leurs contrôles : L'aide Access s'ouvre alors à la bonne page. On peut aussi : |
II. Quelques notions « métier »▲
II-A. Les données qui permettent d'identifier un mouton▲
Le N° d'éleveur
Officiellement appelé « Indicatif de marquage ».
Six chiffres.
Le N° de travail
Cinq chiffres.
Le premier chiffre correspond à l'année de naissance (5 pour la campagne 2015).
Les quatre suivants sont un numéro d'ordre.
N.B. Les campagnes ne sont pas à l'année civile, mais du 1er juillet au 30 juin de l'année suivante.
La combinaison N° d'éleveur/N° de travail est unique : en 2025, on aura 5xxxx, avec xxxx différents de ceux qui avaient été attribués en 2015.
Cette combinaison figure sur la plaque que le mouton porte à l'oreille durant toute sa vie.

À chaque début de campagne, l'éleveur indique à l'administration - suivant ses prévisions d'agnelages - combien de boucles il veut.
II-B. Les données qui caractérisent un mouton▲
La race
Charollais, Bleu du Maine, Île-de-France, Suffolk, Romane, Texel…
La généalogie
Pour autant qu'elle soit connue. (C'est rarement le cas pour un mouton acheté.)
Comment il a été allaité
Naturel par sa mère, par une brebis adoptive, au biberon, complémenté…
Comment il est entré dans le troupeau
Naissance dans l'exploitation, achat, prise en pension.
Comment il en est sorti
Vente, consommation propre, sortie de pension, mort, vol.
II-C. La reproduction▲
Les brebis ont un rythme saisonnier de reproduction dépendant de la variation de la durée du jour au cours de l'année : l'activité sexuelle se manifeste lorsque la durée du jour diminue : du début de l'été à la fin d'automne.Certaines races comme la Romane et l'Île-de-France désaisonnent naturellement (sans hormones !!!), c'est-à-dire qu'elles sont saillies au printemps, pour mettre bas à l'automne et donc proposer des agneaux toute l'année ! Pour stimuler ce désaisonnement, on réalise un flushing avant la lutte (c'est-à-dire qu'on suralimente légèrement les brebis pendant trois semaines) puis « l'effet bélier » fait le reste (l'introduction du bélier dans le troupeau stimule le cycle des brebis).
On constitue une lutte, c'est-à-dire un groupe constitué d'un bélier et de 20 à 40 brebis.
Le bélier porte un harnais avec un bloc marqueur afin que l'arrière-train de la brebis soit marqué d'une couleur lorsqu'elle a été saillie.
La gestation dure de 140 à 150 jours.
On mémorisera les circonstances de mise bas de chaque brebis (seule, aide légère, aide forte, césarienne, embryotomie) afin de ne garder à terme, que les brebis (ou les filles de celles-ci) qui mettent bas facilement.
III. Le modèle de données▲
IV. Le formulaire fMenu▲

C'est lui qui s'affiche à l'ouverture de l'application.
Un clic sur les boutons donne l'accès aux principaux formulaires de l'application :
OOption Compare Database
Option Explicit
Private Sub btInventaire_Click()
DoCmd.OpenForm "fInventaire"
End Sub
Private Sub btLuttes_Click()
DoCmd.OpenForm "fLuttes"
End Sub
Private Sub btOvins_Click()
DoCmd.OpenForm "fOvins"
End Sub
Private Sub BtRepro_Click()
DoCmd.OpenForm "fRepro"
End SubV. Le formulaire fOvins▲
V-A. Présentation▲
Comme dans tout formulaire Access,
Il s'agit d'un formulaire « tout-en-un » : non seulement il permet de gérer la table tOvins (c'est-à-dire ajouter, supprimer ou modifier des enregistrements), mais il offre la possibilité d'opérer des recherches multicritères. En l'occurrence, suivant les filtres mentionnés dans le pied (le N° d'éleveur, le N° de travail, le sexe, la race, le caractère « fictif »(1)).
Selon les filtres choisis, la source du formulaire est modifiée par exemple :
N.B. Si aucun ovin ne correspond à la combinaison de filtres, vient ce message :

La technique de cette méthode est décrite en détail dans ce tutoriel :Formulaire de recherche polyvalent sur la base d'une requête enregistrée.
V-B. La source▲
Même principe que
.
La comparaison se fait par rapport à la valeur du groupe d'option :
- si le groupe d'option a une valeur supérieure à zéro (donc si l'utilisateur a choisi Femelles ou Mâles), la requête ramènera les tSexeFK égaux soit à 1 (si « Femelles » est cochée), soit égaux à 2 (si « Mâles » est cochée) ;
- si le groupe vaut zéro, la requête ramènera les cas où tSexeFK est égal à lui-même, c'est-à-dire tous.
![]() |
||
|
|
C'est comme
et
. Rappelez-vous que FiltreRace est une zone de liste
et que, même si elle affiche une race en clair, ce qu'elle contient effectivement, c'est la colonne liée, en l'occurrence tRacePK.
Si la case est grisée
(valeur Null), tous les enregistrements seront ramenés,
si la case est cochée
(valeur Vrai), seuls les ovins fictifs seront ramenés.
V-C. Quelques fonctionnalités▲
On utilise la mise en forme conditionnelle :
Pour la cause de sortie :
Pour le N° d'éleveur :
V-C-1. La constante NumElevage▲
![]() |
Nous avons défini une constante NumElevage : Sélectionnez Ici fixée à 102030. L'utilisateur modifiera une fois pour toutes pour y mentionner son propre numéro. Cette constante a une portée universelle. C'est-à-dire que l'on peut l'utiliser partout dans l'application (on dit qu'elle est « visible » partout). Cependant, on ne peut pas utiliser des constantes partout dans Access. Par exemple, dans l'expression d'une mise en forme conditionnelle. Qu'à cela ne tienne, on contourne en créant une fonction qui renvoie la valeur de la constante :² Sélectionnez Là où Access proscrit les constantes, il admet généralement les fonctions. C'est le cas ici. |
V-D. Code du formulaire▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
Option Compare Database
Option Explicit
Private Sub Form_Current()
If Me.coFictif = False Then
Me.Section("Détail").BackColor = -2147483633 'gris clair
Else
Me.Section("Détail").BackColor = 33023 'orange
End If
End Sub
Public Sub MaJFiltre()
Me.Requery
If Me.RecordsetClone.RecordCount = 0 Then
MsgBox "Pas d'enregistrement avec cette combinaison"
End If
End Sub
Private Sub BtTout_Click()
Dim ctl As Control
For Each ctl In Me.Controls
If ctl.Name Like "Filtre*" Then
ctl = Null
End If
Next ctl
Me.FiltreSexe = 0
Me.Requery
End Sub
Private Sub FiltreNumEleveur_AfterUpdate()
Call MaJFiltre
End Sub
Private Sub FiltreNumTravail_AfterUpdate()
Call MaJFiltre
End Sub
Private Sub FiltreRace_AfterUpdate()
Call MaJFiltre
End Sub
Private Sub FiltreSexe_AfterUpdate()
Call MaJFiltre
End Sub
Private Sub FiltreFictifs_AfterUpdate()
If Me.FiltreFictifs = 0 Then Me.FiltreFictifs = Null
Call MaJFiltre
End Sub
4-9 : on veut afficher la section détail en orange pour attirer l'attention de l'utilisateur sur le fait qu'il s'agit d'un ovin « fictif » (le mâle d'une brebis achetée pleine, ou le mâle d'une brebis inséminée artificiellement). Access n'offre pas la possibilité de mise en forme conditionnelle pour la section « Détail ». Pour pallier ce manque, on recourt à du code : à chaque lecture d'un enregistrement (événement « Sur activation » On current), on modifie la propriété « Couleur de fond » BackColorde la section.
12-49 : ne concerne que la partie « recherche multicritère » décrite dans Formulaire de recherche polyvalent sur la base d'une requête enregistrée.
VI. Le formulaire tLuttes▲
VI-A. Présentation▲
Il s'agit d'un formulaire père/fils :
- fLuttes, le père avec comme source la table tLuttes ;
- sfLuttesBrebis, le fils avec comme source la table tLutteBrebis.
Si le concept de formulaire « père/fils » ne vous est pas familier, voyez ce tutoriel : Comment classer les données dans des tables liées et construire un formulaire père/fils.
Lorsque fLuttes affiche un enregistrement de tLuttes, sfLuttesBrebis affiche les enregistrements de tLutteBrebis correspondants.
La clé primaire de tLuttes (tLuttesPK) et la clé étrangère de tLutteBrebis (tLuttesFK) assurent la synchronisation entre les deux composantes :

VI-B. La source de fLuttes▲
Tout comme fOvins, ce formulaire est polyvalent : on peut ajouter, modifier ou supprimer les enregistrements de sa source, mais il peut aussi servir de formulaire de recherche multicritère :
Avec ceci de particulier, que les critères portent sur deux tables :
- la source du formulaire fLuttes (tLuttes) ;
- la source du sous-formulaire sfLuttesBrebis (tLutteBrebis).
Si nous suivons la même logique que pour fOvins, nous créons une requête qui se réfère aux filtres :
MAIS
voici ce que ramènerait une telle requête :

c'est-à-dire, pour une lutte, autant de lignes qu'il y a de brebis qui participent à la lutte.
La source de notre formulaire ne doit contenir qu'une seule ligne par lutte !
Qu'à cela ne tienne, transformons la requête de sélection en une requête de regroupement :
qui, cette fois, ramène une seule ligne par lutte :

OUI MAIS
une telle requête n'est plus modifiable : vous n'avez plus la possibilité de modifier le contenu d'une colonne, ni supprimer une ligne et encore moins d'en ajouter une !
Donc, ça ne va pas ! Il va falloir ruser !
En fait, il faut dire à Access : « Dans la source, ramenez tous les enregistrements de tLuttes dont le bélier est mentionné dans la requête rfLuttes ! »
Voici les mots qu'il faut pour le dire :
Dans la foulée, on trie dans l'ordre décroissant de DateDebut. Ainsi, si on fait une recherche selon un bélier ou une brebis, on affichera en premier la dernière lutte à laquelle ils ont participé.
Ce n'est pas beau ça ?
VI-C. Fonctionnalité particulière▲
VI-C-1. Un carré vert indique qu'une mise bas est déclarée▲
Nous verrons dans un chapitre suivant, qu'une mise bas se concrétise par un enregistrement dans la table tLutteBrebis.
Pour faire apparaître un carré vert, nous avons ajouté une zone de texte qui enregistre le nombre (0 ou 1) de mise bas déclarée pour cette femelle dans cette lutte :
et une mise en forme conditionnelle fait le reste :
![]() |
Remarquez que le 0 ou le 1 n'apparaîtront pas : ils sont de la même couleur que le fond de la zone de texte. |
VI-D. Le code de fLuttes▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
Option Compare Database
Option Explicit
Private Sub Form_Current()
Call AmenagerTailleSF(Me.Name, "CTNRsfLuttesBrebis")
Me.CTNRsfLuttesBrebis.Form!cboBrebis.RowSource = Me.CTNRsfLuttesBrebis.Form!cboBrebis.RowSource
End Sub
Private Sub Form_Close()
If CurrentProject.AllForms("fMisesBas").IsLoaded Then DoCmd.Close acForm, "fMisesBas"
End Sub
Public Sub MaJFiltre()
Me.Requery
If Me.RecordsetClone.RecordCount = 0 Then
MsgBox "Pas d'enregistrement avec cette combinaison"
End If
End Sub
Private Sub BtTout_Click()
Dim ctl As Control
For Each ctl In Me.Controls
If ctl.Name Like "Filtre*" Then
ctl = Null
End If
Next ctl
Me.Requery
End Sub
Private Sub FiltreBelier_AfterUpdate()
Call MaJFiltre
End Sub
Private Sub FiltreBrebis_AfterUpdate()
Call MaJFiltre
End Sub
Private Sub txtDateFin_AfterUpdate()
Me.Refresh
End Sub
Commentaires du code
3-7 : concerne l'ajustement de la hauteur du sous-formulaire (voir § suivant).
9-11 : nous présenterons tout à l'heure le formulaire fMisesBas. Il fonctionne de pair avec fLuttes et cela n'a pas de sens de laisser fMisesBas ouvert si fLuttes est fermé.
13-36 : rien de particulier. Ne concerne que la partie « recherche multicritère » décrite dans Formulaire de recherche polyvalent sur la base d'une requête enregistrée.
VI-E. Le sous-formulaire fils sfLuttesBrebis▲
VI-E-1. La hauteur du sous-formulaire s'adapte au nombre d'enregistrements qu'il contient▲
de manière à ce que la section « Pied » qui renseigne le nombre de femelles de la lutte soit « collée » à la fin de la section Détail.
Pour une description détaillée du processus, voir Comment ajuster la taille d'un sous-formulaire en fonction du nombre de ses enregistrements.
VI-E-2. Zone de liste pour le choix des brebis▲
Pour être candidate dans une lutte, il faut :
qu'il s'agisse d'une femelle ;
qui est présente dans le troupeau au début de la mise en lutte. En d'autres mots, elle doit avoir une date d'entrée antérieure à celle du début de lutte et sa date de sortie doit être soit nulle, soit postérieure à celle du début de lutte ;
elle doit avoir au moins 300 jours d'âge à la date de début de lutte ;
si elle a déjà mis bas, il faut que la dernière fois remonte à au moins septante-cinq jours (ma belgitude ne résiste pas au plaisir d'écrire 75 en lettres !
) ;
elle ne doit pas déjà faire partie d'une autre lutte encore en cours.
La requête qui ramène les ovins qui satisfont aux cinq critères :
VI-E-3. Le code de sfLuttesBrebis▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
Option Compare Database
Option Explicit
Private Sub Form_Current()
If CurrentProject.AllForms("fMisesBas").IsLoaded Then DoCmd.Close acForm, "fMisesBas"
Me.cboBrebis.RowSource = Me.cboBrebis.RowSource
End Sub
Private Sub Form_AfterDelConfirm(Status As Integer)
Call AmenagerTailleSF(Me.Parent.Name, "ctnr" & Me.Name)
End Sub
Private Sub Form_AfterInsert()
Call AmenagerTailleSF(Me.Parent.Name, "ctnr" & Me.Name)
End Sub
Private Sub btMB_Click()
If Me.cboBrebis = 0 Then Exit Sub 'clic sur enregistrement encore vierge
DoCmd.OpenForm "fMisesBas", , , , , acHidden
PositionForm Forms("fMisesBas"), Me.btMB
End Sub
Private Sub cboBrebis_AfterUpdate()
On Error GoTo GestionErreurs
Me.Refresh
GestionErreurs:
Select Case Err.Number
Case 0
Exit Sub
Case 3022 'doublon
MsgBox "Cette brebis N° " & Me.cboBrebis.Text & " fait déjà partie de la lutte."
Me.Undo
End Select
End Sub
Commentaires du code
4-7 : à chaque changement d'enregistrement :
- (5) on ferme le formulaire fMiseBas, car s'il est encore ouvert, il se rapporte à l'enregistrement qui était actif juste avant ;
- (6) on réactualise la propriété « Contenu » de la zone de liste.
9-15 : ces instructions concernent l'adaptation de la hauteur du sous-formulaire.
17-21 : le clic sur le bouton « Mise bas » sur une ligne d'enregistrement :

- (18) sera sans effet, s'il s'agit du bouton situé sur la ligne qui propose d'ajouter un enregistrement ;
- (19-20) va provoquer l'ouverture du formulaire fMisesBas juste en dessous du bouton qui a été cliqué (ou à proximité selon la place disponible à l'écran).
Cette technique a été présentée par Arkham46 dans cette contribution.
![]() |
Il n'y a pas besoin de comprendre tout ce qu'il y a sous le capot pour savoir conduire une voiture. Pour utiliser la proposition d'Arkham46 : - vous copiez dans un module le code donné à la fin de cet article ; - pour commander l'ouverture du formulaire, ces deux lignes de code : Sélectionnez - le formulaire à ouvrir doit être une fenêtre indépendante : |
23-33 : on vérifie ici que la brebis qui vient d'être désignée ne fait pas déjà partie de la lutte que nous sommes en train de déclarer.
On provoque la sauvegarde de l'enregistrement… et Access fait le reste, car dans la définition de la table tLutteBrebis, nous avons placé un index unique sur la combinaison lutte/brebis :

Dès lors, en cas de tentative de doublon, Access lève une erreur 3022 que nous récupérons pour afficher ce message :
VII. Le formulaire fMisesBas▲
VII-A. Présentation▲
Lorsqu'une brebis met bas, il faut enregistrer la (les) naissance(s), voici la suite des opérations :
Un clic dans le menu.
Dans le formulaire fLuttes, filtrer pour afficher la dernière lutte de la brebis.
Un clic sur le bouton « MiseBas ».
Le formulaire fMisesBas s'affiche :
L'utilisateur complète (ici des jumeaux pour la Saint-Nicolas !) et comptabilise :
Les deux agneaux ont été automatiquement enregistrés dans la table tOvins.
Remarquez que le bouton « À comptabiliser » a été remplacé par « Déjà enregistré » et les données ne sont plus modifiables (ni ajout, ni suppression) pour cette mise bas.
VII-B. Particularités techniques▲
Les contrôles en rouge sont utiles au fonctionnement, mais sans intérêt pour l'utilisateur. Ils ont leur propriété Visible positionnée à Non.
Tout comme fOvins et fLuttes, fMisesBas est du type père/fils.
Le contrôle TXTtLuttesBrebisFK prend comme valeur par défaut le contenu de TXTtLuttesBrebisPK de la brebis courante dans fLuttes :

Donc, à la création de l'enregistrement - lors de la première ouverture du formulaire fMiseBas pour cette brebis dans cette lutte -, le champ tLutteBrebisFK (la clé étrangère) sera automatiquement mis à jour.
Seulement un des deux boutons aura sa propriété Visible positionnée à Oui, selon le stade de comptabilisation de la mise bas.
VII-B-1. Utilisation (acrobatique) de la mise en forme conditionnelle ▲
Pour le N° de travail
situation normale

cas de la ligne qui propose d'ajouter un enregistrement

si l'agneau est viable, il doit avoir un N°, par contre il ne peut avoir de N° s'il n'est pas viable

si l'agneau n'est pas viable et pas de N°, le champ s'affiche en gris

Pour l'allaitement
situation normale

agneau non viable

cas de la ligne de l'enregistrement à ajouter

VII-C. La source de fMisesBas▲
En d'autres mots, les enregistrements de tMisesBas qui concernent la brebis dont il est actuellement question dans le formulaire fLuttes.
VII-D. Le code du formulaire fMisesBas▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
Option Compare Database
Option Explicit
Private Sub Form_Current()
With Me
If Me.txtEnregistreeMB = True Then
'Afficher le bouton qui convient
.btComptabiliser.Visible = False
.btEnregistree.Visible = True
'Interdire les modif
.AllowEdits = False
.AllowDeletions = False
'Interdire les modif dans le sfLuttesBrebis
.CTNRsfMisesBasAgneaux.Form.AllowAdditions = False
.CTNRsfMisesBasAgneaux.Form.AllowDeletions = False
.CTNRsfMisesBasAgneaux.Form.AllowEdits = False
Else
'Afficher le bouton qui convient
.btComptabiliser.Visible = True
.btEnregistree.Visible = False
'Autoriser les modif
.AllowEdits = True
.RecordSelectors = True
'Autoriser les modif dans le sfMisesBasAgneaux
.CTNRsfMisesBasAgneaux.Form.AllowAdditions = True
.CTNRsfMisesBasAgneaux.Form.AllowDeletions = True
.CTNRsfMisesBasAgneaux.Form.AllowEdits = True
End If
End With
End Sub
Private Sub btComptabiliser_Click()
Dim iReponse As Integer
'Demande de confirmation
iReponse = MsgBox("Confirmez que vous voulez enregistrer cette mise bas", vbYesNo + vbQuestion + vbDefaultButton2)
If iReponse = vbNo Then Exit Sub
With Me
'MàJ de EnregistreeMB
.txtEnregistreeMB = True
'Rendre le formulaire non modifiable
.AllowEdits = False
.AllowDeletions = False
'Rendre le sous-formulaire non modifiable
.CTNRsfMisesBasAgneaux.Form.AllowAdditions = False
.CTNRsfMisesBasAgneaux.Form.AllowDeletions = False
.CTNRsfMisesBasAgneaux.Form.AllowEdits = False
'Changer de bouton
.txtEnregistreeMB = True
.txtDateMB.SetFocus
.btComptabiliser.Visible = False
.btEnregistree.Visible = True
'Enregistrer tMisesBas
.Refresh
End With
'Enregistrer les naissances dans tOvins
DoCmd.SetWarnings False
DoCmd.OpenQuery "rComptaMiseBas"
DoCmd.SetWarnings True
End Sub
Commentaires du code
Pas de difficulté : les explications incluses dans les instructions devraient suffire.
Ligne 57 : la requête rComptaMiseBas est décrite au paragraphe suivant.
VII-E. rComptaMiseBas, la requête qui intègre les nouveau-nés dans tOvins▲
Il s'agit d'une requête qui va ajouter un enregistrement dans tOvins pour chaque nouveau-né.
Cet enchevêtrement de tables permet d'accéder à toutes les données nécessaires pour alimenter les colonnes de tOvins. Remarquez que tOvins est assignée deux fois (une première pour se connecter aux données de la brebis et une seconde fois pour se connecter à celles du bélier).
Les enregistrements ramenés par cette requête seront limités à ceux dont le tLutteBrebisPK est celui de la brebis dont on déclare la mise bas…
… pour autant que l'agneau soit viable.
Deux colonnes méritent un mot d'explication.
- NumEleveur

Ici par exemple, pour compléter le N° d'éleveur de l'agneau, on indique le N° de l'élevage.
Pour rappel, on utilise une fonction, car une constante n'est pas admise dans une requête.
L'explication a été donnée plus haut ici.
- tRaceFK
En d'autres mots : si les deux parents sont de même race, ce sera la race du nouveau-né, sinon ce dernier sera de race « Indéterminée ».
Des « zinnekes » comme on dit à Bruxelles
.
VIII. Annexe : Proposition de Arkham46 pour positionner le formulaire pForm par rapport au contrôle pControl▲
Voici le code de Arkham46 dont il est question au paragraphe Le code du sous-formulaire fils sfLuttesBrebis.
Option Compare Database
Option Explicit
'***************************************************************************************
'* API *
'***************************************************************************************
Private Declare Function SetWindowPos Lib "User32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Declare Function GetWindowRect Lib "User32" (ByVal hwnd As Long, lpRect As RectAPI) As Long
Private Declare Function GetDC Lib "User32" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "User32" (ByVal hwnd As Long, ByVal Hdc As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal Hdc As Long, ByVal nIndex As Long) As Long
Private Declare Function ClientToScreen Lib "User32" (ByVal hwnd As Long, lpPoint As PointAPI) As Long
Private Declare Function SystemParametersInfo Lib "User32" _
Alias "SystemParametersInfoA" (ByVal uAction As Long, _
ByVal uParam As Long, ByRef lpvParam As RectAPI, _
ByVal fuWinIni As Long) As Long
'***************************************************************************************
'* Types *
'***************************************************************************************
' Type Point pour API
Public Type PointAPI
x As Long
y As Long
End Type
Public Type RectAPI
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
'***************************************************************************************
'* Constantes *
'***************************************************************************************
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOMOVE As Long = &H2
Private Const SWP_NOZORDER = &H4
Private Const SWP_SHOWWINDOW = &H40
Private Const LOGPIXELSY = 90
Private Const LOGPIXELSX = 88
Private Const SPI_GETWORKAREA = 48
'---------------------------------------------------------------------------------------
' Convertit les Twips en Pixels sur l'axe horizontal
'---------------------------------------------------------------------------------------
' pTwipsX : Valeur à convertir en Twips
' Renvoie la valeur convertie en Pixels
'---------------------------------------------------------------------------------------
Public Function TwipsToPixelX(pTwipsX As Long) As Long
Static Mult As Long
Dim Hdc As Long
If Mult = 0 Then
Hdc = GetDC(0)
Mult = 1440 / GetDeviceCaps(Hdc, LOGPIXELSX)
ReleaseDC 0, Hdc
End If
TwipsToPixelX = CLng(pTwipsX / Mult)
End Function
'---------------------------------------------------------------------------------------
' Convertit les Twips en Pixels sur l'axe vertical
'---------------------------------------------------------------------------------------
' pTwipsY : Valeur à convertir en Twips
' Renvoie la valeur convertie en Pixels
'---------------------------------------------------------------------------------------
Public Function TwipsToPixelY(pTwipsY As Long) As Long
Static Mult As Long
Dim Hdc As Long
If Mult = 0 Then
Hdc = GetDC(0)
Mult = 1440 / GetDeviceCaps(Hdc, LOGPIXELSY)
ReleaseDC 0, Hdc
End If
TwipsToPixelY = CLng(pTwipsY / Mult)
End Function
'---------------------------------------------------------------------------------------
' Positionne le formulaire pForm par rapport au contrôle pControl
'---------------------------------------------------------------------------------------
Public Sub PositionForm(pForm As Access.Form, pControl As Access.Control)
Dim lParentForm As Access.Form
Dim lPt As PointAPI
Dim lRect As RectAPI
Dim lScreenRect As RectAPI
Dim lScrWitdh As Single, lScrHeight As Single
On Error GoTo Gestion_Erreurs
' Vérifie que le formulaire est en fenêtre indépendante
If Not pForm.PopUp Then
MsgBox "Le formulaire à positionner doit être en fenêtre indépendante" & _
vbCrLf & "(onglet Autre dans les propriétés du formulaire)", vbInformation
SetWindowPos pForm.hwnd, 0, 0, 0, 0, 0, SWP_NOZORDER Or SWP_NOMOVE Or SWP_NOSIZE Or SWP_SHOWWINDOW
Exit Sub
End If
' Formulaire parent
Set lParentForm = pControl.Parent
' Remonte jusqu'au formulaire si contrôle dans onglets
If TypeOf lParentForm Is Page Then
Do
Err.Clear
Set lParentForm = lParentForm.Parent
If Err.Number <> 0 Then Err.Clear: Exit Do
Loop
End If
' Lit la taille du formulaire à positionner
Call GetWindowRect(pForm.hwnd, lRect)
lRect.Right = lRect.Right - lRect.Left + 1
lRect.Bottom = lRect.Bottom - lRect.Top + 1
' Lit la taille de l'écran
SystemParametersInfo SPI_GETWORKAREA, 0, lScreenRect, 0
lScrWitdh = lScreenRect.Right - lScreenRect.Left + 1
lScrHeight = lScreenRect.Bottom - lScreenRect.Top + 1
' Position du contrôle de positionnement
lPt.x = TwipsToPixelX(pControl.Left + lParentForm.CurrentSectionLeft)
lPt.y = TwipsToPixelY(pControl.Top + pControl.Height + lParentForm.CurrentSectionTop)
ClientToScreen lParentForm.hwnd, lPt
Set lParentForm = Nothing
lRect.Left = lPt.x
lRect.Top = lPt.y
' Doit tenir dans l'écran
' Si déborde à droite => décale le formulaire pour qu'il s'affiche entièrement
If lRect.Left + lRect.Right > lScrWitdh Then
lRect.Left = lScrWitdh - lRect.Right
End If
' Si déborde en bas => affiche le formulaire au-dessus du contrôle
If lRect.Top + lRect.Bottom > lScrHeight Then
lRect.Top = lRect.Top - TwipsToPixelY(pControl.Height) - lRect.Bottom
End If
' Repositionne le formulaire
Call SetWindowPos(pForm.hwnd, 0, lRect.Left, lRect.Top, lRect.Right, lRect.Bottom, SWP_NOZORDER Or SWP_NOSIZE Or SWP_SHOWWINDOW)
On Error GoTo 0
Exit Sub
Gestion_Erreurs:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") dans la procédure PositionForm"
End SubIX. Téléchargement▲
La base de données au format Access 2000 se trouve ici.
X. Remerciements▲
Merci à foster53 qui m'a expliqué une partie de l'histoire qui précède l'achat des côtes d'agneau au supermarché.
Merci à Jean-Damien Gayot qui a amélioré la qualité du code.
Merci à Malick Seck (milkoseck) pour la correction orthographique.
1re partie : la collecte des données













































