Création et utilisation de classes personnalisées en VB 6.0 et VBA (partie 1)

Pierre Fauconnier

Commentez Grâce aux classes personnalisées, créez vos propres objets, maintenez facilement votre code, réutilisez rapidement votre code dans de nouvelles applications ♪

- Titre : Création et utilisation de classes personnalisées en VB 6.0 et VBA (partie 1)

- Auteur : Pierre Fauconnier

- Parution : 16 mai 2008

- Mise à jour : 27 janvier 2014

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2008 Pierre Fauconnier. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.

Sommaire

- I. Introduction

- I-A. Pourquoi utiliser des classes personnalisées

- I-B. Objectifs

- I-C. Notions abordées dans la partie 1

- I-D. Notions abordées dans la partie 2

- I-E. Notions abordées dans la partie 3

- I-F. Prérequis

- II. Notions d'objet

- II-A. Les objets de la vie courante

- II-B. Les objets informatiques

- II-B-1. Quelques objets informatiques

- II-B-2. L'explorateur d'objets en VB et VBA

- III. Utilisation de variables simples, puis d'un « pseudoobjet », le type personnalisé

- III-A. Manipulation des données d'un contact sans utilisation d'un type personnalisé

- III-A-1. Limites de l'utilisation de variables simples

- III-B. Qu'est-ce qu'une variable de type personnalisé (structure) et quand créer un type personnalisé?

- III-C. Création d'un type personnalisé

- III-D. Utilisation de mon type personnalisé

- III-E. Différences entre l'utilisation de variables et l'utilisation d'un type personnalisé

- III-F. Limites de l'utilisation d'un type personnalisé

- III-G. Gestion de plusieurs données s'appuyant sur une structure

- IV. Une classe pour gérer des contacts

- IV-A. Notion de classe,notion d'objet, notion d'encapsulation

- IV-A-1. Notion de classe

- IV-A-2. Notion d'objet

- IV-A-3. Notion d'encapsulation

- IV-B. Création de notre première classe

- IV-B-1. Module de classe

- IV-B-1-a. Ajout d'un module de classe en VB 6.0

- IV-B-1-b. Ajout d'un module de classe en VBA

- IV-B-2. Création de propriétés simples pour notre classe

- IV-B-3. Utilisation de notre classe au sein d'un code

- IV-B-4. Conclusions

- IV-C. Création de vraies propriétés

- IV-C-1. Notions de propriétés en lecture/écriture, en lecture seule et en écriture seule

- IV-C-2. Propriétés en lecture/écriture

- IV-C-3. Propriétés en lecture seule

- IV-C-4. Propriétés en écriture seule

- IV-C-5. Propriétés en lecture/écriture qui basculent en lecture seule

- IV-C-6. Utilisation de Property Get et Property Let

- IV-C-6-a. La propriété « Nom » en lecture-écriture

- IV-C-6-a-i. Fonctionnement du code

- IV-C-6-a-ii. Contrôle de la saisie du nom en vue de sa modification

- IV-C-6-a-iii. Adaptation du code: Transformation des variables en propriétés

- IV-C-6-a-iv. La propriété « Age » en lecture seule

- IV-C-6-a-v. Un petit mot sur l'explorateur d'objets

- IV-C-6-a-vi. Conclusions

- IV-D. Déclenchement d'erreurs au sein de notre classe

- IV-D-1. Génération d'une erreur au sein de la classe

- IV-E. Mettre des événements à disposition du code appelant

- IV-E-1. Exemples d'événements mis à disposition par des classes d'objets

- IV-E-2. Création d'un événement pour notre classe personnalisée

- IV-E-2-a. Création et gestion d'un événement sans paramètre

- IV-E-2-a-i. Création de l'événement

- IV-E-2-a-ii. Gestion de l'événement dans le code appelant

- IV-E-2-b. Création d'un événement qui passe un paramètre au code appelant

- IV-E-2-b-i. Exemples d'événements avec paramètre(s)

- IV-E-2-b-ii. Création de l'événement au sein de la classe

- IV-E-2-b-iii. Utilisation de l'événement et de son paramètre dans le code appelant

- IV-E-2-c. Modification du déroulement du code dans la classe grâce à un paramètre « décisionnel »

- IV-E-2-c-i. Création d'un paramètre décisionnel

- IV-E-2-c-ii. Gestion de l'événement dans le code appelant

- V. Conclusions

- VI. Remerciements

I - Introduction

I-A - Pourquoi utiliser des classes personnalisées

On est souvent confronté, lors de l'utilisation de VB ou de VBA, au problème de « réinventer la roue » à chaque nouveau travail que l'on développe.

Si je prends l'exemple d'applications liées à la gestion d'une entreprise, nombre de nos classeurs Excel, de nos bases de données Access, voire de nos applications VB, gèrent les données de contacts.

L'utilisation de classes personnalisées va permettre un gain de temps appréciable en favorisant la réutilisation et la maintenance du code. De plus, les classes personnalisées permettent la mise à disposition d'objets complexes à des programmeurs qui pourront « se borner » à utiliser les propriétés et méthodes qu'une classe personnalisée met à leur disposition.

I-B - Objectifs

Ce tutoriel en trois parties a pour ambition de vous expliquer le fonctionnement des classes personnalisées en VB 6.0 ou en VBA, de vous démontrer que la mise au point de classes performantes n'est pas un travail impossible, et de vous prouver que les classes personnalisées permettent la mise à disposition d'autres programmeurs d'un code qui les soulage de la « réinvention de la roue ».

I-C - Notions abordées dans la partie 1

Dans cette partie, nous allons manipuler principalement les concepts liés aux propriétés d'un objet.

Pour cela, nous allons créer ce que l'on appelle une « classe métier », c'est-à-dire une classe qui reproduit en informatique un objet ou un concept que l'on voudra manipuler.

Notions abordées dans la partie 1

- Qu'est-ce qu'un objet informatique

- Qu'est-ce qu'une propriété

- Propriété en lecture seule, en écriture seule, en lecture écriture

- Encapsulation

- Génération des erreurs dans une classe et gestion par le code appelant

- Génération des événements et gestion par le code appelant.

I-D - Notions abordées dans la partie 2

Dans un deuxième temps, nous utiliserons notre classe « métier » vue en partie 1 en la liant à différentes sources de données au travers d'une nouvelle classe d'accès aux données.

Notions abordées dans la partie 2

- Création d'un objet personnalisé au travers d'une classe d'accès aux données

- Liaison d'un objet personnalisé à une source pour la lecture des données

- Liaison d'un objet personnalisé à une source pour la modification des données du fichier source

- Portabilité du jeu des classes

- Utilisation de données d'une base Access dans un fichier Excel grâce aux classes personnalisées

I-E - Notions abordées dans la partie 3

La troisième partie nous permettra d'aborder une classe liant propriétés, fonctions et méthodes.

I-F - Prérequis

L'utilisation et la compréhension de ce cours requièrent une bonne connaissance de base en VB ou VBA.
Vous devez notamment savoir déclarer et utiliser des variables et comprendre la notion de portée d'utilisation d'une variable.
Vous devez savoir déclarer et utiliser des fonctions personnalisées et des procédures requérant l'emploi de paramètres.
Il est également préférable que l'environnement de développement de VB ou VBE (Visual Basic Editor) vous soit familier, notamment les possibilités de débogage du code.

II - Notions d'objet

II-A - Les objets de la vie courante

Dans la vie courante, nous utilisons quotidiennement des objets.
Nous les définissons par un ensemble de caractéristiques et, généralement, nous pouvons agir avec ces objets.

Ainsi, si je regarde une boite à chaussures, je peux la définir.
Elle est noire, elle fait 40x18x12cm…
Je peux l'ouvrir, la fermer, la remplir, la vider…

Ma voiture possède quatre roues, un volant, un moteur de telle cylindrée.
Je peux la faire avancer, la stopper…

Dans ces exemples, nous discernons que nos objets possèdent des caractéristiques qui leur sont propres, et que nous pouvons définir des actions réalisables sur ou avec ces objets.

II-B - Les objets informatiques

Un objet informatique est similaire à un objet de la vie courante, et nous en utilisons quotidiennement dans nos codes VBA.

Ces objets possèdent des caractéristiques qui leur sont propres et nous pouvons agir sur ou avec ces objets.

Les caractéristiques d'un objet informatique sont appelées les propriétés de l'objet.
Les actions que nous pouvons réaliser sur ou grâce à ces objets sont appelées des méthodes.

Lorsque nous souhaitons utiliser un objet, nous devons donc connaître les propriétés de l'objet mises à notre disposition, et nous devons connaître aussi les méthodes utilisables sur cet objet.

II-B-1 - Quelques objets informatiques

En VB6, nous utilisons tout le temps des objets. Tous les contrôles que nous plaçons sur un formulaire (qui est lui-même un objet) sont des objets.

En VBA pour Excel, nous manipulons les objets Range (plage de cellules), Worksheet (feuille de calcul), Workbook (classeur)…

En VBA pour Word, nous manipulerons les objets Word, Document…

En VBA pour Access, nous manipulerons les objets Form, Database, Recordset

En VB6 ou VBA, nous référençons parfois des bibliothèques d'objets externes, pour manipuler des objets particuliers. Par exemple, l'utilisation de la bibliothèque Microsoft Scripting Runtime permet de manipuler des objets fichier, dossier… non utilisables par défaut dans VB6 ou VBA.

II-B-2 - L'explorateur d'objets en VB et VBA

L'explorateur d'objets permet… d'explorer les objets, c'est-à-dire d'en afficher les propriétés, méthodes et événements.

En VB 6.0 comme en VBA, l'affichage de l'explorateur d'objets est possible via le menu Affichage/Explorateur d'objets ou par le raccourci F2.

Lorsque l'explorateur d'objets est affiché, on peut alors choisir d'afficher les objets de toutes les bibliothèques disponibles du projet, ou de restreindre la liste des objets à une bibliothèque particulière.

On peut alors choisir l'objet pour lequel on souhaite afficher les propriétés, méthodes et événements.

L'objet Form de la bibliothèque VB

L'objet Workbook de la bibliothèque Excel

III - Utilisation de variables simples, puis d'un « pseudoobjet », le type personnalisé

Lorsque l'on doit utiliser un « objet » informatique, on a trois possibilités. La première consiste à créer autant de variables que de propriétés.

La seconde utilise un type personnalisé.

La troisième, enfin, passe par l'utilisation d'une classe personnalisée.

Avant d'aborder la création d'une classe personnalisée, je vais brièvement exposer les deux premières méthodes, ne serait-ce que pour démontrer la puissance d'une classe par rapport aux variables simples et aux types personnalisés.

Pour ceux qui connaissent l'histoire des trois petits cochons, on peut imaginer la première solution comme étant la maison de paille, le type personnalisé étant la maison de bois, la classe personnalisée comme la maison de briques…

III-A - Manipulation des données d'un contact sans utilisation d'un type personnalisé

Si je souhaite manipuler les données d'un contact, je dois déclarer autant de variables que j'ai de données (champs) pour mon contact, puis leur attribuer les données de mon contact, et enfin les utiliser, par exemple au sein d'une procédure qui affiche un message à l'écran.

Sub UtiliserContact()
Dim ContactNom As String
Dim ContactPrenom As String
Dim ContactAdresse As String
Dim ContactCP As String
Dim ContactLocalite As String
Dim ContactDateNaissance As Date

ContactNom = "Fauconnier"
ContactPrenom = "Pierre"
ContactAdresse = "20, Jevoumont"
ContactCP = "4910"
ContactLocalite = "Theux"
ContactDateNaissance = DateSerial(1966, 12, 26)

AfficherMessageContact ContactNom, ContactPrenom, ContactAdresse, _
ContactCP, ContactLocalite, ContactDateNaissance
End Sub

Sub AfficherMessageContact(Nom As String, Prenom As String, Adresse As String, _
CP As String, Localite As String, DateNaissance As Date)
MsgBox "Bonjour " & Prenom & " " & Nom & "." & vbCrLf & _
"Vous habitez " & Adresse & " à " & CP & " " & Localite & "." & vbCrLf & _
"Vous êtes né le " & Format(DateNaissance, "dddd dd mmmm yyyy"), vbOKOnly, "Salutations"
End Sub

Ce qui me donnera le résultat suivant à l'écran.

III-A-1 - Limites de l'utilisation de variables simples

Les limites du code présenté ci-dessus sont évidentes.

Chaque fois que je souhaite utiliser les données d'un autre contact, je dois réinitialiser mes variables, avec le risque d'en oublier, et donc de mélanger les données de mes contacts.

De plus, passer les variables à une procédure ou une fonction qui doit les utiliser n'est déjà pas pratique avec six variables…

...
AfficherMessageContact ContactNom, ContactPrenom, ContactAdresse, _
ContactCP, ContactLocalite, ContactDateNaissance
...

Sub AfficherMessageContact(Nom As String, Prenom As String, Adresse As String, _
...
End Sub

Imaginez ce que cela donnerait avec un objet défini par vingt ou trente champs…

Enfin, si je veux utiliser simultanément les données de plusieurs contacts, je vais devoir créer autant de variables ContactNom1, ContactNom2… que j'ai de contacts à gérer simultanément. Mission impossible !!

Je pourrais bien entendu également utiliser un tableau indexé pour gérer les données de x contacts, mais ce ne serait guère pratique pour les manipuler.

Dim Contacts(9, 5)

Contacts(0, 0) = "Fauconnier"
Contacts(0, 1) = "Pierre"
...
...
Contacts(1, 0) = "Dupont"
Contacts(1, 1) = "Alain"

III-B - Qu'est-ce qu'une variable de type personnalisé (structure) et quand créer un type personnalisé?

Une variable de type personnalisé, appelé aussi structure, est un groupe de plusieurs variables simples dont le type personnalisé concrétise la structure.

Un type personnalisé sera créé lorsqu'il s'avérera nécessaire de manipuler plusieurs variables comme si elles représentaient une entité.
L'utilisation de variables de type personnalisé répondra partiellement aux objections émises plus haut quant à l'utilisation de variables simples.

III-C - Création d'un type personnalisé

Créer un type personnalisé de variable, c'est définir la structure de la variable, préciser quelles sont les variables simples qui seront groupées.

Les variables utilisées plus haut dans le cours constituent la structure de mon type personnalisé.
Je vais les réutiliser en les modifiant légèrement et créer, ou plutôt définir mon type personnalisé avec le code suivant :

Public Type tpContact
Nom As String
Prenom As String
Adresse As String
CP As String
Localite As String
DateNaissance As Date
End Type

Remarquez qu'ici, j'ai supprimé le préfixe Contact pour mes variables, puisqu'elles sont définies à l'intérieur de mon type personnalisé.

Ce code se place toujours en tête de module standard. Il ne peut jamais être utilisé dans le corps d'une procédure ou d'une fonction.

III-D - Utilisation de mon type personnalisé

Je peux maintenant utiliser ce type personnalisé dans mon code

Sub UtiliserContact()
Dim Contact As tpContact

With Contact
.Nom = "Fauconnier"
.Prenom = "Pierre"
.Adresse = "20, Jevoumont"
.CP = "4910"
.Localite = "Theux"
.DateNaissance = DateSerial(1966, 12, 26)
End With

AfficherMessageContact Contact
End Sub

Sub AfficherMessageContact(ByRef Contact As tpContact)
With Contact
MsgBox "Bonjour " & .Prenom & " " & .Nom & "." & vbCrLf & _
"Vous habitez " & .Adresse & " à " & .CP & " " & .Localite & "." & vbCrLf & _
"Vous êtes né le " & Format(.DateNaissance, "dddd dd mmmm yyyy"), vbOKOnly, "Salutations"
End With
End Sub

Ce qui me donne le même résultat que tout à l'heure.

III-E - Différences entre l'utilisation de variables et l'utilisation d'un type personnalisé

Après avoir créé mon type personnalisé, j'utilise une variable de ce type au travers de mon code, en étant certain d'emporter avec moi la structure complète de ma variable.

De plus, l'écriture et la lecture du code sont plus simples, notamment lorsque je dois passer ma variable en argument d'une fonction ou d'une procédure.

...
AfficherMessageContact Contact
End Sub

Sub AfficherMessageContact(Contact As tpContact)
...

est plus simple à écrire et à lire que

...
AfficherMessageContact ContactNom, ContactPrenom, ContactAdresse, _
ContactCP, ContactLocalite, ContactDateNaissance
End Sub

Sub AfficherMessageContact(Nom As String, Prenom As String, Adresse As String, _
CP As String, Localite As String, DateNaissance As Date)
...

Enfin, la maintenance de mon code est simplifiée lorsque la structure de ma variable change.

En effet, si je dois ajouter un « champ » à mon type personnalisé, les modifications se limitent à l'affectation du nouveau champ et à la récupération de sa valeur. Les passages et récupérations d'un paramètre contact ne changent pas.

III-F - Limites de l'utilisation d'un type personnalisé

L'utilisation d'un type personnalisé ne permet pas le traitement des données à l'intérieur de sa structure.
La vérification et le traitement des données sont délégués au code appelant, ce qui limite considérablement la portabilité du type.

En effet, dans l'exemple de l'utilisation de ma variable Contact, je dois déléguer au code appelant la vérification de date de naissance. Nulle part dans la structure Type… End Type, je n'ai la possibilité d'effectuer des actions relatives à la saisie d'informations.

De plus, la gestion des erreurs est également déléguée au code appelant.

Enfin, je ne peux gérer aucun événement relatif à un type personnalisé.

III-G - Gestion de plusieurs données s'appuyant sur une structure

Pour gérer plusieurs contacts, je peux bien entendu déclarer autant de variables de type tpContact que j'ai de contacts à manipuler, mais cette technique n'est pas très pratique.

Dim Contact1 As tpContact
Dim contact2 As tpContact

Dans ce cas, je pourrais également utiliser un tableau, ce qui simplifierait déjà l'utilisation et la manipulation de plusieurs contacts.

Dim Contacts(9) As tpContact

With Contacts(0)
.Nom = "Fauconnier"
.Prenom = "Pierre"
End With

With Contacts(1)
.Nom = "Dupont"
.Prenom = "Alain"
End With

IV - Une classe pour gérer des contacts

L'utilisation d'une classe personnalisée va balayer ces restrictions et me donner la possibilité de mettre à disposition du code utilisateur de ma classe des propriétés, méthodes, erreurs et événements.

Dans la suite du cours, nous allons avancer petit à petit dans la création d'une classe permettant de gérer des contacts.

La classe que nous allons créer dans un premier temps ne sera vraiment pas professionnelle, mais nous améliorerons le code petit à petit pour intégrer calmement les notions essentielles à la création de classes sûres et portables.

IV-A - Notion de classe,notion d'objet, notion d'encapsulation

IV-A-1 - Notion de classe

La classe représente la structure d'un objet. En cela, elle est comparable aux lignes de définition Type… End Type.

IV-A-2 - Notion d'objet

Un objet est une variable dont le type s'appuie sur une classe. Que ce soit en VB ou en VBA, cette classe peut être personnalisée ou mise à disposition par l'application.

VB 6.0 comme VBA permettent d'utiliser des références externes. Dans VB 6.0, le menu Projet/Références… permet d'ajouter une référence. Dans VBA, c'est via le menu Outils/Références… que vous avez la possibilité d'ajouter une référence externe.

Ainsi, en Excel, Dim MaFeuille As Worksheet créera un objet appelé MaFeuille qui s'appuiera sur la classe qui définit les objets de type WorkSheet, c'est-à-dire les feuilles de calcul.

IV-A-3 - Notion d'encapsulation

Nous devrons toujours veiller, lorsque nous construisons une classe, à ce qu'elle soit « encapsulée ».
Cela veut dire qu'à aucun moment, notre classe ne doit avoir besoin de données externes pour fonctionner. Dans les faits, cela veut dire que notre classe ne peut jamais faire appel à une variable extérieure à la portée du module de classe.

De même, une classe ne devrait jamais donner la main à l'utilisateur via par exemple un msgbox ou un inputbox. Il faudra donc développer à l'intérieur du module de classe des événements et des gestions d'erreurs qui rendront la main au code appelant.

Ces notions seront vues dans la suite du cours.

IV-B - Création de notre première classe

La classe que nous allons créer va nous permettre de manipuler un objet contact.

IV-B-1 - Module de classe

IV-B-1-a - Ajout d'un module de classe en VB 6.0

En VB 6.0, nous créerons une classe personnalisée en ajoutant un module de classe via le menu Projet/Ajouter un module de classe

Nous choisissons alors d'ajouter le module de classe parmi les différentes icônes proposées.

Le nouveau module de classe apparaît dans l'arborescence de notre projet, sous le nœud Modules de classe

La première chose que nous allons faire, c'est renommer le module, car c'est lui qui déterminera le nom de notre classe.
Ce nom sera donc choisi avec soin, en évitant notamment les noms réservés par VB 6.0.

IV-B-1-b - Ajout d'un module de classe en VBA

En VBA, nous créerons une classe personnalisée par l'ajout d'un module de classe au sein de notre projet.

Les illustrations proviennent d'un VBA pour EXCEL, mais vous pouvez les transposer sans problème sur Word, PowerPoint, Access, Outlook…

Pour ajouter un module de classe, utilisons le menu Insertion/Module de classe.
Nous pouvons aussi utiliser le bouton d'outil d'ajout de module.

Un nouveau module apparaît dans l'arborescence du projet, dans le nœud Modules de classe.

La première chose que nous allons faire, c'est renommer le module, car c'est lui qui déterminera le nom de notre classe.
Ce nom sera donc choisi avec soin, en évitant notamment les noms déjà utilisés par VBA.

IV-B-2 - Création de propriétés simples pour notre classe

Ce que nous allons voir maintenant est à proscrire en utilisation professionnelle de classes personnalisées.

La seule raison de leur mention ici est de permettre une prise en main « pas à pas » des notions nécessaires à la réalisation d'une classe performante.

Pour ajouter des propriétés simples à notre classe, il nous suffit de déclarer des variables publiques au sein de notre module de classe.

Pour cela, nous pouvons reprendre la structure de notre type personnalisé.
Le code est le suivant :

Public Nom As String
Public Prenom As String
Public Adresse As String
Public CP As String
Public Localite As String
Public DateNaissance As Date
Public Sexe As String

Ces variables publiques, parce que déclarées au sein d'un module de classe, sont des propriétés en lecture/écriture pour les objets qui s'appuieront sur notre classe.

Le terme variable publique est utilisé ici abusivement. La déclaration de variables via Public … dans un module de classe ne crée pas des variables publiques utilisables partout dans le projet, mais bien des propriétés publiques mises à disposition du code appelant par la classe dont l'objet est issu.

À l'extérieur du module de classe, ces propriétés devront être préfixées du nom de l'objet issu de la classe personnalisée.

IV-B-3 - Utilisation de notre classe au sein d'un code

Pour utiliser un objet de type cContact au sein d'un code, nous utiliserons la même technique que pour notre type personnalisé.

En reprenant le code utilisé plus haut, nous avons deux modifications à réaliser pour utiliser notre classe plutôt que notre type personnalisé.
Voici le code :

Sub UtiliserContact()
Dim Contact As New cContact

With Contact
.Nom = "Fauconnier"
.Prenom = "Pierre"
.Adresse = "20, Jevoumont"
.CP = "4910"
.Localite = "Theux"
.DateNaissance = DateSerial(1966, 12, 26)
.Sexe = "masculin"
End With

AfficherMessageContact Contact
End Sub

Sub AfficherMessageContact(Contact As cContact)
With Contact
MsgBox "Bonjour " & .Prenom & " " & .Nom & "." & vbCrLf & _
"Vous habitez " & .Adresse & " à " & .CP & " " & .Localite & "." & vbCrLf & _
"Vous êtes né le " & Format(.DateNaissance, "dddd dd mmmm yyyy") & ".", vbOKOnly, "Salutations"
End With
End Sub

La saisie semi-automatique permet, ici aussi, d'utiliser la liste des objets disponibles en fonction de la saisie.

Et voici les modifications à réaliser :

Le résultat est toujours le même, à savoir l'affichage d'un message de bienvenue.

Attention ! Bien que nous ayons déclaré nos variables publiques, il n'est pas possible de les utiliser telles quelles ailleurs que dans notre module de classe.

Ainsi, le code Debug.Print DateNaissance à l'extérieur de notre code provoquera une erreur de compilation.

IV-B-4 - Conclusions

Nous venons de voir comment créer notre première classe.
Il n'y a pas de quoi fouetter un chat. Nous ne savons pas mieux contrôler les données avec ce code qu'avec l'utilisation d'un type personnalisé.

Néanmoins, nous avons vu comment créer un module de classe, lui affecter un nom qui sera celui de la classe que nous créons, et nous avons ajouté des variables « publiques » qui sont des propriétés en lecture/écriture disponibles pour les objets de notre classe.

IV-C - Création de vraies propriétés

Déclarer des variables publiques au sein d'un module de classe est certes rapide, mais peu intéressant.
Pour pouvoir manipuler, contrôler et travailler avec les propriétés d'un objet, nous allons déclarer des … propriétés.

IV-C-1 - Notions de propriétés en lecture/écriture, en lecture seule et en écriture seule

IV-C-2 - Propriétés en lecture/écriture

Une propriété en lecture/écriture est une propriété qui peut être lue ET modifiée.
Exemple: La propriété DateNaissance doit être en lecture pour pouvoir la récupérer et en écriture pour pouvoir la définir pour un contact particulier.

IV-C-3 - Propriétés en lecture seule

Une propriété en lecture seule ne peut être ni définie ni modifiée.
Exemple: si je dote ma classe d'une propriété Age, elle sera en lecture seule, car elle dépendra de la propriété DateNaissance, qui sera, elle, en lecture/écriture.

IV-C-4 - Propriétés en écriture seule

Une propriété pourrait être en écriture seule, et donc définissable, mais non lisible.
Je ne vois pas d'exemple concret pour ce cas.

IV-C-5 - Propriétés en lecture/écriture qui basculent en lecture seule

Certaines propriétés doivent être définies une fois, puis être verrouillées pour ne plus pouvoir être modifiées.
Elles sont dès lors en lecture/écriture, mais le code les fera basculer en lecture seule pour éviter leur modification.
Exemple: Une propriété ID qui doit être définie, mais qui ne peut par la suite être modifiée. C'est le cas des clés primaires, pour ceux qui connaissent.
Nous verrons ce cas particulier dans la suite du cours.

IV-C-6 - Utilisation de Property Get et Property Let

Property Get et Property Let permettent la création de propriétés au sein de la classe.

Property Get permet la lecture d'une propriété, Property Let permet l'écriture d'une propriété.

IV-C-6-a - La propriété « Nom » en lecture-écriture

La création d'une propriété passe par deux étapes : la définition d'une variable privée, et la création des blocs Property Get et Property Let.

Modifions le code de notre classe en modifiant la ligne

Public Nom As String

pour définir une variable privée

Private mNom As String

Remarquez que ma variable a changé de nom, par l'ajout d'un préfixe « m » précisant la limite de sa portée au module.
Cette variable ne sera donc pas manipulable par le code appelant qui utilisera notre objet.

La définition du nom de notre contact s'effectuera via le code

Property Get Nom() As String
' Propriété en lecture
Nom = mNom
End Property

Property Let Nom(Nom As String)
' Propriété en écriture
mNom = Nom
End Property

Notons que ces modifications n'ajoutent actuellement rien à notre classe par rapport à l'utilisation d'une variable déclarée avec Public. Patience…

Notez aussi que les propriétés ne sont pas déclarées Public, car elles le sont d'office. C'est leur raison d'être !

Notons cependant que des propriétés peuvent être déclarées Private pour n'être utilisables qu'à l'intérieur du module de classe.

IV-C-6-a-i - Fonctionnement du code

Pour nous aider à comprendre comment le code fonctionne avec les propriétés d'une classe, nous allons démarrer le code appelant vu plus haut avec la méthode « pas à pas », en mettant un point d'arrêt sur la ligne With Contact de la procédure UtiliserConctact

Puis nous démarrons le code. L'exécution s'arrête sur With contact. Nous sommes dans le module standard qui contient la procédure UtiliserContact.

Nous pressons sur F8 pour passer à la ligne suivante, puis encore sur F8. Le contrôle du code passe dans le module de classe pour s'arrêter sur Property Let.

L'infobulle nous montre que le paramètre Nom possède la valeur « Fauconnier » passée par le module standard.
Nous connaissons déjà le fonctionnement des paramètres, puisqu'il est identique dans une propriété à celui d'une procédure ou d'une fonction dans VBA. Rien de nouveau sous le soleil de ce côté-là.

Pressons F8. Le code repasse dans le module standard, et pressons F8 jusqu'à nous arrêter sur la ligne End Property. L'infobulle nous montre que la valeur du paramètre a été passée à la variable privée mNom.

Pressons F8 plusieurs fois pour arriver sur la ligne MsgBox… de la procédure AfficherMessageContact

Puis pressons F8, pour constater que le code repasse dans le module de classe, et s'arrête sur la ligne Property Get Nom(). Le code va récupérer la valeur de la variable mNom

Pour la passer au paramètre Nom

Ce qui permettra l'affichage du nom au sein du MsgBox.

On comprend donc maintenant le fonctionnement et le parcours du code au sein des propriétés de la classe. Mais à quoi cela sert-il ?

IV-C-6-a-ii - Contrôle de la saisie du nom en vue de sa modification

L'intérêt de passer par Property Let pour définir le nom de notre contact, c'est que nous pouvons ajouter du code au sein de la propriété, ce qui était impossible avec une variable de type personnalisé ou une variable publique de notre module de classe.

Imaginons que nous voulons que les noms de nos contacts soient systématiquement en majuscules. Grâce à Property Let, nous n'aurons aucune difficulté à satisfaire à cette exigence.
Modifions le code comme suit :

Property Let Nom(Nom As String)
' Propriété en écriture
mNom = UCase(Nom)
End Property

En plaçant un point d'arrêt sur End Property de cette propriété,

Nous verrons, grâce à l'infobulle, que mNom contient le nom en majuscules.

À la lecture de la propriété, le paramètre Nom recevra la valeur de mNom pour afficher, cette fois, un message différent de ceux vus précédemment.

IV-C-6-a-iii - Adaptation du code: Transformation des variables en propriétés

Maintenant que nous avons compris l'intérêt d'utiliser des propriétés plutôt que des variables, nous pouvons modifier le code en transformant toutes les variables en propriétés.
Toutes ces propriétés sont en lecture/écriture.

Option Explicit

Private mNom As String
Private mPrenom As String
Private mAdresse As String
Private mCP As String
Private mLocalite As String
Private mDateNaissance As Date
Private mSexe As String

Property Get Nom() As String
' Propriété en lecture
Nom = mNom
End Property

Property Let Nom(Nom As String)
' Propriété en écriture
mNom = UCase(Nom)
End Property

Property Get Prenom() As String
' Propriété en lecture
Prenom = mPrenom
End Property

Property Let Prenom(Prenom As String)
' Propriété en écriture
mPrenom = UCase(Prenom)
End Property

Property Get Adresse() As String
' Propriété en lecture
Adresse = mAdresse
End Property

Property Let Adresse(Adresse As String)
' Propriété en écriture
mAdresse = UCase(Adresse)
End Property

Property Get CP() As String
' Propriété en lecture
CP = mCP
End Property

Property Let CP(CP As String)
' Propriété en écriture
mCP = UCase(CP)
End Property

Property Get Localite() As String
' Propriété en lecture
Localite = mLocalite
End Property

Property Let Localite(Localite As String)
' Propriété en écriture
mLocalite = UCase(Localite)
End Property

Property Get DateNaissance() As Date
' Propriété en lecture
DateNaissance = mDateNaissance
End Property

Property Let DateNaissance(DateNaissance As Date)
' Propriété en écriture
mDateNaissance = DateNaissance
End Property

Property Get Sexe() As String
' Propriété en lecture
Sexe = mSexe
End Property

Property Let Sexe(Sexe As String)
' Propriété en écriture
mSexe = UCase(Sexe)
End Property

Remarquez qu'il faut une similitude de type entre une propriété en lecture et la même propriété en écriture.

Ainsi, si la propriété en lecture est typée String, la propriété en écriture doit recevoir un paramètre typé String.

IV-C-6-a-iv - La propriété « Age » en lecture seule

Après avoir vu comment ajouter du code dans un Property Let, nous allons aborder une propriété en lecture seule avec du code pour calculer la valeur de la propriété.

Pour éviter que le programmeur du code appelant doive créer une fonction qui calcule l'âge du contact par rapport à la date de naissance, nous allons créer une propriété Age au sein de notre classe. Le code appelant devra alors seulement lire la propriété Age de l'objet contact, sans se soucier de l'algorithme utilisé.

Voici le code permettant de calculer l'âge révolu du contact.

Property Get Age() As Integer
Age = Int((Date - mDateNaissance) / 365)
End Property

Il existe une multitude de façons de calculer l'âge, avec ou sans la fonction DateDiff. Ce cours ayant pour objectif l'apprentissage des classes personnalisées et non les algorithmes de calcul d'âge, le lecteur acceptera ma façon de calculer et la considérera pour ce qu'elle est, à savoir l'illustration d'une création de propriété en lecture seule.

Ainsi, le code suivant, placé dans un module standard, permettra de connaître l'âge d'un contact en fonction de sa date de naissance.

Sub CreerContactAge()
Dim Contact As New cContact

Contact.Prenom = "Pierre"
Contact.DateNaissance = DateSerial(1966, 12, 26)

MsgBox Contact.Prenom & " a " & Contact.Age & " ans"
End Sub

Ce qui affichera le message suivant

Dans ce cas précis, mDateNaissance étant une date, le fait de ne pas spécifier la date de naissance pose simplement un problème de calcul, et le résultat est erroné.

Il n'y a pas d'erreur amenant à un débogage ou un plantage, mais il faudra être vigilant et envisager tous les cas possibles.

Nous verrons très bientôt comment gérer ce type de situation.

IV-C-6-a-v - Un petit mot sur l'explorateur d'objets

J'ai abordé plus haut dans le cours l'explorateur d'objets. Cet explorateur permet également de parcourir les objets que nous créons.

Si je demande à l'explorateur d'objets de m'afficher les propriétés de ma classe cContacts, j'obtiendrai l'affichage suivant :

En sélectionnant une valeur dans la liste, j'obtiens les spécifications de cette propriété ou méthode

IV-C-6-a-vi - Conclusions

Nous venons d'approcher la puissance des propriétés de notre classe, car seule notre imagination va nous bloquer.

Si, par exemple, vous devez souvent calculer des emprunts, les taux de remboursement…, vous pouvez vous définir une classe cEmprunts et créer des méthodes et des propriétés pour cette classe.

Vous mettez alors à disposition du code, voire d'autres programmeurs, une classe qui peut être utilisée, même par quelqu'un qui ne connaît aucune formule de calcul…

Dans la suite du cours, nous allons enrichir notre classe avec des créations d'erreurs et des déclenchements d'événements.
Nous donnerons également au code appelant les moyens de contrôler l'exécution du code à l'intérieur de la classe. Mais… Patience !

IV-D - Déclenchement d'erreurs au sein de notre classe

Lorsque nous avons créé la propriété Age, nous avons remarqué que l'âge pouvait être erroné si la date de naissance n'était pas spécifiée.

Nous pourrions aussi avoir des surprises désagréables si nous créons un contact pour une personne qui n'est pas encore née, en introduisant une date ultérieure à la date système. Gênant…

Nous allons donc contrôler, dans Property Let DateNaissance, que la date passée en paramètre est valide.
Cela se fera très facilement avec le code suivant

Property Let DateNaissance(DateNaissance As Date)
' Propriété en écriture
If DateNaissance <= Date Then
mDateNaissance = DateNaissance
Else
...
End If

End Property

Jusque là, ce n'est pas trop compliqué. Mais par quoi faut-il remplacer les … ? Par ceci ?

Property Let DateNaissance(DateNaissance As Date)
' Propriété en écriture
If DateNaissance <= Date Then
mDateNaissance = DateNaissance
Else
MsgBox "Date non valide", vbOKOnly + vbExclamation, "Erreur de saisie"
End If

End Property

En effet, si je passe comme date de naissance une date ultérieure à la date du jour, j'ai un beau message d'erreur.

Mais, ce faisant, j'enfreins la règle d'encapsulation vue plus haut, car ma classe passe outre le code appelant pour s'adresser directement à l'utilisateur derrière son écran, sans donner la possibilité au code appelant de reprendre la main.

Une classe doit TOUJOURS, en cas d'erreur, rendre la main au code appelant et JAMAIS à l'utilisateur.

C'est au code appelant qu'il revient de gérer l'erreur survenue dans la classe.

Nous allons donc devoir créer du code au sein de la classe pour rendre la main au code appelant et lui déléguer la responsabilité de traiter l'erreur.

La classe a la responsabilité de signaler l'erreur, le code appelant a la responsabilité de le gérer.

IV-D-1 - Génération d'une erreur au sein de la classe

Pour informer le code appelant d'une erreur, dans le cas présent une erreur de saisie, nous allons générer une erreur avec Err.Raise Number, [Source], [Description]…

Property Let DateNaissance(DateNaissance As Date)
' Propriété en écriture
If DateNaissance <= Date Then
mDateNaissance = DateNaissance
Else
Err.Raise Number:=vbObjectError + 1, Description:="Date non valide"
End If

End Property

Pour générer l'erreur, nous avons besoin d'un numéro d'erreur, et optionnellement d'une source et d'une description de l'erreur. Nous pouvons également définir des paramètres utiles lors de l'utilisation de fichier d'aide, mais je n'en parlerai pas ici.

Certaines plages de numéros d'erreur étant réservées par Windows et VBA, nous utiliserons la constante vbobjecterror en lui ajoutant des unités, et il faudra, pour le support technique, tenir des fiches à jour avec le numéro de l'erreur et les lignes de code concernées, pour faciliter la maintenance de code.

En plus de ce numéro d'erreur, nous utiliserons le paramètre Description qui explicitera l'erreur rencontrée.

Si, après cette modification de la propriété, nous relançons le code, nous avons un message d'erreur, mais au niveau du code appelant et plus au niveau de l'utilisateur « final ».

On comprend donc clairement ici que la main est rendue au code appelant. À lui la responsabilité de la gestion de l'erreur, par exemple avec le code suivant

On Error GoTo Erreurs
Contact.Prenom = "Pierre"
Contact.DateNaissance = DateSerial(2008, 12, 26)

MsgBox Contact.Prenom & " a " & Contact.Age & " ans"

Erreurs:
Select Case Err
Case -2147221503
MsgBox "La date saisie n'est pas valide et ne sera pas enregistrée", vbOKOnly + vbExclamation, "Erreur de saisie"
Resume Next
End Select

C'est donc bien le code appelant qui reprend la main et qui décide de la façon de traiter l'erreur.

IV-E - Mettre des événements à disposition du code appelant

VB 6.0 et VBA permettent de créer des événements liés à une classe personnalisée. Ces événements sont levés lors de certaines manipulations à l'intérieur de la classe et passent la main au code appelant qui peut choisir de les gérer ou pas

IV-E-1 - Exemples d'événements mis à disposition par des classes d'objets

Nous connaissons tous la gestion des événements pour des objets couramment utilisés lors d'une programmation en VB 6.0 ou en VBA.

Voici par exemple la gestion de l'événement de chargement d'un « form » en VB 6.0

… et voici l'illustration de la gestion de l'événement « SelectionChange » d'une feuille de calcul Excel, recevant en paramètre la cellule active de la plage sélectionnée.

Les événements qu'il est possible de gérer dépendent évidemment de l'objet que l'on manipule. Ainsi, dans une classe personnalisée, il n'y a par défaut aucun événement à la disposition du code qui exploite un objet issu de cette classe. C'est au concepteur de la classe qu'il revient de créer les événements qu'il souhaite mettre à disposition du code utilisateur.

IV-E-2 - Création d'un événement pour notre classe personnalisée

La mise à disposition d'événements pour des objets d'une classe personnalisée n'impose pas la gestion de ces événements par le code appelant. Dans Excel, lorsque vous manipulez des feuilles de calcul, vous n'êtes pas tenus de gérer les événements mis à votre disposition. Il en est de même pour les événements de vos classes personnalisées, qui ne seront peut-être jamais gérés par le code appelant. C'est ce qui différencie les événements des erreurs, qui sont, quant à elles, levées systématiquement, qu'elles soient gérées ou non par le code appelant.

IV-E-2-a - Création et gestion d'un événement sans paramètre

Imaginons que nous souhaitons que le code appelant puisse gérer le fait que l'anniversaire du contact surviendra dans les 10 jours, et que nous souhaitons déclencher l'événement lors de la saisie de sa date de naissance.

IV-E-2-a-i - Création de l'événement

Tout d'abord, il faut déclarer l'événement. Celui-ci se déclare avant la première méthode, la première propriété ou la première fonction, tout en haut du module. Créons un événement appelé AnniversaireDans10Jours. Le code utilisé est simple

Public Event AnniversaireDans10jours()

Lorsque cela est fait, il nous reste à déclencher l'événement lorsque la condition est remplie. Pour cela, nous allons modifier le code de property let DateNaissance, de façon à tester la date d'anniversaire

Property Let DateNaissance(DateNaissance As Date)
' Propriété en écriture
Dim DateAnniversaire As Date
mDateNaissance = DateNaissance

' Calcul de la date anniversaire pour l'année en cours
DateAnniversaire = DateSerial(Year(Date), Month(DateNaissance), Day(DateNaissance))
If DateAnniversaire >= Date And DateAnniversaire <= Date + 10 Then RaiseEvent AnniversaireDans10jours
End Property

Avec la saisie semi-automatique, la liste des événements disponibles pour l'objet apparaît après la saisie de RaiseEvent

Nous venons donc de créer un événement pour les objets de notre classe. Si cet événement est géré par le code appelant, ce dernier pourra, par exemple, avertir l'utilisateur que le contact qu'il est en train de gérer a son anniversaire dans les dix prochains jours.

IV-E-2-a-ii - Gestion de l'événement dans le code appelant

Pour pouvoir gérer l'événement de l'objet issu de notre classe cContact, nous devons déclarer à VB 6.0 ou VBA que nous voulons gérer les événements pour cet objet. Pour cela, il suffit de déclarer notre objet de la façon suivante, en utilisant Dim ou Public en fonction de la portée d'utilisation voulue pour notre objet.

Dim WithEvents oContact As cContact

Tant en VB 6.0 qu'en VBA, les modules standard ne permettent pas de gérer des événements pour des objets, qu'ils soient personnalisés ou non. Il n'est donc pas possible de déclarer des objets avec la clause WithEvents au sein d'un module standard. La ligne est directement refusée et mise en rouge.

Pour info, la déclaration avec WithEvents d'un objet qui ne possède pas d'événements ne déclenche pas d'erreur lors de la saisie du code, mais déclenche une erreur à l'exécution. Il n'est donc possible d'utiliser WithEvents que pour des objets pour lesquels il existe des événements gérables.

Pour illustrer cela, nous allons utiliser un formulaire avec un bouton.

Dans un formulaire de notre projet (formulaire pour VB 6.0 ou userform pour VBA), déclarons un objet de type cContact en tête de module.

Option Explicit

Dim WithEvents oContact As cContact

En déclarant notre objet de cette manière, nous pouvons gérer les événements qui lui sont attachés. En effet, dans la fenêtre du code, nous disposons de l'objet dans la liste déroulante de gauche,

VB 6.0 : Objet dans la liste des objets disposant d'événements

VBA : Objet dans la liste des objets disposant d'événements

et de la liste de ses événements dans la liste déroulante de droite.

VB 6.0 : Liste des événements pour l'objet sélectionné dans la liste déroulante de gauche

VBA : Liste des événements pour l'objet sélectionné dans la liste déroulante de gauche

Lorsque l'objet a été déclaré ce cette manière, nous pouvons gérer l'événement souhaité

Private Sub oContact_AnniversaireDans10jours()
MsgBox "L'anniversaire de " & oContact.Prenom & " a lieu dans les 10 prochains jours"
End Sub

Si, sur le formulaire (VB 6.0) ou le userform (VBA), nous plaçons un bouton nommé btnContact, nous pouvons gérer notre contact via un clic sur ce bouton, avec le code suivant

Private Sub btnContact_Click()
Set oContact = New cContact
With oContact
.Prenom = "Manon"
.DateNaissance = DateSerial(1992, 12, 3)
End With
End Sub

En démarrant le code, nous aurons le message suivant

Pour que l'exemple fonctionne, il faut évidemment que la date de naissance utilisée soit calculée pour que l'anniversaire tombe dans les 10 prochains jours par rapport à la date système de vote pc.

Si nous suivons l'exécution du code pas à pas, nous pouvons comprendre comment la levée de l'événement fonctionne. Pour cela, plaçons un point d'arrêt sur la ligne de code qui attribue la date de naissance.

Démarrons le code. L'exécution s'arrête sur la ligne qui attribue la date de naissance.

Avec F8, avançons pas à pas. L'exécution saute dans le module de classe, et nous pressons F8 jusqu'à la ligne qui teste la date de naissance

La date de naissance est testée. Si la condition est remplie, l'événement est généré

L'exécution saute dans le code appelant pour générer l'événement, et nous constatons que l'affichage du message va avoir lieu

Après affichage du message et clic sur le bouton OK, on passe à la fin de la gestion de l'événement dans le code appelant

Puis on retourne dans le code de la classe pour terminer le code lié à la propriété DateNaissance en écriture

Et on repasse dans le code appelant pour continuer l'exécution du code

Comme vous le voyez ici, la mise en place d'un événement au sein d'une classe est finalement assez simple et s'effectue en deux temps : déclarer un événement au niveau du module, puis générer l'événement dans le code, à l'endroit opportun. Il ne reste plus qu'à choisir de gérer ou non l'événement dans le code appelant.

IV-E-2-b - Création d'un événement qui passe un paramètre au code appelant

IV-E-2-b-i - Exemples d'événements avec paramètre(s)

Certains événements passent au code appelant un ou plusieurs paramètres. C'est le cas de l'événement Unload d'un form en VB 6.0, qui passe un paramètre

ou encore de l'événement BeforeDoubleClick d'une feuille de calcul Excel, qui passe quant à lui deux événements au code appelant.

Ces paramètres peuvent alors être gérés ou utilisés par le code appelant.

IV-E-2-b-ii - Création de l'événement au sein de la classe

Il est possible de passer un paramètre au code appelant. Ainsi, on pourrait vouloir passer la date de naissance au code appelant lorsque le contact a son anniversaire dans les 10 jours.

Pour cela, on doit modifier la déclaration de l'événement.

Il faut aussi modifier également le code qui génère l'événement, car il doit correspondre à la syntaxe de la déclaration de l'événement.

Bien entendu, le code qui gère l'événement doit lui aussi correspondre à la syntaxe de l'événement paramétré.

Le code appelant est bien entendu libre d'utiliser ou pas le paramètre passé lors de la levée de l'événement. Ici, le paramètre est utilisé dans un messagebox

IV-E-2-b-iii - Utilisation de l'événement et de son paramètre dans le code appelant

IV-E-2-c - Modification du déroulement du code dans la classe grâce à un paramètre « décisionnel »

IV-E-2-c-i - Création d'un paramètre décisionnel

Un paramètre décisionnel est un paramètre dont la modification par le code appelant va modifier l'exécution du code au sein de la classe. L'exemple typique de paramètre « décisionnel » est le paramètre Cancel utilisé par nombre de codes évènementiels.

Imaginons que nous voulions contrôler la modification de la propriété sexe de notre contact. On se rend compte rapidement que, sauf à devoir changer le contenu de la garde-robe du contact, il peut être utile de mettre à disposition du code appelant un événement lorsque celui-ci modifie la propriété Sexe du contact.

Nous allons déclarer l'événement dans la classe en utilisant le code suivant

Public Event ModificationGenre(ByRef Cancel As Boolean)

Remarquez que le paramètre Cancel est passé ByRef.

Il nous faut modifier la propriété Sexe en écriture, dans la classe cContact

Property Let Sexe(Sexe As String)
' Propriété en écriture
Dim Cancel As Boolean
If mSexe <> "" Then RaiseEvent ModificationGenre(Cancel) ' Teste si le genre a déjà été attribué
If Not Cancel Then mSexe = UCase(Sexe) ' Si Cancel = FALSE, on n'annule pas la modification...et donc on modifie!
End Property

Le code modifié testera si le genre a déjà été défini. Si oui, l'événement est généré, à charge pour le code appelant de définir la suite des opérations.

IV-E-2-c-ii - Gestion de l'événement dans le code appelant

Dans le code appelant (code du form ou du userform), on va gérer l'événement, puisque nous avons maintenant un deuxième événement dans la liste de droite.

Private Sub oContact_ModificationGenre(Cancel As Boolean)
Dim Reponse As VbMsgBoxResult

Reponse = MsgBox("Etes-vous certain de vouloir modifier le genre du contact?", vbYesNo + vbQuestion, "Gestion du contact")
If Reponse <> vbYes Then Cancel = True
End Sub

Remarquez que la ligne de déclaration de la procédure évènementielle reprend la structure de la déclaration de l'événement dans le module de classe,

et donc passe le(s) paramètre(s) défini(s) dans cette déclaration.

À nouveau, utilisons l'exécution pas à pas pour bien comprendre ce qui se passe. Pour cela, nous allons utiliser le code appelant suivant, dans la procédure évènementielle Click du bouton btnContact

Private Sub btnContact_Click()
Set oContact = New cContact
With oContact
.Prenom = "Manon"
.Sexe = "F"
End With

oContact.Sexe = "M"
MsgBox oContact.Prenom & ": sexe " & UCase(oContact.Sexe)
End Sub

Plaçons un point d'arrêt sur la ligne qui affecte le genre du contact

Démarrons l'exécution du code, qui s'arrête sur la ligne qui définit pour la première fois le genre du contact

Le code passe dans le module de classe, teste si mSexe est une chaine vide.

Comme c'est le cas, l'événement n'est pas généré, et le code passe à la ligne suivante, sur laquelle on va tester la variable locale Cancel, définie à False par défaut

Cancel est différent de True, donc la variable mSexe reçoit la valeur passée par le code appelant.

Et le code continue, et repasse dans le code appelant pour, cette fois, modifier le genre du contact, puisque nous venons de l'attribuer à la ligne précédente.

Le code appelant veut modifier la propriété Sexe du contact, le code repasse donc dans le module de classe sur la propriété Sexe en écriture et teste si mSexe est une chaine vide. Ce n'est plus le cas, puisque la valeur « F » a déjà été attribuée

Donc, l'événement est généré avec Cancel = False (valeur par défaut).

Le code repasse au code appelant puisqu'une procédure évènementielle a été rédigée. Cette procédure évènementielle reçoit bien le paramètre Cancel avec la valeur False

Si nous répondons NON à la question posée, la valeur de Cancel est modifiée

La procédure évènementielle se termine, et le code repasse dans le module de classe pour continuer son exécution. On voit ici que la valeur de Cancel a été modifiée, grâce au mot-clé ByRef utilisé lors de la déclaration de l'événement en début du module de classe.

Comme Cancel vaut True, mSexe n'est pas modifié. La modification de Cancel par le code appelant a donc bien modifié l'exécution du code à l'intérieur de la classe.

Si nous avions répondu Oui à la question posée par le code appelant, la valeur de Cancel n'aurait pas été modifiée, et donc mSexe aurait reçu la nouvelle valeur, ce qui aurait pour conséquence que notre contact féminin serait devenu un homme…

On comprend donc mieux ici, grâce à cet exemple, comment fonctionne le paramètre Cancel présent dans de nombreuses procédures évènementielles que nous rencontrons avec les objets que nous utilisons dans nos applications.

V - Conclusions

Cette partie du tutoriel nous a permis de comprendre ce qu'était un objet personnalisé. Nous avons abordé les notions de propriété, génération d'erreurs et génération d'événements

Nous voici arrivés au terme de la première partie de ce que je souhaite vous expliquer sur le fonctionnement des classes personnalisées.

Dans une seconde partie, nous envisagerons l'utilisation de cette classe « métier » au travers d'une classe qui liera notre contact à une source de données. Nous verrons que peu de choses seront à modifier dans cette nouvelle classe pour lier nos données à un classeur Excel ou à une base de données Access.

VI - Remerciements

Merci à jeannot45 et à bbil pour la relecture, et aux équipes de office et de vb sur DVP