I. Introduction▲
M.E.L.A., ou C.R.U.D. en anglais est l'acronyme de Modification, Effacement, Lecture, Ajout (Create, Read, Update, Delete). Il s'agit des actions de création, suppression, modification et enregistrement des données d'un formulaire. Toutes les applications qui gèrent de l'information sont équipées d'un système M.E.L.A.
La plupart des applications ACCESS possèdent un M.E.L.A. par formulaire. Qu'il ait été dupliqué ou créé pour chacun d'eux, il représente, en phase de conception, une charge importante.
Dans ce tutoriel, nous allons créer un M.E.L.A. unique et générique qui s'adaptera à tous les formulaires d'une application via une simple déclaration. Pour certains formulaires le M.E.L.A. devra être complété au niveau du formulaire par des procédures spécifiques, nous montrerons aussi comment les mettre en œuvre.
I-A. Mise en garde▲
Ce tutoriel nécessite une bonne connaissance de VBA et du fonctionnement des formulaires en général.
Il a été constaté que certains types de champs spécifiques comme les Multivalués (Pièces jointes) provoquent des dysfonctionnements de la classe, notamment lors de l'enregistrement.
II. Analyse du besoin▲
Le besoin est décrit en partie par l'acronyme.
II-A. Modification▲
Dans les formulaires ACCESS, on retrouve souvent des contrôles déverrouillés. C'est la méthode basique qui ne nécessite pas de programmation, mais qui pose de réels problèmes lors de fausses manipulations. Cette méthode étant insuffisante pour une application professionnelle, nous utiliserons un système de verrouillage/déverrouillage des contrôles avec la propriété Enabled ouLocked.
II-B. Effacement▲
L'effacement d'un enregistrement se fait par la commande Runcommand acCmdDeleteRecord.
II-C. Lecture▲
La lecture ne comprend que le déplacement entre enregistrements.
Deux boutons permettent de le faire : le bouton précédent et le bouton suivant. Bien qu'il existe les méthodes MovePrevious et MoveNext dans l'objet Recordset, il est conseillé de passer par la commande GotoRecord habituelle.
En effet, les méthodes Move ne sont pas opérationnelles lorsque le curseur est positionné sur le NewRecord.
Vous pouvez également créer des boutons pour aller à la première et la dernière fiche.
II-D. Ajout▲
Pour ajouter une fiche, il faut se placer sur le nouvel enregistrement avec la méthode AddNew.
II-E. Copie▲
Pour copier une fiche, on utilise le RecordsetClone comme source dont on duplique le contenu dans le Recordset.
III. Formulaire et boutons▲
Maintenant que nous avons déterminé les grandes fonctionnalités nécessaires pour bâtir notre M.E.L.A., nous allons réaliser un formulaire type dans une base annexe.
Ouvrons une nouvelle base et ajoutons une table et un formulaire basé sur celle-ci.
III-A. Le formulaire▲
La propriété du formulaire Avec Module doit être réglée sur Oui.
Nous allons positionner les nouveaux boutons dans son en-tête.
Les boutons :
Nom |
Libellé |
btnPrecedent |
&Précédent |
btnSuivant |
Sui&vant |
btnCreer |
&Créer |
btnEnregistrer |
&Enregistrer |
btnModifer |
&Modifier |
btnCopier |
&Modifier |
btnSupprimer |
&Supprimer |
btnAnnuler |
&Annuler |
btnFermer |
&Fermer |
On ajoute la liste déroulante LmRechercher qui permettra de faire des recherches rapides. Cette liste est composée d'une colonne cachée contenant l'ID unique que toute table doit posséder et d'une ou plusieurs autres colonnes contenant les données utilisateur d'identification.
Dans la zone de détail, il est possible que vous souhaitiez garder des contrôles actifs. C'est le cas dans l'exemple fourni en téléchargement du bouton Envoyer Email. Dans ce cas vous devez mettre le mot NOLOCK dans la propriété Remarque de ces contrôles.
Le nom des contrôles (boutons, zone de liste) doit absolument être respecté.
Voici le résultat.
Le bouton Enregistrer est positionné au-dessus du bouton Modifier.
IV. La classe▲
Pour réaliser notre M.E.L.A. générique, nous allons créer une classe. La classe a plusieurs avantages, son installation est rapide sur les formulaires, et les évolutions touchent l'ensemble de l'application sans autre modification qu'elle-même.
L'interaction entre une classe et un objet doté d'événements comporte des règles précises qu'il vaut mieux bien connaitre avant de se lancer.
Pour que la classe gère les événements d'un objet, il faut :
- qu'il dispose d'événements ;
- que les événements utilisés soient activés en réglant leur propriété sur [Event Procedure] ou [Procédure événementielle] ;
- que la variable qui le représente soit déclarée avec la clause WithEvents.
Les propriétés événementielles qui seront utilisées peuvent être réglées directement dans la classe comme nous le verrons plus tard.
La procédure événementielle de l'objet n'a pas forcément besoin d'exister explicitement, cependant si elle est présente, son code sera exécuté avant celui de la classe.
Par exemple, si du code existe sur l'événement Sur Clic à la fois dans l'objet et dans la classe, c'est toujours celui de l'objet qui sera exécuté en premier.
IV-A. Créer la classe▲
Créons un nouveau fichier nommé MELA.accdb.
Pour créer un module de classe, il faut aller dans le ruban Créer, Autre, Macro, Module de classe. L'éditeur VBE s'ouvre.
Les propriétés du module de classe sont réglées ainsi :
PublicNotCreatable permet d'utiliser cette classe à l'extérieur de sa base moyennant un petit artifice.
Une classe publique placée en bibliothèque est visible dans toutes les bases depuis laquelle elle est référencée. Cependant son instanciation est impossible.
Schématiquement, cela veut dire que la classe se trouvant dans une base bibliothèque dûment référencée est visible de la base principale, mais ne peut être instanciée. En effet, le périmètre d'action de l'instruction de déclaration New est limité à la base dans laquelle elle est exécutée.
Une base A voit une classe de la base B référencée comme bibliothèque, mais est incapable de l'utiliser.
Un artifice consiste à appeler une fonction de la bibliothèque qui se chargera de déclarer la classe et d'en renvoyer l'instance.
IV-B. Déclaration▲
Les variables d'objets de la classe sont déclarées privées et déclencheuses d'événements (WithEvents).
'---------------------------------------------------------------------------------------
' Module : cBoutonNav
' Author : fabrice CONSTANS MVP - 2008 - 2011
' Developpez.com (pseudo : Loufab)
' Date : 23/09/2011,29/09/2012
' Purpose : Classe MELA (CRUD)
' gestion de Modification, Effacement, Lecture, Ajout dans un formulaire
'
' Copyright : Vous pouvez utiliser cette classe et la modifier dans le cadre privé,
' professionnel et associatif. Vous devez laisser le nom de l'auteur.
' Vous ne pouvez pas la distribuer sous quelque forme que ce soit
' sans l'autorisation écrite de l'auteur.
' fabrice.constans@wanadoo.fr
' Merci à Micniv et Robyseb (developpez.com) pour les corrections apportées au code
'---------------------------------------------------------------------------------------
Option
Compare Database
Option
Explicit
Private
WithEvents oForm As
Form
Private
WithEvents btnCreer As
CommandButton
Private
WithEvents btnModifier As
CommandButton
Private
WithEvents btnEnregistrer As
CommandButton
Private
WithEvents btnCopier As
CommandButton
Private
WithEvents btnAnnuler As
CommandButton
Private
WithEvents btnSupprimer As
CommandButton
Private
WithEvents btnPrecedent As
CommandButton
Private
WithEvents btnSuivant As
CommandButton
Private
WithEvents btnFermer As
CommandButton
Private
WithEvents LmRechercher As
ComboBox
Private
vDefaultControlName As
String
Private
vIdCurrentRecord As
Long
La variable vDefaultControlName permet de stocker le nom d'un contrôle. Elle sera utilisée ultérieurement pour positionner le focus sur un contrôle lors de certaines actions.
La variable vIdCurrentRecord permet de stocker l'ID unique de l'enregistrement courant. Nous verrons lors de la suppression comment elle peut être utilisée.
IV-C. Propriété Form▲
La première propriété à créer initialise le formulaire et les boutons du M.E.L.A.
Cette propriété publique sera appelée depuis chaque formulaire bénéficiant du M.E.L.A.
Public
Property
Set
Form
(
objForm As
Form)
' initialisation du formulaire à gérer
On
Error
GoTo
errSub
Dim
ctrl As
Control
Set
oForm =
objForm
' parcourt l'entete de formulaire
For
Each
ctrl In
oForm.Section
(
acHeader).Controls
If
TypeOf ctrl Is
CommandButton Then
' on active l'événement
ctrl.OnClick
=
"[Event Procedure]"
Select
Case
ctrl.Name
Case
"btnCreer"
Set
btnCreer =
ctrl
Case
"btnModifier"
Set
btnModifier =
ctrl
Case
"btnEnregistrer"
Set
btnEnregistrer =
ctrl
Case
"btnCopier"
Set
btnCopier =
ctrl
Case
"btnSupprimer"
Set
btnSupprimer =
ctrl
Case
"btnPrecedent"
Set
btnPrecedent =
ctrl
Case
"btnSuivant"
Set
btnSuivant =
ctrl
Case
"btnFermer"
Set
btnFermer =
ctrl
Case
"btnAnnuler"
Set
btnAnnuler =
ctrl
Case
Else
' non géré
End
Select
End
If
'Cas spécial pour la liste de recherche
If
ctrl.Name
=
"LmRechercher"
Then
Set
LmRechercher =
ctrl
ctrl.AfterUpdate
=
"[Event Procedure]"
End
If
Next
...
L'objet formulaire est stocké dans la variable objet oForm tandis qu'un premier For Each parcourt la section d'en-tête de formulaire (acHeader) pour déclarer chaque bouton de commande (CommandButton). Pour chaque bouton de commande, la propriété événementielle OnClick est activée.
Chaque bouton de commande possède sa propre variable.
Le deuxième For Each parcourt la section de détail (acDetail) à la recherche du contrôle dont l'index de tabulation (TabIndex) est égal à 0. Ce contrôle capturé recevra le focus au moment du passage en saisie.
' fixe le contrôle par défaut
For
Each
ctrl In
oForm.Section
(
acDetail).Controls
' c'est un contrôle avec une propriété TabIndex
If
Not
(
TypeOf ctrl Is
Label Or
TypeOf ctrl Is
Line Or
TypeOf ctrl Is
Image Or
_
TypeOf ctrl Is
CustomControl Or
TypeOf ctrl Is
Page Or
_
TypeOf ctrl Is
PageBreak Or
TypeOf ctrl Is
Rectangle) Then
If
ctrl.TabIndex
=
0
Then
DefaultControlName =
ctrl.Name
Exit
For
End
If
End
If
Next
oForm.OnCurrent
=
"[Event Procedure]"
Set
ctrl =
Nothing
Exit
Property
errSub
:
Debug.Print
Err
.Number
&
" -- "
&
Err
.Description
Resume
Next
End
Property
La propriété événementielle OnCurrent du formulaire est également activée.
Chaque fois qu'on doit ajouter un bouton de commande ou autre dans le M.E.L.A., on le fait depuis cette procédure.
Le Get sur la propriété Form permet d'accéder à l'objet formulaire. Nous pourrions nous passer de cette propriété et y accéder en utilisant l'objet oForm.
Private
Property
Get
Form
(
) As
Form
' renvoie l'objet formulaire
Set
Form =
oForm
End
Property
Le nom des procédures événementielles de l'objet Form est toujours formé avec le nom de la variable (oForm), jamais avec le Get (Form).
Public
Sub
Form_Current
(
)
Ceci ne fonctionne pas dans la classe.
Alors que :
Public
Sub
oForm_Current
(
)
Cette syntaxe fonctionne dans la classe.
Il ne faut pas oublier de déclarer le « Getter » et « Setter » de la propriété DefaultControlName.
Public
Property
Get
DefaultControlName
(
) As
String
' renvoie le contrôle par défaut
Let
DefaultControlName =
vDefaultControlName
End
Property
Public
Property
Let
DefaultControlName
(
vControlName As
String
)
' définit le contrôle par défaut
vDefaultControlName =
vControlName
End
Property
Ces deux procédures ont pour but d'écrire et de lire la propriété DefaultControlName. Elle est volontairement déclarée publique pour pouvoir la définir depuis l'extérieur. En effet, il est possible de vouloir définir un autre contrôle à activer.
De la même manière, nous déclarons le «Getter» et «Setter» de la propriété IdCurrentRecord.
Public
Property
Get
IdCurrentRecord
(
) As
Long
' pour les opérations postméthode
IdCurrentRecord =
vIdCurrentRecord
End
Property
Public
Property
Let
IdCurrentRecord
(
vlIdCurrentRecord As
Long
)
vIdCurrentRecord =
vlIdCurrentRecord
End
Property
IV-D. La fonction d'instanciation▲
Comme précisé dans un des chapitres précédents, les classes d'une bibliothèque ne peuvent être utilisées directement. Il faut créer une fonction.
Ouvrons un nouveau module normal pour y ajouter cette fonction.
Public
Function
CreerMELA
(
) As
cBoutonNav
' Pour créer une instance de la classe
' obligatoire dans le cas d'une bibliothèque
Set
CreerMELA =
New
cBoutonNav
End
Function
La base bibliothèque peut être enregistrée.
V. Référencer la bibliothèque▲
Repassons sur l'application pour référencer la bibliothèque.
Dans le menu Outils/Références, il faut cliquer sur Parcourir, choisir Tous les fichiers (*.*) dans la liste déroulante Fichiers de type, puis aller chercher le fichier MELA.accdb dans son répertoire.
Nous validons notre choix avec le bouton Ouvrir.
La bibliothèque apparait maintenant dans la liste.
VI. Le formulaire▲
Pour que le formulaire utilise la classe cBoutonNav, nous devons instancier cette dernière.
Ouvrons le formulaire en mode création et activons l'événement Sur Ouverture.
Dans l'en-tête du module, il faut déclarer la variable de classe.
Option
Compare Database
Option
Explicit
Dim
mButtonNav As
cBoutonNav
Il ne reste qu'à instancier la classe au chargement du formulaire.
Private
Sub
Form_Open
(
Cancel As
Integer
)
' mise en fonction de la classe
Set
mButtonNav =
MELA.CreerMELA
(
)
' paramétrage de la classe avec le formulaire
Set
mButtonNav.Form
=
Me
' Dans le cas où l'on veut fixer un contrôle par défaut différent
' de celui défini par la classe.
'mButtonNav.DefaultControlName = "Prenom"
End
Sub
La propriété Form de la classe est renseignée avec l'objet formulaire (Me).
Toute variable de type objet doit être libérée, nous ajoutons cette libération dans l'événement Sur Fermeture du formulaire.
Private
Sub
Form_Close
(
)
Set
mButtonNav =
Nothing
'libère la variable
End
Sub
À la fermeture du formulaire, la classe sera libérée de la mémoire.
VII. Les méthodes de la classe▲
À présent, rédigeons les méthodes et propriétés de la classe.
La modification d'une bibliothèque ne peut se faire au travers de la base qui la référence. Bien que son code soit visible, la bibliothèque est, à ce moment, en lecture seule. Pour modifier une bibliothèque, il faut fermer les bases qui la référencent et la rouvrir.
VII-A. Verrouiller les contrôles▲
Le verrouillage/déverrouillage des contrôles se fait grâce à une méthode privée de notre classe. Lors de l'appel, une valeur booléenne peut être passée :
- True verrouille les contrôles ;
- False déverrouille ;
- lorsque rien n'est passé, il y a inversion du verrouillage des contrôles.
La visibilité des boutons Enregistrer et Modifier est inversée. Le focus est passé soit au contrôle par défaut (DefaultControlName), soit au bouton Modifier afin d'éviter les erreurs liées à la désactivation d'un contrôle actif.
Private
Sub
ActiverControleDetail
(
Optional
Activer As
Variant
=
True
)
' active/desactive les contrôles
On
Error
Resume
Next
Dim
ctrl As
Control
' Activer n'est pas fourni on fait le contraire
'If IsMissing(Activer) Then Activer = Not oForm.Controls(Me.DefaultControlName).Enabled
If
IsMissing
(
Activer) Then
Activer =
oForm.Controls
(
Me.DefaultControlName
).Locked
' Evol : 14/7/2012
' Suppression, Ajout, modif sont gérés dans les formulaires continus et liés.
'oForm.AllowDeletions = Activer
'oForm.AllowAdditions = Activer
'oForm.AllowEdits = Activer
'on ne parcourt que le détail (le header contient les boutons de navigation
For
Each
ctrl In
oForm.Section
(
acDetail).Controls
If
Not
(
ctrl.Tag
Like "*NOLOCK*"
) Then
ctrl.Locked
=
Not
Activer
'Evol 13/07/2012 gestion des sous-oForm
If
TypeOf ctrl Is
SubForm Then
ctrl.oForm.AllowDeletions
=
Activer
ctrl.oForm.AllowAdditions
=
Activer
ctrl.oForm.AllowEdits
=
Activer
End
If
Next
' gestion de l'enregistrement
'If oForm.Controls(Me.DefaultControlName).Enabled Then ' enregistrer
If
Not
oForm.Controls
(
Me.DefaultControlName
).Locked
Then
oForm.btnEnregistrer.Visible
=
True
oForm.Controls
(
Me.DefaultControlName
).SetFocus
oForm.btnModifier.Visible
=
False
Else
oForm.btnModifier.Visible
=
True
oForm.btnModifier.SetFocus
oForm.btnEnregistrer.Visible
=
False
End
If
End
Sub
Seuls les contrôles de la section Détail sont désactivés.
Le grisage provoqué par la propriété Enabled peut poser des problèmes de lecture avec certaines résolutions d'écran. On utilisera Locked à la place, qui a l'avantage et l'inconvénient de laisser les boutons de commande actifs.
Si vous souhaitez utiliser la propriété Enabled, la variante est disponible dans le code.
Pour que cette fonctionnalité soit opérationnelle, on y fait appel lors d'événements bien précis, comme lors du changement d'enregistrement qui déclenche l'événement Sur Activation. On en profite pour capter l'ID unique s’il existe.
Private
Sub
oForm_Current
(
)
' verrouille l'enregistrement dès son affichage sauf si c'est un nouvel enregistrement.
ActiverControleDetail oForm.NewRecord
' Capte l'id de l'enregistrement
If
Not
oForm.NewRecord
Then
Me.IdCurrentRecord
=
vIdCurrentRecord
End
If
End
Sub
Ici le choix de l'activation ou non des contrôles est conditionné par la valeur renvoyée par la propriété NewRecord du formulaire. Ceci permet d'être immédiatement en situation de saisie si l'ouverture du formulaire est réglée sur Ajout de données.
Docmd.OpenForm
"fAdherent"
,,,,acAdd
On continue le verrouillage/déverrouillage tout au long des actions qui le nécessitent.
VII-B. Créer▲
Pour la procédure Créer, nous commençons par effectuer un test de la propriété Dirty qui détermine si l'enregistrement est modifié. Dans ce cas, on déclenche la procédure d'enregistrement.
On se positionne systématiquement sur le nouvel enregistrement. Enfin on déverrouille les contrôles.
Private
Sub
btnCreer_Click
(
)
' se positionne sur le nouvel enregistrement
If
oForm.Dirty
Then
' si une modification est en cours
btnEnregistrer_Click
End
If
oForm.Recordset.AddNew
' positionnement sur le Nouvel enregistrement
ActiverControleDetail True
' déverrouille les contrôles.
End
Sub
Là encore, vous pouvez remplacer la méthode Addnew par la commande classique.
Docmd.GotoRecord
acActiveDataObject,,acNewRec
Dirty passe à True lorsqu'un contrôle lié est modifié, aussi bien par l'utilisateur que par code. Il sera donc incompatible avec les formulaires préremplis à moins d'utiliser la propriété Valeur par défaut pour faire le préremplissage.
VII-C. Modifier▲
Le bouton Modifier ne fait que déverrouiller les contrôles.
Private
Sub
btnModifier_Click
(
)
' déverrouille les contrôles
ActiverControleDetail True
End
Sub
VII-D. Enregistrer▲
Pour l'enregistrement, la propriété Dirty est utilisée pour déterminer l'état de l'enregistrement. La méthode du même nom permet de faire la sauvegarde. Dans tous les cas, les contrôles sont verrouillés.
Private
Sub
btnEnregistrer_Click
(
)
' enregistre la fiche courante
If
oForm.Dirty
Then
oForm.Dirty
=
False
End
If
ActiverControleDetail False
End
Sub
Dirty a la particularité d'être à la fois une propriété et une méthode. Vous pouvez consulter l'aide Microsoft ACCESS pour en savoir plus.
Vous pouvez employer la commande classique à la place de Dirty = False.
Docmd.RunCommand
acCmdSaveRecord
VII-E. Annuler▲
Pour l'annulation, nous testons toujours si l'enregistrement est modifié, puis nous utilisons la méthode Undo de l'objet Form.
Private
Sub
btnAnnuler_Click
(
)
' annule la dernière saisie
If
oForm.Dirty
Then
oForm.Undo
End
Sub
Vous pouvez utiliser la commande classique d'annulation à la place de la méthode Undo.
docmd.RunCommand
acCmdUndo
VII-F. Supprimer▲
La suppression utilise la commande classique Runcommand acCmdDeleteRecord plutôt que la méthode Delete du Recordset. En effet cette dernière pose quelque problème lors de la suppression du dernier enregistrement ou la confirmation de suppression est requise par l'utilisateur. En revanche si l'enregistrement est nouveau, la modification est annulée.
Private
Sub
btnSupprimer_Click
(
)
' suppression de l'enregistrement courant
On
Error
GoTo
errSub
If
oForm.NewRecord
Then
' c'est un nouveau record, message et annule
MsgBox
"Vous êtes sur la fiche réservée à la saisie, elle ne peut être supprimée."
, vbInformation
+
vbOKOnly
, fVersionProduit
(
)
oForm.Undo
Exit
Sub
End
If
'demande de confirmation
If
MsgBox
(
"Voulez-vous supprimer définitivement la fiche "
&
oForm.Caption
&
" ?"
, _
vbYesNo
+
vbDefaultButton2
, fVersionProduit
(
)) =
vbYes
Then
' Corr 18 / 07 / 2012 : Méthode Delete échoue sur le dernier record
DoCmd.RunCommand
acCmdDeleteRecord
'oForm.Recordset.Delete
TraitementApresSuppression ' lance le traitement postsuppression
On
Error
Resume
Next
' si pas de liste de recherche
oForm.LmRechercher.Requery
End
If
Exit
Sub
errSub
:
' après suppression du dernier enregistrement
If
Err
.Number
=
3021
Then
btnCreer_Click
Exit
Sub
End
If
If
Err
.Number
=
2010
Then
oForm.btnCreer.SetFocus
Resume
End
If
MsgBox
"erreur : "
&
Err
.Number
&
"---"
&
Err
.Description
Exit
Sub
Resume
End
Sub
VII-F-1. Cas spécial la procédure TraitementApresSuppression▲
Cette procédure va nous permettre de supprimer des enregistrements dans des tables filles lorsque la relation d'intégrité n'est pas active. En effet, volontairement ou non, beaucoup d'applications fonctionnent sans relation d'intégrité.
Les méthodes de la classe MELA surcharge les méthodes du formulaire, elles sont donc exécutées après le code de l'événement du formulaire. Il en découle que des événements qui suivent peuvent ne pas être déclenchés. C'est le cas lors de la suppression. L'événement AfterDelete ne se déclenche pas.
Pour contourner ce problème, on utilise une nouvelle procédure de la classe MELA (TraitementApresSuppression) pour lancer le traitement situé dans le formulaire (piAfterDelete).
Private
Sub
TraitementApresSuppression
(
)
'---------------------------------------------------------------------------------------
' Procedure : TraitementApresSuppression
' Author : Fabrice CONSTANS (MVP)
' Date : 13 / 07 / 2012
' Purpose : Lance des traitements après suppression, utile pour supprimer des enreg.
' connexes sur des tables sans relation d'intégrité référentielle.
' Cause : Après le delete de la classe (btnSupprimer_Click) les événements
' (AfterDelete) de la pile dans le formulaire ne sont pas joués.
' Parametres: N/A
' Return : N/A
'---------------------------------------------------------------------------------------
'
On
Error
Resume
Next
' s’il n'y a pas de piAfterDelete dans le formulaire on passe.
' Fonction qui supprime les enregistrements connexes
Call
oForm.piAfterDelete
' attention à bien faire une gestion d'erreur
' dans piAfterDelete.
End
Sub
Le fait d'isoler l'appel dans une procédure de la classe et de ne pas appeler directement piAfterDelete dans la méthode btnSupprimer_Click permet de traiter l'erreur remontée si le formulaire ne nécessite pas ce type de traitement.
Dans le formulaire livré avec la bibliothèque, voici comment se présente la procédure.
Public
Sub
piAfterDelete
(
)
'---------------------------------------------------------------------------------------
' Procedure : piAfterDelete (à placer dans le module du formulaire)
' Purpose : Après la suppression de la fiche par la classe Mela CBoutonNav celle-ci
' fait un appel à cette procédure publique pour faire des traitements Post.
' En effet il n'y a pas de déclenchement d'événement après.
' Parametres: vlId l'id de la fiche supprimée
' Return :
'---------------------------------------------------------------------------------------
'
Dim
db As
DAO.Database
Dim
i As
Integer
Set
db =
CurrentDb
db.Execute
"DELETE * FROM tInscription WHERE Id_Adherent="
&
mButtonNav.IdCurrentRecord
&
";"
, dbFailOnError
Set
db =
Nothing
On
Error
GoTo
0
Exit
Sub
End
Sub
On exécute une ou plusieurs requêtes de suppression en utilisant la propriété IdCurrentRecord qui conserve l'ID jusqu'au changement d'enregistrement.
Si vous avez mis en place la relation d'intégrité référentielle, vous n'avez pas besoin de mettre en place piAfterDelete. Vous pouvez cependant laisser l'appel dans la classe.
VII-G. Copier▲
Cette méthode copie les données de l'enregistrement courant vers un nouvel enregistrement.
Elle utilise l'objet RecordsetClone comme source et évite la copie de valeur des champs NumeroAuto.
Private
Sub
btnCopier_Click
(
)
' copier l'enregistrement courant
Dim
rst As
DAO.Recordset
Dim
fld As
DAO.Field
Dim
bkm As
Variant
If
oForm.NewRecord
And
Not
oForm.Dirty
Then
Exit
Sub
'pas de copie d'un enregistrement vide
btnEnregistrer_Click ' on enregistre
bkm =
oForm.Recordset.Bookmark
'repère l'enregistrement courant
Set
rst =
oForm.RecordsetClone
'ouvre le clone
rst.Bookmark
=
bkm 'positionne le curseur du clone sur le record repéré
btnCreer_Click 'mode création
oForm.Recordset.AddNew
For
Each
fld In
oForm.Recordset.Fields
If
Not
fld.Attributes
And
dbAutoIncrField Then
'sauf NumeroAuto
fld.Value
=
rst.Fields
(
fld.Name
)
End
If
Next
oForm.Recordset.Update
btnEnregistrer_Click 'enregistrement
On
Error
Resume
Next
' si pas de liste de recherche
oForm.LmRechercher.Requery
End
Sub
Les champs sont parcourus un à un pour être copiés dans le RecordSet de Form.
VIII. La navigation▲
VIII-A. Vers la fiche précédente▲
En tout premier lieu, il faut vérifier que l'enregistrement courant n'est pas en cours de modification. Si c'est le cas, un message demande à l'utilisateur s'il souhaite l'annulation.
Ensuite, la position dans la table est vérifiée. Si le curseur n'est pas au début, il y a déplacement sur l'enregistrement précédent. Sinon un message d'avertissement est émis.
Private
Sub
btnPrecedent_Click
(
)
' vers la fiche suivante
On
Error
GoTo
errSub
If
oForm.Dirty
Then
If
MsgBox
(
"Souhaitez-vous abandonner les modifications ?"
, _
vbInformation
+
vbYesNo
, fVersionProduit
(
)) =
vbYes
Then
btnAnnuler_Click ' oui Annuler
Else
Exit
Sub
End
If
End
If
If
oForm.CurrentRecord
>
1
Then
' pas sur le 1er record
DoCmd.GoToRecord
acActiveDataObject, oForm.Name
, acPrevious
Else
MsgBox
"Vous avez atteint la première fiche !"
, vbInformation
+
vbOKOnly
, fVersionProduit
(
)
End
If
Exit
Sub
errSub
:
MsgBox
Err
.Number
&
" -- "
&
Err
.Description
End
Sub
VIII-B. Vers la fiche suivante▲
Pour le déplacement vers la fiche suivante, c'est le même déroulement que précédemment à la différence que l'on teste si le curseur n'est pas à la fin.
Private
Sub
btnSuivant_Click
(
)
' vers la fiche précédente
On
Error
GoTo
errSub
If
oForm.Dirty
Then
If
MsgBox
(
"Souhaitez-vous abandonner les modifications ?"
, _
vbInformation
+
vbYesNo
, fVersionProduit
(
)) =
vbYes
Then
btnAnnuler_Click ' oui Annuler
Else
Exit
Sub
End
If
End
If
If
oForm.CurrentRecord
<
oForm.RecordsetClone.RecordCount
Then
' pas sur le dernier record
DoCmd.GoToRecord
acActiveDataObject, oForm.Name
, acNext
Else
MsgBox
"Vous avez atteint la dernière fiche !"
, vbInformation
+
vbOKOnly
, fVersionProduit
(
)
End
If
Exit
Sub
errSub
:
MsgBox
Err
.Number
&
" -- "
&
Err
.Description
End
Sub
VIII-C. Rechercher▲
Pour cette méthode vous devez avoir au préalable paramétré la liste modifiable LmRechercher comme ceci :
- la source est basée sur la même table que le formulaire ;
- la 1re colonne de la source est l'ID, le numéro unique de l'enregistrement ;
- la propriété Colonne liée est 1.
Ainsi
Private
Sub
LmRechercher_AfterUpdate
(
)
On
Error
GoTo
errSub
If
Not
IsNull
(
oForm.LmRechercher
) Then
oForm.Recordset.FindFirst
oForm.LmRechercher.Recordset.Fields
(
0
).Name
&
"="
&
oForm.LmRechercher
End
If
Exit
Sub
errSub
:
MsgBox
Err
.Number
&
" -- "
&
Err
.Description
End
Sub
VIII-D. Fermeture du formulaire▲
La fermeture ne se résume pas à un simple Close du formulaire, il faut veiller à bien libérer toutes les variables de la classe.
Private
Sub
btnFermer_Click
(
)
' ferme le formulaire
On
Error
GoTo
errSub
If
oForm.Dirty
Then
' si enreg. modifier
If
MsgBox
(
"Voulez-vous enregistrer la fiche avant de fermer ?"
, _
vbInformation
+
vbYesNo
, fVersionProduit
(
)) =
vbYes
Then
oForm.Dirty
=
False
' enregistre
Else
oForm.Undo
' annule
End
If
End
If
DoCmd.Close
acForm, oForm.Name
' ferme le formulaire
Exit
Sub
errSub
:
MsgBox
Err
.Number
&
" -- "
&
Err
.Description
End
Sub
Dans la classe, il existe une méthode privée qui est toujours exécutée à la fin. Il s'agit de Class_Terminate qui peut servir à libérer toutes les variables objet.
Private
Sub
Class_Terminate
(
)
' libère les variables
On
Error
Resume
Next
Set
btnCreer =
Nothing
Set
btnModifier =
Nothing
Set
btnEnregistrer =
Nothing
Set
btnCopier =
Nothing
Set
btnAnnuler =
Nothing
Set
btnSupprimer =
Nothing
Set
btnPrecedent =
Nothing
Set
btnSuivant =
Nothing
Set
btnFermer =
Nothing
Set
LmRechercher =
Nothing
Set
oForm =
Nothing
End
Sub
La classe est libérée lors de l'événement OnClose du formulaire.
IX. Piloter un autre formulaire▲
Rien de plus simple que d'implémenter la classe dans un autre formulaire.
Copier le code du formulaire fourni avec la bibliothèque.
Private
Sub
Form_Open
(
Cancel As
Integer
)
' mise en fonction de la classe
Set
mButtonNav =
MELA.CreerMELA
(
)
' paramétrage de la classe avec le formulaire
Set
mButtonNav.Form
=
Me
' Dans le cas où l'on veut fixer un contrôle par défaut différent
' de celui défini par la classe.
'mButtonNav.DefaultControlName = "Prenom"
End
Sub
Ensuite copier les boutons.
Exécutez ! Ça fonctionne !
X. Conclusion▲
L'avantage d'une classe M.E.L.A. c'est son extrême portabilité, sa rapidité de déploiement ainsi que sa maintenance aisée.
Imaginez !
Le fonctionnement de l'annulation ne vous plait pas. Vous n'avez qu'à modifier la classe et l'ensemble de votre application bénéficie instantanément de ce changement.
Vous souhaitez ajouter une fonctionnalité dans certains formulaires ? Qu'à cela ne tienne, modifiez la classe et rajoutez le ou les boutons dans les formulaires concernés. Seuls ceux-ci seront impactés.
Une classe n'est pas très complexe à mettre en œuvre. Cependant il faut de la rigueur et être familiarisé avec le concept objet. Notez que lorsque vous manipulez du code dans un formulaire ou un état, vous manipulez un module de classe de formulaire ou d'état.
La bibliothèque M.E.L.A. est disponible en téléchargement ; si vous avez une version de Microsoft ACCESS 97 vous pouvez télécharger le code au format texte ici. https://loufab.developpez.com/tutoriels/access/classe-mela/CodeMelaTexte.zip ou bien si vous êtes équipé de 2007 et supérieur la bibliothèque est téléchargeable ici : https://loufab.developpez.com/tutoriels/access/classe-mela/MELA.zip
XI. Incompatibilité▲
Il a été constaté que certains types de champs spécifiques comme les Multivalués (Pièces jointes) provoquent des dysfonctionnements de la classe. Notamment lors de l'enregistrement.
XII. Liens utiles▲
Pour en savoir plus sur les modules de classes dans ACCESS, je vous conseille quelques lectures.
https://access.developpez.com/cours/?page=langagevba#vbaclasses