Créer un bouton animé pour la sélection d'enregistrements dans un formulaire Access

Jean-Philippe AMBROSINO

Ce document a pour but de vous montrer comment créer un bouton animé (qui change de taille) en fonction d'un contexte particulier en amont et faisant apparaître une liste d'enregistrements à sélectionner pour continuer…
Vous devez être relativement à l'aise avec Microsoft Access et connaître la conception de formulaires, mais également avoir des notions du langage Visual Basic for Application afin mettre en pratique cet exemple.

Commentez

- Titre : Créer un bouton animé pour la sélection d'enregistrements dans un formulaire Access

- Auteur : Jean-Philippe AMBROSINO

- Parution : 5 avril 2010

- Mise à jour : 5 avril 2010

- Licence : Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.

Sommaire

- I. Avant-propos

- I-A. Niveau

- I-B. Contact

- II. Préambule

- III. Présentation du projet

- IV. Mise en œuvre du projet

- IV-A. Création du formulaire

- IV-A-1. Détail sur les contrôles

- IV-A-2. Le formulaire finalisé

- IV-B. Mise en place du code événement

- IV-B-1. En-tête de module (Déclarations)

- IV-B-2. Événement lors du chargement du formulaire

- IV-B-3. Événement AprèsMaj de la liste des clients

- IV-B-4. Événement AprèsMaj de la liste des commandes

- IV-B-5. Événement Clic du bouton de commande

- IV-B-6. Événement Après Maj du cadre d'options

- IV-C. La procédure MoveMouseToControl()

- IV-C-1. Mais au fait… Pourquoi 170 et 24 ?

- V. Mise en exécution

- VI. Conclusion

- VII. Remerciements

I - Avant-propos

Ce document a pour but de vous montrer comment créer un bouton animé (qui change de taille) en fonction d'un contexte particulier en amont et faisant apparaître une liste d'enregistrements à sélectionner pour continuer…

Vous devez être relativement à l'aise avec Microsoft Access et connaître la conception de formulaires, mais également avoir des notions du langage Visual Basic for Application afin mettre en pratique cet exemple.

I-A - Niveau

Ce tutoriel s'adresse à toutes personnes connaissant l'environnement de développement Access et notamment la gestion des événements avec Visual Basic sur Microsoft Access.

I-B - Contact

Pour tous renseignements complémentaires, veuillez me contacter directement (Argyronet) par MP.

II - Préambule

Dans le cadre du développement d'un projet pour un client, j'ai été amené à réaliser une application d'aide à la décision et de reporting.
Le principe était basé sur l'absorption de fichiers Excel en base où des lignes étaient à valider avec différents cas selon leur contenu.

Tant qu'un classeur n'était pas validé, e.g. toutes les lignes n'étaient pas confirmées par une validation, il n'était pas possible de travailler avec un autre classeur et donc le bouton principal de validation sur le Menu principal voyait son intitulé changer en fonction de ce constat défini par une fonction idoine qui examinait la situation.

Pour parfaire l'effet, j'ai eu l'idée d'animer ce bouton dès que le classeur en cours était terminé et que d'autres classeurs étaient disponibles

Menu principal de l'application évoquée

J'ai alors décidé de vous faire profiter de ce petit embellissement, ma foi assez sympathique et presque utile…

III - Présentation du projet

Pour mettre en application ce tutoriel, j'ai transformé l'objectif du projet source en situation de gestion de commandes où ces dernières sont censées être saisies avec ou sans statut en instance.

La base de données exemple contient donc une table de commandes et une table de clients.

Je précise qu'aucun peaufinage n'est mis en application dans la conception de ce projet du fait que l'objectif se limite à la compréhension du phénomène d'animation du bouton selon trois situations, à savoir :

1. Visualisation des commandes (aucun effet) ;

2. Saisie d'une nouvelle commande (effet + liste des clients) ;

3. Validation d'une commande en cours (effet + liste des commandes concernées).

En mode démo, un appel de la fonction MsgBox() remplace l'ouverture effective du formulaire ciblé.

Note

Ce tutoriel peut, dans son ensemble, être mis en application pour toutes les versions d'Access (Ici sur Windows XP / Seven et Access 2007).
Toutefois, certaines méthodes seront à adapter selon les versions (antérieures).

IV - Mise en œuvre du projet

IV-A - Création du formulaire

Partant de l'idée que vous allez certainement mettre en application ce tutoriel sur un projet existant, je considère que vous possédez tous les éléments de base pour commencer.

Sur le principe, il est idéal que ce(s) bouton(s) soi(en)t posé(s) sur le formulaire représentant le Menu Principal…

Si tel n'était pas le cas et que vous souhaitez reproduire, disons fidèlement cet exemple, créez un nouveau formulaire d'environ 14 x 10 cm et posez-y cinq contrôles :

- 1 - Un contrôle Cadre d'options nommé fraDemo avec trois options (dûment intitulées) ;

- 2 - Un contrôle Label nommé lblQuestionAction justifié à droite avec une police type Calibri 12 gras italique ;

- 3 - Un contrôle Rectangle nommé shpDropList ;

- 4|5 - Deux contrôles Zone de Liste déroulante superposés et respectivement nommés cboOrders et cboCustomers (Détails spécifiés après) ;

- 6 - Un contrôle Bouton de commande nommé cmdGo.

Les contrôles dans le formulaire

Il est important de bien nommer les contrôles pour le succès de l'opération d'une part et pour une meilleure lisibilité du code d'autre part…

En parallèle, la disposition et la taille de certains contrôles jouent un rôle primordial.

Rappel

Pour plus d'information sur la convention de nommage des objets, vous pouvez lire ce tutoriel.

IV-A-1 - Détail sur les contrôles

Le cadre d'option fraDemo

Le cadre d'options possède trois options dont les intitulés sont les suivants :
- Consulter les commandes ;
- Créer une nouvelle commande ;
- Valider les commandes en cours.

Ce cadre est uniquement voué à la démonstration.

Dans la réalité et donc dans votre projet, l'état et l'intitulé du bouton changent en fonction d'une fonction idoine qui supervise la priorité.

Ainsi que je l'ai précisé ci-avant, dans mon projet initial, le bouton possédait deux intitulés d'une part et déclenchait trois appels de procédure selon trois circonstances spécifiques…

L'étiquette lblQuestionAction

Cette étiquette ne sert qu'à afficher un petit message de choix du client ou de la commande selon le cas…
Sa largeur est égale à celle du bouton de commande.

Le Rectangle shpDropList

Ce rectangle ne sert qu'à embellir le contour de chaque liste déroulante de manière à s'immiscer avec la hauteur du bouton. Il est en relief 3 D relevé.

Détails :

- Largeur : 2,672cm

- Hauteur : 1,173cm

- Haut : 4,566cm

- Gauche : 9,734cm

- Transparent : Oui

- Plan : Arrière (4)

Les listes cboOrders et cboCustomers

Ces listes contiennent respectivement la liste des commandes en instance et la liste des clients et sont établies avec les clauses SQL ci-après…
Elles apparaissent selon l'intitulé du bouton et se déroulent…

Détails :

cboOrders

- Largeur : 2,249cm

- Hauteur : 0,688cm

- haut : 4,808cm

- Gauche : 9,998cm

- Plan : Arrière (3)

- Nombre de colonnes : 2

- Largeur colonnes : 0,702cm;5cm

- Largeur liste : 6cm

- Colonne liée : 1

- Contenu :

SELECT TBLOrders.OrderID, "N° " & Format([OrderID],"000") & " du " & Format([OrderDate],"d mmm yyyy") AS OrderInfo
FROM TBLOrders
WHERE (((TBLOrders.IsValidated)=False))
ORDER BY "N° " & Format([OrderID],"000") & " du " & Format([OrderDate],"d mmm yyyy");

cboCustomers

- Taille et position : idem

- Plan : Arrière (2)

- Nombre de colonnes : 2

- Largeur colonnes : 0cm;6cm

- Largeur liste : 6cm

- Colonne liée : 1

- Contenu :

SELECT TBLCustomers.CustomerID, TBLCustomers.CompanyName FROM TBLCustomers;

Le bouton cmdGo

C'est le bouton principal… Il bénéficie des effets d'animation régis par le code.

Détails :

- Largeur : 11,429cm

- Hauteur : 1,323cm

- haut : 4,524cm

- Gauche : 1,032cm

- Plan : Premier (1)

- Légende : ""

IV-A-2 - Le formulaire finalisé

Une fois le formulaire et ses contrôles agencés, vous devez obtenir quelque chose comme ceci :

Formulaire fini en mode création

IV-B - Mise en place du code événement

IV-B-1 - En-tête de module (Déclarations)

Le formulaire utilise pour ses contrôles, des constantes déclarées dans l'en-tête du module de classe de ce dernier.

- Les constantes de largeur du bouton définissent la largeur mini et maxi de celui-ci.

- Les constantes d'intitulés sont utilisées au gré du choix de l'option cliquée pour le bouton.

- Une énumération est déclarée en en-tête pour faciliter la transposition des différents cas du cadre d'options.

La constante MODE_DEMO sert à passer en mode démo pour éviter d'exécuter le code d'appel qui pourrait survenir lors de l'ouverture des formulaires…
Elle est définie à True dans ce cas

En-tête de module (Déclarations)

Option Compare Database
Option Explicit

''' Longueur du bouton agrandi et réduit
Private Const CMD_MAX_WIDTH                  As Long = 6615
Private Const CMD_MIN_WIDTH                  As Long = 5100
''' Légende du bouton
Private Const CPT_CMD_CREATE_NEW_ORDER       As String = "&Saisir une nouvelle commande"
Private Const CPT_CMD_CONSULT_ORDERS         As String = "&Consulter les commandes"
Private Const CPT_CMD_VALIDATE_ORDERS        As String = "&Valider des commandes"
Private Const CPT_CMD_ORDERS                 As String = "&Gestion des commandes"

''' Énumération des états
Private Enum m_eOrdersButton
eShowOrders = 1
eNewOrder = 2
eOrdersToValidate = 3
End Enum

''' Mettre False pour l'usage fonctionnel
Const MODE_DEMO                              As Boolean = True

IV-B-2 - Événement lors du chargement du formulaire

- Au chargement du formulaire, on initialise le cadre d'option avec la valeur 0 pour qu'aucune option ne soit sélectionnée ;

- Le bouton reçoit sa légende par défaut ;

- Les autres contrôles sont cachés (même s'ils sont en arrière-plan pour certains).

Événement lors du chargement du formulaire

Private Sub Form_Load()
''' Intialise les contrôles pour la démonstration
fraDemo = 0
cmdGo.Caption = CPT_CMD_ORDERS
shpDropList.Visible = False
cboCustomers.Visible = False
cboOrders.Visible = False
lblQuestionAction.Visible = False
End Sub

IV-B-3 - Événement AprèsMaj de la liste des clients

L'ouverture du formulaire permettant de générer une nouvelle commande est régie par l'événement AprèsMaj de la zone de liste des clients, obligeant ainsi l'utilisateur à générer une commande pour un client donné…

La valeur issue de la sélection dans cette liste est passée à l'argument OpenArgs de la méthode OpenForm de l'objet DoCmd, précédée du nom du champ concerné et séparée par un point-virgule…
Il est évident que l'argument de la clause WHERE n'aurait pu être exploité ici du fait que la commande n'existe pas.
=> Vous aurez alors à mettre en œuvre la gestion de la propriété OpenArgs de la méthode OpenForm ayant ouvert le formulaire.

Événement AprèsMaj de la liste des clients

Private Sub cboCustomers_AfterUpdate()
If MODE_DEMO Then
MsgBox "On ouvre le formulaire de création d'une commande pour le client :" _
& vbCrLf & Me!cboCustomers.Column(1), , "Mode démonstration"
Else
'''Ouvre le formulaire de création d'une commande pour le client sélectionné
DoCmd.OpenForm "frmCreateNewOrder", , , , acFormAdd, acWindowNormal, "CustomerID;" & Me.cboCustomers
End If
End Sub

IV-B-4 - Événement AprèsMaj de la liste des commandes

L'ouverture du formulaire permettant d'ouvrir une commande n'ayant pas été validée (dans le schéma fonctionnel bien sûr) est régie par l'événement AprèsMaj de la zone de liste des commandes, obligeant ainsi l'utilisateur à ouvrir la commande concernée…

La valeur issue de la sélection dans cette liste est passée à l'argument WhereCondition de la méthode OpenForm de l'objet DoCmd, précédée du nom du champ concerné…
Il est précisé ici que la clause WHERE est exploitée du fait que la commande existe déjà.

Événement AprèsMaj de la liste des commandes

Private Sub cboOrders_AfterUpdate()
If MODE_DEMO Then
MsgBox "On ouvre le formulaire permettant de valider la commande " _
& Me!cboOrders.Column(1), , "Mode démonstration"
Else
''' Ouvre le formulaire listant les commandes en restant à valider
DoCmd.OpenForm "frmShowOrderToValidate", , , "[OrderID]=" & Me.cboOrders, acFormAdd, acWindowNormal
End If
End Sub

IV-B-5 - Événement Clic du bouton de commande

C'est le bloc de code le plus riche, et pour cause, c'est le centre d'intérêt du tutoriel…

Le principe est fondé sur la propriété Caption du bouton qui possède trois intitulés définis par le choix du cadre d'option et des constantes déclarées en en-tête.
Il est bien entendu possible d'exploiter un tout autre type de cas voire la propriété Tag, si le cœur vous en dit.

Pour les cas de Validation d'une commande ou de Génération d'une nouvelle commande (toujours dans le contexte fonctionnel), le bouton voit sa taille affectée* pour faire apparaître la Zone de liste déroulante correspondante :

- pour une nouvelle commande : la liste des clients ;

- pour une validation de commande : la liste des commandes non validées.

Enfin et surtout, le bouton de commande lui-même perd son Focus et devient désactivé (Enabled = False).

Pour le troisième intitulé, le bouton ne change pas d'état et exécute directement le code souhaité à savoir, l'ouverture du formulaire contenant toutes les commandes en cours…

D'un point de vue propriétés des contrôles…

- Ceux qui étaient définis à Visible = False passent à True.

- L'étiquette voit sa propriété Caption affectée ou non.

- Etc. (voir code et commentaires).

La constante MODE_DEMO est utilisée ici aussi afin de faire apparaître un MsgBox() au lieu d'ouvrir le formulaire…

* La valeur de 40 définit le pas de rétrécissement… Vous pouvez l'augmenter pour le ralentir.

Événement Clic du bouton de commande

Private Sub cmdGo_Click()
Dim lngCountDemands                      As Long
Dim lngWidth                             As Long
Dim L                                    As Long

''' Selon l'intitulé du bouton...
Select Case cmdGo.Caption

''' Validation de commandes
Case CPT_CMD_VALIDATE_ORDERS
lngWidth = cmdGo.Width
''' Réduction de la taille
For L = lngWidth To 0 Step -40
cmdGo.Width = L
If L <= CMD_MIN_WIDTH Then Exit For
DoEvents
Next
cmdGo.SetFocus
shpDropList.Visible = True
''' Affectation de l'état des listes...
With cboOrders
.Visible = True
.SetFocus
.Dropdown
End With
With cboCustomers
.Visible = False
End With
With lblQuestionAction
.Visible = True
.Caption = "Pour quelle commande ?"
End With
cmdGo.Enabled = False
MoveMouseToControl Me, "cboOrders", 170, 24

''' Nouvelle commande
Case CPT_CMD_CREATE_NEW_ORDER
lngWidth = cmdGo.Width
''' Réduction de la taille
For L = lngWidth To 0 Step -40
cmdGo.Width = L
If L <= CMD_MIN_WIDTH Then Exit For
DoEvents
Next
cmdGo.SetFocus
shpDropList.Visible = True
''' Affectation de l'état des listes...
With cboOrders
.Visible = False
End With
With cboCustomers
.Visible = True
.SetFocus
.Dropdown
End With
With lblQuestionAction
.Visible = True
.Caption = "Pour quel client ?"
End With
cmdGo.Enabled = False
MoveMouseToControl Me, "cboCustomers", 170, 24

''' Liste des commandes
Case CPT_CMD_CONSULT_ORDERS
lngCountDemands = DCount("OrderID", "TBLOrders")
If lngCountDemands Then
''' Ouvre le formulaire listant les commandes en cours
If MODE_DEMO Then
MsgBox "On ouvre le formulaire listant les commandes en cours", , "Mode démonstration"
Else
DoCmd.OpenForm "frmShowOrderList", , , , acFormAdd, acWindowNormal, "CustomerID;" & Me.cboCustomers
End If
Else
MsgBox "Il n'existe aucune commande à consulter actuellement.", 16, "Information"
End If
End Select
End Sub

IV-B-6 - Événement Après Maj du cadre d'options

Comme précisé ci-avant, le cadre d'option n'a pour rôle que d'appliquer le côté théorique de ce tutoriel.
Il vous appartient de mettre en œuvre le code ad hoc afin que le bouton voie sa propriété Caption affectée en fonction de telle ou telle circonstance…

Ici, son rôle se limite à :

1. Réinitialiser la taille du bouton ;

2. Lui donner le Focus ;

3. Affecter sa propriété Caption ;

4. Masquer certains contrôles.

Rappel

Un cadre d'option sous Access renvoie la valeur correspondante de l'élément coché ou cliqué (1,2,n…) si peu que vous ayez énuméré ces éléments chronologiquement ou la valeur correspondante dans le cas contraire…

Paradoxalement, en VBA (pour Excel, Word et PowerPoint…) dans un UserForm, c'est l'élément coché ou cliqué qui prend la valeur True et les autres False, le cadre ne servant qu'à délimiter le périmètre des boutons d'option qu'il contient. Tout bouton d'option externe au cadre n'est pas concerné.

Événement AprèsMaj du cadre d'options

Private Sub fraDemo_AfterUpdate()
''' Une fonction gère l'état et le Caption du bouton à l'ouverture du formulaire
With Me.cmdGo
Select Case fraDemo
Case eShowOrders
.Caption = CPT_CMD_CONSULT_ORDERS
.ControlTipText = "Permet d'afficher la liste des toutes les commandes"
Case eNewOrder
.Caption = CPT_CMD_CREATE_NEW_ORDER
.ControlTipText = "Permet d'enregistrer une nouvelle commande"
Case eOrdersToValidate
.Caption = CPT_CMD_VALIDATE_ORDERS
.ControlTipText = "Permet d'afficher la liste des commandes à valider"
End Select
.Enabled = True
.Width = CMD_MAX_WIDTH
.SetFocus
End With
''' Masque les autres contrôles
shpDropList.Visible = False
cboCustomers.Visible = False
cboOrders.Visible = False
lblQuestionAction.Visible = False
End Sub

IV-C - La procédure MoveMouseToControl()

Le petit plus qui rend l'effet encore plus sympathique est de forcer la position du pointeur de la souris sur la liste déroulante après avoir cliqué sur le bouton…

Le principe consiste à utiliser l'API SetCursorPos couplée à la fonction non documentée accLocation().

Code avec accLocation (non opération sous 2007)

Private Declare Function apiSetCursorPos
Lib "user32" Alias "SetCursorPos" (ByVal X As Long, ByVal Y As Long) As Long

Private Sub MoveMouseToControl (ByRef F As Access.Form, _
ByVal ControlName As String, _
ByVal GoToRight As Long, _
ByVal GoToBottom As Long)
Dim lx As Long, ly As Long, lw As Long, lh As Long
' *** Pour la version 2007 un 5e paramètre est requis
Call F.Controls(ControlName).accLocation(lx, ly, lw, lh, 0&)
apiSetCursorPos lx + lw / 2 + GoToRight, ly + lh / 2 + GoToBottom
End Sub

Du fait que la fonction accLocation() ne s'exploite pas sous la version 2007 d'Access comme sous les versions précédentes, vous pouvez :

- soit inscrire, quoi qu'il en soit, le 5e argument 0&, que vous utilisiez la version 2007 ou une version antérieure ;

- soit utiliser l'exemple stipulé ci-après.

Sous Access 2007, si vous omettez le 5e argument - celui-ci étant facultatif dans les versions antérieures à 2007 - cela se traduira par une erreur 5 et bien entendu, aucune aide contextuelle n'est consultable…

Dans ce tutoriel, j'ai exploité et adapté le code de la fonction alternative issue de la FAQ et écrite par Arkham46 et Cafeine que vous trouvez à cette page…

Vous ajoutez ainsi un module à votre projet que vous nommez par exemple basApplication et vous y recopiez le code suivant :

'---------------------------------------------------------------------------------------
' Module     : basApplication
' Author     : Arkham46, Cafeine
' Adaptation : Argyronet
' Date       : 06/04/2010
' Purpose    : Permet de positionner la souris sur un contrôle donné
' Source     : Developpez.com (http://access.developpez.com/faq/?page=Ctrl#DeplacerMouseControl)
'---------------------------------------------------------------------------------------

Option Compare Database
Option Explicit

Private Type RECT
Left                                     As Long
Top                                      As Long
Right                                    As Long
Bottom                                   As Long
End Type

Private Declare Function apiSetCursorPos _
Lib "user32" Alias "SetCursorPos" (ByVal X As Long, ByVal Y As Long) As Long
Private Declare Function GetWindowRect _
Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function apiGetFocus _
Lib "user32" Alias "GetFocus" () As Long

'---------------------------------------------------------------------------------------
' Procedure : MoveMouseToControl
' Author    : Arkham46, Cafeine
' Updated   : JP AMBROSINO
' Date      : 06/04/2010
'--------------------------------
' Parameters:
' F = Objet formulaire
' ControlName = Nom du contrôle
' GoToRight = Valeur de décalage (vers le la droite)
' GoToBottom = Valeur de décalage (vers le bas)
'---------------------------------------------------------------------------------------
'
Public Sub MoveMouseToControl(ByRef F As Access.Form, _
ByVal ControlName As String, _
ByVal GoToRight As Long, _
ByVal GoToBottom As Long)
Dim oCtl                                 As Control
Dim hCtl                                 As Long
Dim tRect                                As RECT

For Each oCtl In F.Controls
If oCtl.Name = ControlName Then
oCtl.SetFocus
hCtl = apiGetFocus
GetWindowRect hCtl, tRect
apiSetCursorPos tRect.Left + (tRect.Right - tRect.Left) / 2 + GoToRight, _
tRect.Top + (tRect.Bottom - tRect.Top) / 2 + GoToBottom
Exit For
End If
Next oCtl
Set oCtl = Nothing
End Sub

IV-C-1 - Mais au fait… Pourquoi 170 et 24 ?

Effectivement, vous êtes en droit de vous poser la question…
J'ai inscrit ces valeurs respectivement pour le décalage droit et bas en convertissant les valeurs du contrôle de Zone de liste exprimées en centimètres une fois déroulé à concurrence de 75 % de sa valeur et convertie en Pixels…
Ces paramètres permettent au pointeur de se loger suffisamment à droite et en dessous du texte…

V - Mise en exécution

Une fois cette étape terminée, vous basculez de nouveau sur votre formulaire (Alt+Tab) ou bien vous quittez l'éditeur Visual Basic Editor depuis le menu Fichier.

Vous enregistrez de manière à ne pas perdre votre travail et vous passez en Mode Formulaire.

À cet instant, lorsque vous cochez une option puis vous cliquez sur le bouton, l'événement correspondant s'applique…

Lorsque l'on souhaite générer une nouvelle commande…

Lorsque l'on souhaite valider une commande en instance…

Voici la vidéo représentant le résultat de l'utilisation du bouton en question

Animation en format "flash" non supportée.
Contenu uniquement consultable en ligne à cette adresse :
https://argyronet.developpez.com/office/access/animatedbutton/video/demobutton.swf

VI - Conclusion

Ce petit tutoriel relativement simple permettra aux personnes désireuses d'agrémenter leurs formulaires de mettre en place cette routine ma foi amusante et peu orthodoxe.

D'autres idées peuvent découler de ce tutoriel, en tout cas je vous le souhaite…

VII - Remerciements

Je tiens à remercier toutes celles et tous ceux qui ont participé à la relecture de ce document en y incluant leurs remarques et en particulier :

Arkham46, Pierre Fauconnier et Tofalu pour leurs points de vue et pixelomilcouleurs pour sa relecture approfondie.