I. Avant-propos▲
Le code proposé dans cet article contient des API, c'est-à-dire des fonctions externes déclarées avec l'instruction Declare Function ou Declare Sub.
Depuis Office 2010, Microsoft propose une version 64 bits.
Si vous exécutez un fichier contenant ce type de déclaration avec Office 2010 64 bits, ce code ne compile plus et vous aurez ce message
Une réécriture de ces instructions est nécessaire.
Vous trouverez la documentation nécessaire dans ce tutoriel : http://arkham46.developpez.com/articles/office/vba64bits/
------------
Considérez ceci comme un prétexte pour manipuler quelques propriétés des formulaires Access et apprendre à écrire quelques lignes de code.
Si vous êtes pressé et que votre intérêt porte sur la solution, voyez plutôt ces deux contributions :
Argyronet a traité le sujet dans son tutoriel Comment positionner un formulaire ouvert depuis un autre, à droite de l'écran ;
Arkham46 dans cette contribution, donne tout le code pour positionner un formulaire sous un contrôle d'un autre formulaire.
La solution d'Arkham46 aboutit pratiquement au même résultat que nous et en moins de cent lignes, hors commentaires ! Et si vous ne comprenez pas tout ce que font les API qu'il utilise, ce n'est pas trop grave. Personnellement, je conduis une voiture depuis un demi-siècle et je n'ai toujours pas compris comment fonctionne un carburateur !(1)
II. Prérequis▲
Ce texte s'adresse à des utilisateurs Access débutants qui veulent s'initier à la programmation et qui ont lu le premier tutoriel.
III. L'objectif▲
Dans un formulaire, un double-clic sur un contrôle provoque l'ouverture d'un autre formulaire qui viendra s'afficher juste en dessous du contrôle cliqué...
… et si l'écran est trop petit, le second formulaire se déplace pour rester visible.
IV. Plantons le décor▲
N'importe quel formulaire ferait l'affaire. Mais pour passer en revue toutes les possibilités, nous avons choisi un formulaire en continu, avec trois sections : En-tête de formulaire, Détail et Pied de formulaire.
Il nous fallait donc baser notre formulaire sur une table. Tant qu'à faire, autant prendre une liste de produits sympathiques, à consommer toutefois avec modération.
En voici un extrait :
Voici le formulaire fPrincipal qui nous servira d'exemple :
Tous les champs de la table sont repris, leur nom est zdt suivi du nom du champ. Exemple, zdtDENOMINATION pour le champ DENOMINATION (« zdt » pour zone de texte).
Un gadget : zdtMILLESIME est mis en évidence en vert dans la ligne de l'enregistrement actif. (Voir plus bas comment on y arrive.)
Ma première idée d'exemple était : si on double-clique sur le millésime, on lance une recherche Google sur les sites spécialisés et on affiche les appréciations des œnologues réputés dans un formulaire. C'est sans doute possible mais ça nous aurait conduits trop loin dans les explications. (Voir à ce sujet Arkham46 VBA et développement Web.) Je vous invite à revoir le sujet... quand le débutant, à qui je m'adresse, aura acquis un peu plus de bouteille(s).
Ici, on fera plus simple, car en fait, n'importe quel formulaire indépendant fait l'affaire pour nos explications.
Dans l'exemple, il s'appelle fDetail et ne contient rien d'autre qu'une image, sans aucun rapport. Quoique !
Notre objectif : un double-clic sur le contrôle zdtMILLESIME de l'enregistrement en cours provoque l'ouverture de fDetail qui viendra se superposer à fPrincipal juste en dessous et aligné à gauche du contrôle double-cliqué. Ou ailleurs si manque de place.
V. Allons-y pas à pas, d'abord avec des mots▲
Si le formulaire fDetail est une fenêtre indépendante, pour afficher à l'endroit voulu, il « suffit » de fournir les bons paramètres à une instruction Docmd.MoveSize droite, bas, largeur, hauteur.
Dès lors, le problème se ramène à localiser le point sous la loupe :
en s'exprimant en twips, par rapport au coin supérieur gauche de notre écran.
V-A. Le paramètre « droite »▲
Relativement au coin supérieur gauche de l'écran, il se décompose en :
- la distance horizontale du coin supérieur gauche du formulaire fPrincipal ;
- l'épaisseur de la bordure gauche du formulaire fPrincipal ;
- la largeur de la coulisse du sélecteur d'enregistrement ;
- la distance entre cette coulisse et le bord gauche du contrôle ciblé.
V-B. Le paramètre « bas »▲
Relativement au coin supérieur gauche de l'écran, il se décompose en :
- la distance verticale du coin supérieur gauche du formulaire fPrincipal ;
- la hauteur de la bordure supérieure du formulaire fPrincipal ;
- à l'intérieur de fPrincipal, la position de la section détail actuellement active ;
- dans cette section, la position supérieure de contrôle ciblé ;
- la hauteur de ce contrôle.
V-C. Paramètres « Largeur » et « Hauteur »▲
Cela dépend des dimensions du formulaire fDetail. Fixons les idées : 4245 et 3495 twips, respectivement.
V-D. Reste à vérifier qu'il y aura assez de place à l'écran▲
En d'autres termes que les distances entre notre point visé et les bords droit et inférieur de l'écran permettent de loger le formulaire fDetail.
Si ça déborde à droite, il faudra corriger le paramètre droite actuel :
- ajouter la largeur du contrôle zdtMILLESIME ;
- soustraire la largeur de fDetail.
Donc, aligner le côté droit de fDetail sur le côté droit de zdtMILLESIME.
Si ça déborde en dessous, il faudra corriger le paramètre bas actuel :
- soustraire la hauteur du contrôle zdtMILLESIME ;
- soustraire la hauteur de fDetail.
Donc, placer le bord inférieur de fDetail sur le bord supérieur de zdtMILLESIME.
VI. Récapitulons les données nécessaires et voyons comment les obtenir▲
- Les expressions en vert n'ont pas de secret pour ceux qui ont lu le précédent tutoriel sur ce sujet.
- Pour les expressions en orange, le réflexe offre une explication bien documentée. Je ne vais pas la reproduire ici. Tapez <F1> pour ouvrir l'aide Access et ensuite le mot-clé dans la zone de recherche.
Vous obtiendrez quelque chose comme ceci :
- Pour les parties en rouge, on n'a pas de fonction ni de propriété. On va devoir ruser !
VII. Trouver la dimension des bordures▲
Monsieur de La Palice aurait dit un quart d'heure avant de mourir (donc quand il vivait encore) :
l'espace occupé par les bordures, c'est la différence entre
les mesures du formulaire avec bordures
et
ses mesures sans bordure.
Ce n'est pas aussi bête qu'il y paraît !
Une bordure latérale = la moitié de (la - ls ).
La bordure inférieure = la bordure latérale.
La bordure supérieure = (ha - hs) - bordure inférieure.
VII-A. Suivons la piste▲
Nous allons nous organiser pour ne faire les calculs qu'une seule fois par session (le fait d'ouvrir la base de données).
Nous allons mémoriser la dimension des bordures dans des variables globales.
Pour relever les mesures, nous allons créer une copie du formulaire fPrincipal et l'ouvrir successivement avec et sans bordures. Nous pourrons ainsi réaliser notre objectif.
Voici l'algorithme que nous allons appliquer :
VII-B. Traduisons en code▲
Nous affecterons les dimensions des bordures dans une variable globale d'un type que nous définissons dans le module mFonctions. Nous aurons aussi besoin de mémoriser les coefficients de conversion pixels => twips:
Type
Bordure
Laterale As
Long
Inferieure As
Long
Superieure As
Long
End
Type
Type
Pixel2Twip
Horizontal As
Long
Vertical As
Long
End
Type
Global
gBordure As
Bordure
Global
gBordureDispo As
Boolean
Global
gPixel2Twip As
Pixel2Twip
Par défaut, gBordure.Laterale, gBordure.Inferieure et gBordure.Superieure valent zéro et gBordureDispo vaut False (Faux).
Dans le module de code du formulaire fPrincipal nous créons une procédure.
Enfoncez simultanément <Alt> et <F11> (on note <Alt-Key-F11>) pour ouvrir l'éditeur de code.
<Ctrl-Key-R> pour afficher l'explorateur de projet.
Dans ce dernier, vous cliquez sur Form_fPrincipal. Votre écran devrait afficher :
Cliquez sur le dérouleur de l'icône , il vient ceci . Cliquez sur Procédure...
Nous baptisons notre procédure : « CalculBordures ».
Nous choisissons de donner une portée « privée » à notre procédure. C'est-à-dire qu'elle sera disponible uniquement dans le périmètre du formulaire.
Cliquez sur OK, votre écran affiche :
Le reste du code sera inséré là où votre curseur clignote. Entre la déclaration de la procédure et son indicateur de fin.
Dans ce code, la propriété « Style Bordure » s'écrit « . BorderStyle ».
Pour trouver la correspondance entre les noms en français et la syntaxe de la propriété, procédez comme ceci :
- affichez les propriétés du formulaire (ou de l'état) ;
- cliquez sur la propriété choisie, elle s'affiche en surbrillance ;
- enfoncez <F1>, l'aide s'affiche et vous donne le nom anglais.
Si une liste déroulante offre un choix de valeurs, cette dernière s'exprime dans le code au moyen d'un nombre. Ce nombre est en fait le rang de la ligne dans la liste en commençant par zéro.
Par exemple :
à « Aucun » correspond « 0 » ;
à « Dimensionnable » correspond « 2 ».
La syntaxe serait :
Forms!LeFormulaire.BorderStyle = 0 pour exprimer qu'il ne faut pas de bordure.
On définit des variables pour mémoriser les dimensions des fenêtres (rectForm) et les résultats intermédiaires (lLargeAvec, lHautAvec, lLargeSans et lHautSans, le « l » rappelle qu'elles sont de type entier long) :
Dim
rectForm As
RECT
Dim
lLargeAvec As
Long
Dim
lHautAvec As
Long
Dim
lLargeSans As
Long
Dim
lHautSans As
Long
Prenez dès le début de bonnes habitudes de nommage. Si vous aimez la rigueur, voyez les Conventions typographiques de Argyronet.
On affecte la valeur True à la variable gBordureDispo, pour signifier que les données relatives aux bordures seront disponibles. Cela nous évitera de recommencer tous les calculs dès que les dimensions seront connues.
S'il s'avère que le formulaire n'a pas de bordure, on laisse telles quelles les dimensions initiales (zéro) :
gBordureDispo =
True
If
Me.BorderStyle
=
0
Then
Exit
Sub
Dans le cas contraire (une valeur de BorderStyle ≠0), on crée une copie du formulaire que l'on nomme « |bordure| ». Ce nom est arbitraire, la seule contrainte, c'est qu'aucun formulaire ne se nomme déjà ainsi dans la base de données. C'est pour cette raison que je choisis de décorer son nom avec des « pipes » (« | », <Alt> et <124> sur le pavé numérique), ce caractère étant rarement utilisé, un formulaire homonyme est peu probable.
On ouvre ce formulaire en mode caché. Bien qu'invisible à l'écran, la fenêtre existe et nous pouvons la mesurer au moyen de notre fonction PositionFormPx que nous connaissons depuis le tutoriel précédent :
DoCmd.CopyObject
, "|Bordures|"
, acForm, CurrentProject.AllForms
(
Me.Name
).Name
DoCmd.OpenForm
"|Bordures|"
, , , , , acHidden
lLargeAvec =
PositionFormPx
(
"|Bordures|"
).Largeur
lHautAvec =
PositionFormPx
(
"|Bordures|"
).Hauteur
Nous refermons.
Nous allons maintenant modifier la copie en enlevant les bordures.
Nous l'ouvrons en mode création (caché), pour affecter la valeur 0 à sa propriété BorderStyle. Et nous passons en mode formulaire pour mesurer sans bordures :
DoCmd.Close
acForm, "|Bordures|"
DoCmd.OpenForm
"|Bordures|"
, acDesign, , , , acHidden
Forms![|Bordures|].BorderStyle
=
0
DoCmd.OpenForm
"|Bordures|"
, , , , , acHidden
lLargeSans =
PositionFormPx
(
"|Bordures|"
).Largeur
lHautSans =
PositionFormPx
(
"|Bordures|"
).Hauteur
À ce stade, nous disposons des éléments pour calculer la dimension de chacune des bordures :
gBordure.Laterale
=
(
lLargeAvec -
lLargeSans) /
2
gBordure.Inferieure
=
gBordure.Laterale
gBordure.Superieure
=
lHautAvec -
lHautSans -
gBordure.Inferieure
Nous n'avons plus besoin de notre copie, on la ferme (sans sauver) et on la supprime :
DoCmd.Close
acForm, "|Bordures|"
, acSaveNo
DoCmd.DeleteObject
acForm, "|Bordures|"
On est presque au bout : nous avons la dimension des bordures en pixels, il reste à les transformer en twips.
Il suffit de multiplier les données par leur nombre de twips par pixel.
Souvenez-vous, il s'agit du contenu des contrôles ZdtNbreTwipspPixelHor et ZdtNbreTwipspPixelVer de notre formulaire fOuSuisJe.
Nous allons ouvrir, en mode caché, fOusuisJe.
On y puise les coefficients de conversion, on en profite pour les mémoriser dans une variable globale (nous en aurons encore besoin plus tard) et nous refermerons le formulaire :
DoCmd.OpenForm
"fOuSuisJe"
, , , , , acHidden
gPixel2Twip.Horizontal
=
Forms!fOuSuisJe.ZdtNbreTwipspPixelHor
gPixel2Twip.Vertical
=
Forms!fOuSuisJe.ZdtNbreTwipspPixelVer
DoCmd.Close
acForm, "fOuSuisJe"
gBordure.Laterale
=
gBordure.Laterale
*
gPixel2Twip.Horizontal
gBordure.Superieure
=
gBordure.Superieure
*
gPixel2Twip.Vertical
gBordure.Inferieure
=
gBordure.Inferieure
*
gPixel2Twip.Vertical
Et voilà, nous avons un « truc » pour les marges en twips.
VIII. Les dimensions de l'écran▲
VIII-A. Une API : SystemParametersInfo▲
Si, pour vous, l'important est que « ça » fonctionne, même si on ne comprend pas, ce serait sans doute raisonnable de faire appel à une API !
Voilà pourquoi nous donnons ici les ingrédients de la recette, sans expliquer pourquoi c'est bon.
Un peu plus loin dans ce tutoriel, nous expliquerons un moyen plus « acrobatique », avec quelques contorsions qui nous permettront de progresser dans la maîtrise du code.
Vous collez ceci dans un module, par exemple dans le module mAPI.
Public
Declare
Function
SystemParametersInfo Lib
"User32"
_
Alias "SystemParametersInfoA"
(
ByVal
uAction As
Long
, _
ByVal
uParam As
Long
, ByRef
lpvParam As
RECT, _
ByVal
fuWinIni As
Long
) As
Long
Public
Const
SPI_GETWORKAREA =
48
Ensuite, cette fonction par exemple dans mFonctions.
Public
Function
EcranPx
(
) As
Position
Dim
lScreenRect As
RECT
SystemParametersInfo SPI_GETWORKAREA, 0
, lScreenRect, 0
EcranPx.Largeur
=
lScreenRect.Right
-
lScreenRect.Left
EcranPx.Hauteur
=
lScreenRect.Bottom
-
lScreenRect.Top
End
Function
Et pour constater que ça fonctionne, ouvrez la fenêtre d'exécution <Ctrl-key-G>, et saisissez :
? EcranPx().Largeur <Enter> => la largeur de l'écran s'affiche en pixels.
? EcranPx().Hauteur <Enter> => la hauteur de l'écran s'affiche en pixels. Il s'agit de la hauteur disponible. C'est-à-dire la distance entre le bord supérieur de l'écran et la barre des tâches si vous avez choisi de l'afficher en permanence.
VIII-B. Un peu de gymnastique pour le même résultat▲
On va se servir de la théorie développée dans le tutoriel précédent.
Avec notre formulaire fOusuisJe et une dose d'imagination, nous allons mesurer notre écran.
Pour comprendre la démarche, nous allons un peu torturer une copie de notre formulaire fOuSuisJe.
Suivez pas à pas :
- prenez une copie : clic sur fOuSuisJe dans la fenêtre des objets => surbrillance => <Ctrl-key-C> suivi de <Ctrl-key-V> (équivaut à Copier/Coller) => Access vous invite à donner un nom =>« Martyr » (par exemple !) ;
- modifiez la propriété Style Bordure : « Aucune » ;
- modifiez le code des événements : <Alt-key-F11> pour ouvrir l'éditeur, dans l'explorateur de projets cliquez sur Form_Martyr et modifiez le code pour arriver à ceci :
La flèche bleue indique une instruction ajoutée : le formulaire occupera la totalité de la surface libre, en l'occurrence tout l'écran puisque notre formulaire est une fenêtre indépendante.
Par contre, les flèches vertes montrent des instructions qui ont été « commentées » : elles sont maintenant neutralisées (Access les confondra avec du commentaire). Ceci est une astuce pour garder une trace de la situation initiale quand on met du code au point.
Ces modifications sont en fait : l'ouverture du formulaire à dimension maximale et incorporation dans l'événement « Sur ouverture », du code initialement prévu dans la minuterie (timer) qui a d'ailleurs disparu.
- Sauvegardez « Martyr » et ouvrez-le en mode formulaire.
Le contrôle zdtLargeurTwi affiche la largeur de l'écran et zdtHauteurTwi affiche la hauteur totale (ce qu'on veut, c'est la hauteur disponible).
Cela dépend de la résolution de votre écran, par exemple avec une résolution de 1280 x 1024, vous obtiendrez : 19 200 (twips) de largeur et 15 360 (twips) de hauteur.
Puisque vous avez créé une copie de l'objet fOuSuisJe, lorsque vous fermerez la session, vous aurez ce message :
Nous avons déjà la moitié de ce qu'on cherche : la largeur. Continuons nos basses œuvres sur Martyr :
- ouvrons-le à nouveau en mode création, et cette fois, remplaçons DoCmd.Maximize par DoCmd.Minimize et ouvrons en mode formulaire. Le bas de l'écran affiche ceci :
- en fait la hauteur que nous recherchons, c'est la distance entre le bord supérieur de l'écran et le bord supérieur de Martyr (zdtBasTwi) + la hauteur de Martyr (zdtHauteurTwi). Dommage que l'affichage est tel que ces données ne sont pas visibles ;
- qu'à cela ne tienne, on peut quand même les voir !
Ouvrons la fenêtre d'exécution et demandons d'afficher la somme de ces deux valeurs :
? forms!Martyr.zdtBasTwi + forms!Martyr.zdtHauteurTwi <Enter>.
Les nombres affichés dépendent de la résolution de votre écran. Par exemple avec une résolution de 1280 x 1024, vous obtiendrez : 14 910 (twips).
Et voilà le travail !
À titre d'exercice, comparez ces valeurs à celles obtenues par la voie rapide décrite au paragraphe précédent (exprimés en pixels). Les résultats sont identiques. Cependant, un peu comme de la soupe en sachet comparée à celle préparée avec les légumes du jardin, la seconde semble meilleure, juste parce qu'on l'a cuisinée soi-même.
VIII-C. Une procédure pour automatiser ce processus▲
Dans mfonctions, nous ajoutons une variable globale gEcran de type Position. Nous pourrons ainsi mémoriser les mesures dans gEcran.Largeur et gEcran.Hauteur.
Avant de poursuivre, nous allons apporter, manuellement, quelques modifications dans le code de notre formulaire fOuSuisJe.
Nous allons donner une portée publique à la procédure Sub Form_Timer(). Il y a Private Sub Form_Timer(), nous modifions en Public Sub Form_Timer(). Ainsi, nous pourrons faire appel à cette procédure depuis l'extérieur du formulaire.
Public
Sub
Form_Timer
(
)
[...
]
Nous allons aussi conditionner la copie dans le presse-papier. Elle ne surviendra que si le formulaire a été ouvert par l'utilisateur.
Pour détecter ce fait, nous utilisons la propriété CodeContextObject, elle permet de déterminer dans quel objet, du code Visual Basic est en cours d'exécution. Dans le cas où le formulaire fOuSuisJe a été ouvert par l'utilisateur, la propriété CodeContextObject.name aura pour valeur : « fOuSuisJe ». Par contre, si l'ouverture a été commandée par un processus inclus dans fPrincipal, nous aurons CodeContextObject.name=fPrincipal.
[...
]
If
CodeContextObject.Name
=
Me.Name
Then
'Commande dans le presse-papier
Me.zdtParam.SetFocus
'on sélectionne tout le texte
Me.zdtParam.SelStart
=
0
Me.zdtParam.SelLength
=
Len
(
Me.zdtParam.Text
)
'on copie dans le presse-papier
DoCmd.RunCommand
acCmdCopy 'équivalent du raccourci CTRL + C
End
If
[...
]
Par prudence, nous ajouterons une gestion d'erreur. Celle qui surviendrait, si pour une raison ou l'autre, la fonction « copier » n'est pas disponible à l'instant où le programme l'appelle.
GestionErreur
:
Select
Case
Err
.Number
Case
2046
'pour une raison ou l'autre la fonction "copier" n'est pas disponible
Resume
Next
Case
Else
MsgBox
"Erreur non prévue dans Form_Timer"
&
vbLf
_
&
Err
.Number
&
" : "
&
Err
.Description
&
"."
End
Select
Le code complet de l'événement « Sur minuterie » devient donc :
Public
Sub
Form_Timer
(
)
On
Error
GoTo
GestionErreur
'La position du coin sup gauche
Me.zdtDroitePix
=
PositionFormPx
(
Me.Name
).Droite
Me.zdtBasPix
=
PositionFormPx
(
Me.Name
).Bas
Me.zdtDroiteTwi
=
Me.zdtDroitePix
*
Me.ZdtNbreTwipspPixelHor
Me.zdtBasTwi
=
Me.zdtBasPix
*
Me.ZdtNbreTwipspPixelVer
'La Largeur et la hauteur
Me.zdtLargeurTwi
=
Me.WindowWidth
Me.zdtHauteurTwi
=
Me.WindowHeight
Me.zdtLargeurPix
=
Me.zdtLargeurTwi
/
Me.ZdtNbreTwipspPixelHor
Me.zdtHauteurPix
=
Me.zdtHauteurTwi
/
Me.ZdtNbreTwipspPixelVer
Me.zdtParam
=
"DoCmd.MoveSize "
&
Me.zdtDroiteTwi
&
" , "
&
Me.zdtBasTwi
&
" , "
_
&
Me.zdtLargeurTwi
&
" , "
&
Me.zdtHauteurTwi
' Si exécution à partir d'un autre formulaire, on n'effectue pas
'la copie presse-papiers de la zone de texte
If
CodeContextObject.Name
=
Me.Name
Then
'Commande dans le presse-papier
Me.zdtParam.SetFocus
'on sélectionne tout le texte
Me.zdtParam.SelStart
=
0
Me.zdtParam.SelLength
=
Len
(
Me.zdtParam.Text
)
'on copie dans le presse-papier
DoCmd.RunCommand
acCmdCopy 'équivalent du raccourci CTRL + C
End
If
Exit
Sub
GestionErreur
:
Select
Case
Err
.Number
Case
2046
'pour une raison ou l'autre la fonction "copier" n'est pas disponible
Resume
Next
Case
Else
MsgBox
"Erreur non prévue dans Form_Timer"
&
vbLf
_
&
Err
.Number
&
" : "
&
Err
.Description
&
"."
End
Select
End
Sub
Ceci fait, traduisons en code les manœuvres que nous avons faites au § 8. B.
D'abord la largeur de l'écran.
Nous ouvrons fOuSuisJe en mode création, pour supprimer ses bordures.
Ensuite, nous l'ouvrons et maximisons la fenêtre et dans la foulée (pour ne pas devoir attendre une seconde), nous déclenchons le code de la minuterie. C'est pour cela que nous la voulions « publique ».
Nous relevons alors la largeur et la mémorisons dans la variable gEcran.Largeur. Et terminons sans sauvegarder : fOuSuisJe reste donc intact.
Private
Sub
Ecran
(
)
'La Largeur
DoCmd.OpenForm
"fOuSuisJe"
, acDesign
Forms!fOuSuisJe.BorderStyle
=
0
DoCmd.OpenForm
"fOuSuisJe"
, acNormal
DoCmd.Maximize
Forms!fOuSuisJe.Form_Timer
gEcran.Largeur
=
Forms!fOuSuisJe.zdtLargeurTwi
DoCmd.Close
acForm, "fOuSuisJe"
, acSaveNo
DoCmd.Restore
Et on refait la même opération, pour la hauteur.
DoCmd.OpenForm
"fOuSuisJe"
, acDesign
Forms!fOuSuisJe.BorderStyle
=
0
DoCmd.OpenForm
"fOuSuisJe"
, acNormal
DoCmd.Minimize
Forms!fOuSuisJe.Form_Timer
gEcran.Hauteur
=
Forms!fOuSuisJe.zdtBasTwi
+
Forms!fOuSuisJe.zdtHauteurTwi
'Refermer sans sauver
DoCmd.Close
acForm, "fOuSuisJe"
, acSaveNo
Avant de terminer, on rétablit la dimension des autres objets ouverts (entre autres : fPrincipal).
DoCmd.Restore
End
Sub
IX. Rassemblons les pièces du puzzle▲
IX-A. Le code du formulaire fPrincipal▲
Nous allons expliquer le code événement par événement.
Nous ne réexpliquerons pas ici comment on navigue entre l'affichage en mode formulaire, en mode création ou de l'éditeur de code. Cela a été décrit en détail au § 3.B du tutoriel précédent.
IX-A-1. Sur ouverture (Private Sub Form_Open)▲
À l'ouverture du formulaire, nous allons déclencher les processus relatifs au calcul des bordures et des dimensions de l'écran.
La première fois que l'on ouvre le formulaire depuis que la base de données est ouverte, la variable globale gBordureDispo a sa valeur initiale : False (faux). Lorsque la procédure CalculBordures aura été exécutée gBordureDispo passera à True (vrai).
C'est la valeur constatée de gBordureDispo qui va nous servir de critère pour déclencher ou non l'appel des procédures CalculBordures et Ecran.
Ainsi, dans une même session, si le formulaire est ouvert plusieurs fois, les calculs ne seront lancés qu'une seule fois.
Remarquez cette instruction Application.Echo False, elle a pour effet de figer l'écran. Les actions qui suivent ne seront pas affichées. On rétablit la situation normale avec Application.Echo False quelques fractions de seconde plus tard. Cela pour éviter, à l'utilisateur, l'effet de flash désagréable.(2)
Private
Sub
Form_Open
(
Cancel As
Integer
)
If
gBordureDispo =
False
Then
Application.Echo
False
Call
CalculBordures
Call
Ecran
Application.Echo
True
End
If
End
Sub
Pour vérifier que ça marche :
passez en mode formulaire ;
dans la fenêtre d'exécution, saisissez : ? gBordureDispo ;
True s'affiche.
IX-A-2. Sur activation (Private Sub Form_Current)▲
C'est ici que nous allons faire le nécessaire pour que le contrôle zdtMILLESIME s'affiche en vert dans l'enregistrement actif.
Ici aussi, on va ruser. Nous allons utiliser le format conditionnel pour colorer le contrôle uniquement dans l'enregistrement actif.
Dans fPrincipal ouvert en mode création, cliquez sur le contrôle zdtMILLESIME pour le sélectionner.
Dans la barre des menus, vous cliquez sur Format et ensuite Mise en forme conditionnelle...
Il vient :
Si vous cliquez pour dérouler le choix des conditions, vous ne trouvez pas les mots pour dire : « changer la couleur dans l'enregistrement actif ». Il y a bien « Champ activé », mais ce que nous voulons, c'est changer la couleur de zdtMILLESIME quel que soit le contrôle activé (lui ou un autre, ou aucun) et ce, uniquement sur la ligne qui correspond à l'enregistrement en cours, comme ceci :
Voici une astuce pour dire « dans l'enregistrement actif ».
Dans notre formulaire, nous allons ajouter un contrôle indépendant (pas de source) que nous appelons par exemple zdtActif. Et nous le rendons invisible parce qu'il n'a pas d'intérêt pour l'utilisateur.
À chaque changement d'enregistrement, nous allons systématiquement copier dans ce contrôle, la valeur que nous savons unique, d'un autre contrôle. Par exemple la valeur de zdtDENOMINATION (DENOMINATION étant la clé de tVins, sa valeur est forcément unique).
Avec ce subterfuge le seul cas où on a égalité entre zdtDENOMINATION et zdtActif, c'est dans l'enregistrement actuellement actif !
Dès lors, nous exprimons la condition pour le formatage particulier comme ceci :
Et pour l'événement « Sur activation » nous codons ceci :
Private
Sub
Form_Current
(
)
Me.zdtActif
=
Me.zdtDENOMINATION
End
Sub
Vérifions que ça marche :
fermez fPrincipal en le sauvegardant ;
ouvrez-le à nouveau et naviguez entre les enregistrements pour constater le comportement du contrôle qui affiche le millésime, en vert dans le seul enregistrement actif.
IX-A-3. Double-clic sur zdtMILLESIME (Private Sub zdtMILLESIME_DblClick)▲
Il s'agit de commander l'ouverture du formulaire fDetail.
Juste une précaution : on referme d'abord fDetail au cas où il aurait déjà été précédemment ouvert. Ceci pour provoquer le déclenchement de son événement sur ouverture que nous verrons au § suivant.
Remarquez aussi la gestion d'erreur. Vous comprendrez mieux lorsque nous passerons en revue le code de fDetail.
Private
Sub
zdtMILLESIME_DblClick
(
Cancel As
Integer
)
On
Error
GoTo
GestionErreur
'Fermer le formulaire fDetail s'il est encore ouvert (appel précédent)
If
CurrentProject.AllForms
(
"fDetail"
).IsLoaded
Then
DoCmd.Close
acForm, "fDetail"
'Ouvrir le formulaire fDetail
DoCmd.OpenForm
"fDetail"
Select
Case
Err
.Number
Case
2501
Resume
Next
Case
Else
MsgBox
"Erreur inattendue dans ""Sub zdtMILLESIME_DblClick"""
_
&
vbLf
&
Err
.Number
&
" : "
&
Err
.Description
&
"."
End
Select
End
Sub
IX-A-4. Sur fermeture (Private Sub Form_Close)▲
Si on ferme fPrincipal, fDetail n'a plus de raison de rester ouvert. On le referme si c'était le cas.
Private
Sub
Form_Close
(
)
'Fermer le formulaire fDetail s'il est encore ouvert (appel précédent)
If
CurrentProject.AllForms
(
"fDetail"
).IsLoaded
Then
DoCmd.Close
acForm, "fDetail"
End
Sub
IX-B. Le code du formulaire fDetail▲
IX-B-1. Sur ouverture (Private Sub Form_Open)▲
Il s'agit « simplement » de calculer les paramètres de la méthode MoveSize de l'objet DoCmd.
Nous définissons une variable pour chacun de ces paramètres :
Private
Sub
Form_Open
(
Cancel As
Integer
)
Dim
lDroite As
Long
Dim
lBas As
Long
Dim
lLargeur As
Long
Dim
lHauteur As
Long
Nous allons nous assurer que fDetail a bien la propriété Fen indépendante = oui. C'est à cette condition que les paramètres de doCmd.MoveSize s'expriment par rapport au bord supérieur gauche de l'écran.
Si ce n'est pas le cas, on affiche un message à l'utilisateur et on interrompt le processus d'ouverture de fDetail.
If
Not
Me.PopUp
Then
MsgBox
"Le formulaire à positionner doit être en fenêtre indépendante"
&
_
vbCrLf
&
"(onglet Autre dans les propriétés du formulaire)"
, vbInformation
Cancel =
True
Exit
Sub
End
If
Si on fixe la valeur de Cancel à True, une erreur n° 2501 sera levée dans l'exécution du code qui avait demandé l'ouverture. Voir Sub zdtMILLESIME_DblClick de fPrincipal.
Pour la largeur et la hauteur, nous fixons les valeurs arbitraires de notre choix :
lLargeur =
4245
lHauteur =
3495
Dans un premier temps nous calculons les paramètres Droite et Bas, comme s'il y avait la place disponible à l'écran. On traduit donc en code les distances que nous avons montrées aux § 5 A et 5 B en utilisant la syntaxe montrée au § 6 :
'Calcul du paramètre Droite
lDroite =
PositionFormPx
(
"fPrincipal"
).Droite
*
gPixel2Twip.Horizontal
_
+
gBordure.Laterale
_
+
Forms!fPrincipal.CurrentSectionLeft
_
+
Forms!fPrincipal.zdtMILLESIME.Left
'Calcul du paramètre Bas
lBas =
PositionFormPx
(
"fPrincipal"
).Bas
*
gPixel2Twip.Vertical
_
+
gBordure.Superieure
_
+
Forms!fPrincipal.CurrentSectionTop
_
+
Forms!fPrincipal.zdtMILLESIME.Top
_
+
Forms!fPrincipal.zdtMILLESIME.Height
Nous vérifions ensuite qu'il y a place à l'écran et nous rectifions si nécessaire :
'c'est trop bas si détail est en dehors de l'écran
If
lBas +
lHauteur >
gEcran.Hauteur
Then
lBas =
lBas -
Forms!fPrincipal.zdtMILLESIME.Height
-
lHauteur
End
If
'c'est trop à droite si détail est en dehors de l'écran
If
lDroite +
lLargeur >
gEcran.Largeur
Then
lDroite =
lDroite +
Forms!fPrincipal.zdtMILLESIME.Width
-
lLargeur
End
If
Et on ouvre le formulaire à l'endroit précis :
DoCmd.MoveSize
lDroite, lBas, lLargeur, lHauteur
End
Sub
Pensez à compiler votre code.
Dans le menu de l'éditeur de code, cliquez sur Débogage et ensuite sur Compiler.
X. La preuve que ça fonctionne quelle que soit la section qui héberge le contrôle▲
Ouvrez le formulaire fPrincipal en mode création, sélectionnez le contrôle zdtMILLESIME et faites-le glisser dans la section Entête du formulaire. Passez en mode formulaire et essayez.
De même si le contrôle se trouve dans la section Pied du formulaire.
XI. Conclusion▲
On y est, fdetail s'affiche en entier à proximité du millésime cliqué, quelle que soit la position de fPrincipal.
C'était un des objectifs, mais peut-être pas le principal. Mon but était en fait de vous mettre le pied à l'étrier et vous montrer qu'il n'est pas tellement difficile d'apprendre à programmer, cela vous ouvre de nouvelles perspectives pour tirer meilleur parti de votre logiciel Access.
XII. La base de données▲
La base de données qui a servi à construire ce tutoriel se trouve ici.
XIII. Remerciements▲
À Arkham46 qui m'a généreusement offert l'idée d'écrire ce tutoriel et qui m'a guidé tout au long de la conception.
À l'équipe Office pour son soutien technique et logistique, en particulier Domi2.
À Djibril et _MAX_ qui m'ont aidé à respecter la langue de Molière. Pardon à ce dernier, si des fautes ont échappé à notre vigilance et merci à vous si vous me les dénoncez.