Cours VB.NETDate de mise à jour : 05/12/2010
XV-A. Programmation orientée objet, Propriétés des Classes (Rappel)
XV-A-1. Interface et Implémentation
XV-A-2. Encapsulation
XV-A-3. Héritage
XV-A-4. Polymorphisme
XV-A-5. Constructeur, destructeur
XV-A-6. Accesseur, mutateur
XV-A-7. Déclaration, instanciation
XV-A-8. Propriétés, Méthodes
XV-A-9. Les Classes : elles sont 'By Ref'
XV-A-9-a. Création de variable objet
XV-A-9-b. Affectation
XV-A-9-c. Comparaison
XV-A-10. Nommage
XV-B. Créer une Classe
XV-B-1. Création de Classe
XV-B-1-a. Revenons une nouvelle fois sur la notion de Classe et d'Objet
XV-B-1-b. Créer une Classe
XV-B-1-c. Ajouter des variables dans une classe
XV-B-1-d. Ajouter des propriétés grâce à 'Property'
XV-B-1-e. Méthode
XV-B-1-f. Récapitulatif Property, méthodes
XV-B-1-g. Constructeur
XV-B-1-h. Destructeur
XV-B-1-i. Surcharge
XV-B-1-j. Évènement
XV-B-1-k. Exception
XV-B-1-l. Les Classes partielles
XV-B-1-m. Méthode partielles
XV-B-2. Classe suite et astuces
XV-B-2-a. Me, My, MyClass, MyBase
XV-B-2-b. Propriété par défaut
XV-B-2-c. Méthode de Classe avec Shared
XV-B-2-d. Création d'un compteur d'instances
XV-B-2-e. Création d'un identifiant unique d'instance
XV-B-2-f. Singleton
XV-B-2-g. Surcharge d'opérateur
XV-B-2-h. Surcharge de ToString
XV-C. Créer un composant (Bibliothèque de Classe et de Contrôles)
XV-C-1. Créer une Bibliothèque de classes
XV-C-1-a. Namespace
XV-C-1-b. Utilisation du composant
XV-C-2. Créer un 'contrôle utilisateur' à partir d'un contrôle existant en le modifiant
XV-C-3. Créer un 'contrôle utilisateur' contenant un ou plusieurs contrôles pilotés
XV-A. Programmation orientée objet, Propriétés des Classes (Rappel)
VB.NET permet maintenant de faire de la POO (Programmation Orientée Objet)à part entière:
Il y a:
Les Classes du FrameWork.
On peut aussi CREER soi même (dans des modules de Classe) de nouvelles Classes qui suivront elles aussi les règles de la POO. Ces classes serviront à instancier des objets.
 |
Pour ce chapitre, nous sommes du coté de l'application utilisatrice des objets (et non dans les objets).
|
 L'objet est une boite (jaune ici!!), je l'utilise , mais je ne sais pas ce qui se passe à l'intérieur.
XV-A-1. Interface et Implémentation
Nous savons déjà:
On utilise une Classe (le moule) pour instancier (créer) un objet.
Une classe est une combinaison de code et de données.
- Le code et la définition des données constituent l'implémentation (c'est à l'intérieur de la boite).
- L'interface de l'objet est l'ensemble de ses membres visibles et utilisables (les membres sont les propriétés, les méthodes, les évènements).
Exemple:
Prenons un objet d'une classe ListBox:
- L'interface ListBox.Visible ListBox.AddItem...c'est Je la vois , je peux l'utiliser.
- L'implémentation, je ne la vois pas, c'est le code qui gère la ListBox, la définition des éléments; c'est une 'boite noire', je ne sais pas ce qui s'y passe, je n'y est pas accès, et c'est tant mieux!!!
XV-A-2. Encapsulation
Le fait de ne pas voir l'implémentation (le code), c'est l'encapsulation.
Le code, les définitions de données sont privés à l'objet et non accessibles, ils sont enfermés, encapsulés dans une boite noire.
 |
L'encapsulation permet donc d'exposer aux applications clientes uniquement l'interface.
|
Les applications clientes n'ont pas à se soucier du fonctionnement interne.
Cela a une conséquence, si je modifie le code mais pas l'interface, l'application cliente n'a pas à être modifiée.
XV-A-3. Héritage
On a vu qu'un objet issu d'une Classe dérivée hérite des membres de la classe de base (la classe parent), cela crée une relation mère/fille (parent/enfant); la classe fille pouvant réutiliser les membres de la classe mère.
A noter qu'une classe ne peut hériter que d'une classe en VB.NET.
La Classe fille peut utiliser les membres de la classe mère, mais aussi ajouter ses propres membres ou redéfinir certains membres de la classe mère.
Exemple:
On a vu que quand on dessine une Form1, cela crée une Classe 'Form1' qui hérite des Windows.Forms (Inherits System.Windows.Forms.Form)
Autre exemple:
ListBox hérite de Control
XV-A-4. Polymorphisme
Le nom de polymorphisme signifie "qui peut prendre plusieurs formes".
Tous les sites français donnent les mêmes définitions et entretiennent une certaine confusion. Il indiquent 3 types de polymorphisme et la différence entre polymorphisme 'paramétrique' de surcharge et ad ohc n'est pas évidente. Je vais donc expliquer les choses à ma manière!!
Il y a 4 sortes de polymorphisme:
- Le polymorphisme ad hoc.
Le polymorphisme ad hoc permet d'avoir des fonctions de même nom, avec des fonctionnalités similaires, dans des classes sans aucun rapport entre elles . Par exemple, la classe Integer, la classe Long et la classe Date peuvent avoir chacune une fonction ToString. Cela permettra de ne pas avoir à se soucier du type de l'objet que l'on a si on souhaite le transformer en String.
- Le polymorphisme de ou en anglais .
Une méthode gère des paramètres de type et de nom différents
Ce polymorphisme représente la possibilité de définir plusieurs méthodes de même nom mais possédant des paramètres différents (en nombre et/ou en type).
pour ouvrir une fenêtre MessageBox, la méthode Show a 12 signatures, en voici 2:
Ici on donne 4 paramètres.
Reponse= MessageBox.show(TexteAAfficher,Titre, TypeBouton etIcone, BoutonParDéfaut)
|
Ici 1 seul paramètre.
Reponse= MessageBox.show(TexteAAfficher)
|
On appelle signature chaque combinaison d'arguments d'une fonction (combinaison en nombre et en type). Une fonction a donc autant de signatures que de manière d'appeler cette fonction C'est donc la signature d'une méthode qui détermine quel code sera appelé.
- Le polymorphisme d'héritage (redéfinition, spécialisation ou en anglais overriding)
Quand une classe hérite d'une autre classe, elle hérite des méthodes. On peut redéfinir substituer une méthode de la classe parent par une méthode de même nom dans la classe enfant.
- Le polymorphisme générique ( en anglais template)
C'est la forme la plus naturelle du polymorphisme: elle permet d'appeler la méthode d'un objet sans devoir connaître son type. En VB 2005 cela correspond aux générics.
XV-A-5. Constructeur, destructeur
Un constructeur est une fonction effectuée lors de l'instanciation d'un objet de la Classe; il sert généralement à 'initialiser' l'objet. Il est appelé quand on fait New.
Souvent il y a plusieurs signatures. Il y a habituellement un constructeur par défaut qui n'a pas de paramètres.
Exemple: pour créer un objet graphique Point, j'utilise un constructeur permettant de définir les coordonnées du point:
Dim P As New Point(45, 78)
|
La destruction d'un objet est effectué lorsqu'on lui affecte la valeur Nothing ou lorsqu'on quitte la portée où il a été défini.
XV-A-6. Accesseur, mutateur
Un accesseur (accessor en anglais) est un membre renvoyant la valeur d'une propriété d'un objet.
MyObjet.GetName est un accesseur car elle renvoie la valeur de la propriété Name.
Un mutateur (mutator en anglais) ou encore modifieur (modifier en anglais) est un membre qui modifie la valeur d'une propriété d'un objet.
MyObjet.SetName est un mutateur car elle modifie la valeur de la propriété Name.
XV-A-7. Déclaration, instanciation
On peut déclarer et instancier en même temps:
On peut séparer les 2 actions:
Dim P As Maclasse 'déclaration
P As New Maclasse 'instanciation
|
La déclaration et l'instanciation peuvent être effectuées dans les endroits différents:
Module Mon module
Public P As MaClasse
Sub MaRoutine
P As New MaClasse
End Sub
End Module
|
Ici P est déclaré comme Public, il est instancié dans une Sub.
XV-A-8. Propriétés, Méthodes
Un Objet peut avoir une ou des propriétés:
Dim B As New Button
B.Name ="toto"
|
Un Objet peut avoir une ou des Méthodes:
B.Click est une méthode.
XV-A-9. Les Classes : elles sont 'By Ref'
On rappelle que les classes sont des Objets 'By Ref' (Par référence)
 |
Il faut comprendre qu'une variable Objet contient la référence, le pointeur de l'objet, mais pas l'objet lui même.
|
Cela entraîne:
XV-A-9-a. Création de variable objet
Soit une classe Class1.
On crée un pointeur vide, entraîne: I contient Nothing: il ne pointe sur aucun objet.
Maintenant I contient la référence, le pointeur vers un objet de type Class1
le constructeur New a bien crée une instance de Class1
Habituellement on utilise en une fois:
On peut voir si la variable contient Nothing : If IsNothing( I ) then.. ou If I Is Nothing..
XV-A-9-b. Affectation
Si on affecte une variable par référence à une autre, elle pointe toutes les 2 sur le même endroit mémoire: si j'en modifie une, cela modifie l'autre.
'Créons une Classe contenant un entier.
Class Class1
Public Value As Integer = 0
End Class
Dim C1 As New Class1()
Dim C2 As Class1 =C1 'on crée C2, on affecte C1 à C2
C2.Value = 123 'on modifie C2
=> C1.Value=123 C2.Value=123
|
Modifier C2 a modifié C1 car elles pointent sur le même endroit mémoire.
On le redit autrement: quand on crée C1 et C2, il n'y a pas 2 objets C1 et C2 mais 2 pointeurs vers le même objet.
Si on veut faire une copie 'indépendante', il faut utiliser Clone.
Class Class1
Public Value As Integer = 0
End Class
Dim C1 As New Class1()
C1.Value= 555
Dim C2 As Class1 =C1.Clone 'on crée C2, on clone
C2.Value = 123 'on modifie C2
'C1.Value=555 C2.Value=123
|
XV-A-9-c. Comparaison
Deux objets peuvent être comparés par "Is".
Dim O As Object
Dim Q As Object
If O Is Q then..
|
Equals peut être utilisé pour la comparaison:
Obj1.Equals(Obj2)) 'Retourne True si Obj1 et Obj2 ont le même pointeur.
|
XV-A-10. Nommage
Pour les noms de Classe, utiliser le case Pascal: Chaque mot qui compose le nom a sa première lettre en majuscule.
Exemple Class MaClasse
Idem pour les évènement, espace de nom, méthodes:
Exemple: System.Drawing, ValueChange..
Dans les objets, il ne faut pas inclure des noms de classe dans les noms de propriétés Patient.PatientNom est inutile, utiliser plutôt Patient.Nom.
XV-B. Créer une Classe
XV-B-1. Création de Classe
On a vu qu'il existait des classes prédéfinies (celle du Framework par exemple) mais on peut soi même
CREER SES PROPRES CLASSES:
 Maintenant on est DANS la boite.
XV-B-1-a. Revenons une nouvelle fois sur la notion de Classe et d'Objet
On va créer une classe 'Médicament'( c'est l'objet de ce chapitre).
Un 'Médicament' possède les variables:
Nom
Laboratoire
Nombre de médicament.
. .
|
Il faut donc pouvoir 'regrouper' ces variables pour un médicament précis. Pour regrouper ces variables, on utilise une structure particulière: la Classe.
Une Classe 'Medicament' aura:
Medicament. Nom
Medicament. Laboratoire
Medicament. NbdeMedicament
|
Avec cette Classe (la structure, le moule), on peut créer (instancier) un Objet MonMedicament.
Dim MonMedicament As New Medicament
|
Il comporte les propriétés (données):
MonMedicament. Nom
MonMedicament. Laboratoire
MonMedicament. NbdeMedicament
|
Il comporte des méthodes (comportement):
MonMedicament.GetNom est une méthode
L'utilisateur de l'objet ne voit que le nom de la méthode (l'interface), il ne voit pas le code de la procédure (l'implémentation). Il y a bien encapsulation.
Une fois cette classe créée, du coté utilisateur, comment l'utiliser?
Pour instancier un objet Medicament:
Dim MonMedicament As New Medicament ()
|
Donner une valeur à une propriété:
MonMedicament. Nom = " Aspirine " : MonMedicament. Laboratoire = " RP "
|
Récupérer la valeur d'une propriété:
LeNom= MonMedicament. Nom : Nb= MonMedicament. NombreMedicament
|
 |
Pour la suite de ce chapitre, nous sommes DANS la Classe de l'objet ( et non dans l'application utilisatrice).
|
XV-B-1-b. Créer une Classe
Menu Projet puis Ajouter une Classe.
Entrée 'Medicament' dans la zone nom puis OK
Une nouvelle fenêtre de module est ajoutée à notre projet, contenant le code suivant:
Public Class Medicament
. . .
End Class
|
Le mot avant Class indique la portée de la classe:
- Public (Classe instanciable dans et hors du projet, utilisable par un autre programme)
- Private (Classe instanciable que dans elle même!!)
- Friend (Classe instanciable dans le projet )
- Protected (Classe instanciable uniquement dans les classes dérivées)
On peut ajouter:
MustInherit: Cela donne une classe non instanciable, on ne peut pas créer d'objet avec!! Alors à quoi cela sert!! A fournir une base pour des classes qui en hériteront. on appelle ces classes des classes abstraites.
NotInheritable: Cette classe ne peut-être héritée.
En vb 2010, si dans le code, on utilise une classe qui n'existe pas, vb souligne le nom de la classe et vous propose (en cliquant sur le bouton dessous) de créer une classe vide:
XV-B-1-c. Ajouter des variables dans une classe
On parle de variable ou 'Attribut' permettant d'associer des données à une Classe.
La variable peut être 'Privée' et non visible à l'extérieur:
Il est conseillé de mettre un '_' au début du nom d'une variable privée puis une majuscule au début de chaque mot sauf le premier.
Elle peut être 'Public' , visible à l'extérieur et donc non encapsulée.
Public Laboratoire As String
|
Il est conseillé de mettre une majuscule au début de chaque mot formant le nom de la variable public.
A l'extérieur , si on instance M comme un médicament (Dim M As New Médicament) , M.Laboratoire est valide.
On peut définir un champ 'Public' en lecture seule qui a une valeur constante:
Public ReadOnly NombreMedicament= 2000
|
Vous pouvez ajouter à votre variable : Shared. Cela signifie que la variable déclarée comme Shared est partagée par toutes les instances de la classe : C'est une variable de classe et non une variable d'instance.
Une variable Shared est même utilisable sur le nom de la classe , sans instancier.
Exemple:
Public Class Medicament
Public Laboratoire As String
Public Shared Societe as String
End Class
Dim M1 As New Medicament
Dim M2 As New Medicament
M1. Laboratoire = " MVV "
M2. Laboratoire = " VVT "
|
Chaque instance à sa propre variable Laboratoire (Non Shared)
Par contre:
entraîne que M2.societe est aussi égal à "ooo"
La propriété Societe de toutes les instances de Medicament a la même valeur
Medicament.Societe est aussi égal à "ooo"
Ici on a directement utilisé le nom de la classe.
(Ne pas confondre avec une variable Static qui est une variable qui conserve sa valeur, mais qui a une valeur pour chaque instance).
Une variable, comme tous les membres d'une classe peut être, Private, Public, mais aussi Protected (accessible par les seules méthodes de la classe ou des objets dérivés)
 |
Il faut en général déclarer les variables interne en 'Private': elles sont internes et non accessible à l'extérieur de la Classe.
|
XV-B-1-d. Ajouter des propriétés grâce à 'Property'
Les propriétés permettent d'associer des données à une Classe.
On rappelle qu'une Property peut être utilisée comme cela:
Dim MonMedicament As New Medicament ()
MonMedicament. Nom = " Amoxicilline " : Nom est une Property .
|
Vu de l'extérieur cela pourrait ressembler à une variable, en fait dans l'implémentation c'est complètement différent. Il y a du code permettant d'écrire ou de lire la property.
Pour créer une Property:
Tapez 'Property Nom' puis validez, la définition de la propriété est générée en faisant apparaître
- un bloc Get qui correspond à la lecture de la propriété par l'utilisateur.
- un bloc Set qui correspond à l'écriture de la propriété par l'utilisateur, on récupère la valeur dans le paramètre value.
Property Nom ()
Get
End Get
Set (By Val Value)
End Set
End Property
|
J'ajoute Public pour que cette propriété soit accessible hors de la classe, j'indique que c'est une String. Lors de la lecture de la propriété (par l'utilisateur de l'instance) Get retourne (grâce à Return) la valeur _mNom qui est une variable privée de la classe et qui sert à stocker la valeur de la propriété. Lors de l'écriture de la variable, Set récupère la valeur dans Value et la met dans _mNom:
Public Class Medicament
Private _mNom As String
Public Property Nom () as String
Get
Return _mNom
End Get
Set (By Val Value)
_mNom= value
End Set
End Property
End Class
|
Encapsulation:
La variable '-mNom' est encapsulée: l'utilisateur qui utilise une instance de la classe, ne voit pas ce qui se passe (ici c'est très simple) quand il utilise le nom, il ne voit pas l'implémentation (l'implémentation c'est Get...Set...), il ne voit que l'interface c'est à dire .Nom ; Il n'a pas accès à _mNom.
Si l'utilisateur tape: MonMedicament.Nom=" Aspirine" , c'est le code Set..EndSet qui est exécuté.
Si l'utilisateur tape: Dim s As String= MonMedicament.Nom , c'est le code Get..EndGet qui est exécuté.
Une propriété peut être en lecture seule:
Public ReadOnly Property InitialeNom () As String
Get
Return Left (_mNom,1)
End Get
End Property
|
Mais aussi en écriture seule grâce à WriteOnly.
Les propriétés comme les méthodes peuvent être Public, Private, Protected, Friend, ProtectedFrient:
A partir de VB 2005, on peut créer des Property avec une portée différente pour le Set et le Get:
Public Class employee
Private salaryValue As Double
Protected Property salary () As Double
Get
Return salaryValue
End Get
Private Set (ByVal value As Double)
salaryValue = value
End Set
End Property
End Class
|
Implémentation automatique.
A partir de vb 2010 une propriété peut être écrite (implémentée) de manière simple et rapide: plus besoin de Get et Set:
Private _MaProperty As String = " Empty "
Property MaProperty As String
Get
Return _MaProperty
End Get
Set (ByVal value As String )
_MaProperty = value
End Set
End Property
Property MaProperty As String = " Empty "
|
La dernière ligne génère '_MaProperty' qui est un champ privé.
On peut même indiquer une valeur par défaut ou un type:
Property ID () As Integer = - 2
Property ManList () As New List (Of Man)
|
Petit exemple: Créons une classe 'Personne' avec 2 propriétés: 'Nom' et 'Prenom'.
On instancie un objet 'FirstPersonne' de type 'Personne' et on donne une valeur aux 2 propriétés de l'objet.
|
Public Class Personne
Property Nom As String
Property Prenom As String
End Class
. . . . .
Dim FirstPersonne As New Personne
FirstPersonne. Nom = " Las "
FirstPersonne. Prenom = " Philippe "
Dim FirstPersonne As New Personne With {. Nom = " Las " , . Prenom = " Philippe " }
|
XV-B-1-e. Méthode
Une Classe peut contenir un comportement sous forme de procédures et fonctions contenant du code.
On peut ajouter une méthode à une Classe:
Les méthodes d'une classe sont les procédures Sub ou Function déclarées dans la classe.
Elles sont habituellement 'Public'.
Une méthode peut contenir du code effectuant un traitement:
Public Sub Dessine () As Double
End Sub
|
Une méthode peut aussi être utilisée pour lire ou écrire des variables privées, on parle dans ce cas d'accesseurs:
Public Class Medicament
Private _nom As String
Public Sub GetNom () As String
Return _nom
End Sub
Public Sub SetNom (ByVal N As String )
Me. _nom = N
EndSub
End Class
|
SetNom est une mutateur.
XV-B-1-f. Récapitulatif Property, méthodes
- Un objet peut être vu comme un regroupement de données (variables et propriétés).
La différence entre une variable et une Propriété (Property) est qu'alors que la variable est une simple variable, la Property est contrôlée par du code interne qui peut modifier le comportement de la lecture ou de l'écriture de la Property.
- Mais un objet va plus loin: il peut contenir un comportement sous forme de procédures et fonctions contenant du code. (Ce sont les méthodes de l'objet). Elles représentent le comportement commun de tous les objets appartenant à la classe.
Tous les éléments de la Classe peuvent être Public (visibles par l'utilisateur de la classe) ou Private (non accessibles à l'utilisateur de la classe et utilisés en interne dans la classe)
 |
On voit aussi qu'il faut que les variables soient 'Private' et les méthodes et Property 'Public'.
|
XV-B-1-g. Constructeur
Un constructeur est une procédure exécutée lors de l'instanciation.
Dans le module de classe, elle est définie par:
On peut ajouter des paramètres qui serviront à instancier.
Par exemple pour instancier et donner le nom en même temps:
Sub New (ByVal LeNom As String )
_mNom= LeNom
End sub
|
Cela permet Dim M As New Medicament("Aspirine")
On peut définir plusieurs constructeurs avec un nombre de paramètres différents (plusieurs signatures), dans ce cas il y a surcharge, le constructeur par défaut étant celui sans paramètres.
Une autre manière de faire est d'utiliser une méthode Initialise:
Créons une classe avec une méthode Initialise.
Classe Homme
Private _Nom As String
Private _Prenom As String
Public Sub initialise (ByVal N As String , ByVal P As String )
Me. _nom = N
Me. _prenom = P
End Sub
End Class
|
_nom, _prenom étant des données privées de la classe Homme, les instructions :
dim p as New Homme ()
p. _prenom = " Philippe " est refusée
On écrira donc :
dim p as New Homme
p. initialise (" Monnom " , " Philippe " )
|
XV-B-1-h. Destructeur
Un objet est détruit:
- en lui affectant la valeur Nothing.
- si on sort de sa portée.
Une procédure Finalize (appartenant à la Classe Objet) est automatiquement appelée quand l'objet est détruit.
On peut ajouter une procédure Finalize à notre classe, qui redéfini la procédure Finalise de la Classe 'Objet'.
Protected Overrides Sub Finalize ()
End Sub
|
On peut y mettre le code libérant les ressources ou d'autres choses.
Noter que la procédure Finalize est ici la redéfinition (d'ou 'Overrides') de la procédure Finalize (qui est Overridable) de la Classe Objet.
Attention la méthode Finalize est exécutée quand l'objet est réellement détruit (Objet=Nothing le rend inutilisable mais il est toujours présent en mémoire). C'est parfois très tardivement que l'objet est détruit: quand il y a besoin de mémoire (c'est le Garbage Collector qui entre en action) ou à la sortie du programme.
Pour forcer la destruction on peut utiliser l'interface IDisposable:
Il faut mettre dans l'entête de la classe
et mettre dans le code de la classe
Public Sub Dispose () Implements System. IDisposable . Dispose
End sub
|
C'est une méthode Public, on peut l'appeler de l'application cliente:
Là aussi attention, Dispose doit être appelé de manière explicite (il faut le faire, ce n'est pas automatique); quand on fait M.dispose, la procédure Dispose et le code qu'il contient sont exécutés immédiatement, par contre c'est toujours le Garbage Collector qui décide quand Finalise sera exécuté.
Dans la pratique quelle est d'utilité de gérer la destruction autrement que par Objet=Nothing si le Garbage Collector nettoie la mémoire? C'est une question.
Réponse donnée par Microsoft:
Votre composant a besoin d'une méthode Dispose s'il alloue des objets système, des connexions à la base de données et d'autres ressources rares qui doivent être libérées dès qu'un utilisateur a fini de se servir d'un composant.
Vous devez également implémenter une méthode Dispose si votre composant contient des références à d'autres objets possédant des méthodes Dispose.
XV-B-1-i. Surcharge
On peut surcharger un constructeur.
Pour cela il suffit de rajouter autant de procédure New que l'on veut avec pour chacune un nombre de paramètre différent (signatures différentes).
Exemple: On peut surcharger un constructeur:
Class Figure
Sub New ()
Bla Bla
End Sub
Sub New ( ByVal X As Integer, ByVal Y As Integer)
Blabla
End Sub .
End Class
On peut donc instancier la classe correspondante de 2 manières:
Dim A As New Figure
ou
Dim A As New Figure (100,150)
|
On peut surcharger une property.
Pour cela il suffit de rajouter des procédure Property ayant le même nom de méthode avec pour chacune un nombre de paramètre différent (signature différente)
On peut ajouter Overloads mais c'est facultatif.
Exemple surchargeons un membre:
Class Figure
Public Overloads Property Calcul ()
Bla Bla
End Sub
Public Overloads Property Calcul ( ByVal X As Integer, ByVal Y As Integer)
Blabla
End Sub .
End Class
|
XV-B-1-j. Évènement
On peut définir un évènement pour la classe.
Dans la classe:
- Il faut déclarer l'évènement, avec le mot Event
- Il faut utilisez l'instruction RaiseEvent pour le déclencher lorsqu'un état ou une condition le nécessite.
Exemple:
je crée une classe nommé 'Class1' qui contient un membre 'Texte'( Property Texte), si 'Texte' change, alors on déclenche l'évènement TextChange:
Public Class Class1
Private _mTexte As String
Public Event TextChange (ByVal UserName As String )
Public Property Texte ()
Get
Return _mTexte
End Get
Set (ByVal Value)
If Value < > _mTexte Then
RaiseEvent TextChange (" hello " )
End If
_mTexte = Value
End Set
End Property
End Class
|
Si l'application cliente modifie la propriété .Texte d'un objet Class1 alors on compare l'ancien et le nouveau texte, s'il est différent on déclenche un évènement TextChange.
Dans l'application cliente:
- Il faut définir dans la partie déclaration l'objet M de classe Class1 en indiquant qu'il gère des évènements.
Private WithEvents M As Class1
|
- Dans Form_Load par exemple il faut instancier l'objet:
- Il faut écrire la procédure évènement avec son code:
Private Sub M_TexteChange (ByVal v As String ) Handles M. TextChange
MsgBox (" le texte a changé " )
End Sub
|
Ici on demande simplement quand le texte change, d'ouvrir une MessageBox.
Lors de l'utilisation:
M.Text="Nouveau text" 'déclenche la Sub M.TextChange qui dans notre exemple simple ouvre une MessageBox indiquant que le texte à changer.
On remarque que la Classe définit l'évènement, la procédure correspondant à l'évènement est dans l'application cliente.(De la même manière que quand on clique sur un objet bouton cela déclenche la procédure Bouton-Click.)
XV-B-1-k. Exception
Il est parfois utile de déclencher une exception: si l'utilisateur de la classe donne par exemple une valeur invalide à une property, une exception se déclenche indiquant que la donnée est invalide.
Dans l'exemple suivant si l'utilisateur affecte à la propriété Mavaleur une valeur négative, une exception est déclenché (Maclasse.Mavaleur=-2)
Public Property MaValeur () As Integer
Get
. .
End Get
Set (ByVal Value As Integer)
If Value>= 0 Then
_mValeur= Value
Else
Throw New Exception (" La valeur " & Value. ToString & " est invalide " )
End If
End Set
End Property
|
XV-B-1-l. Les Classes partielles
Les classes 'Partielles' sont possible en VB2005 (Framework 2).
Les types partiels permettent la définition d'une classe unique dans plusieurs fichiers source. La définition a une déclaration normale dans un fichier :
Public Class MaClasse
End Class
|
Dans d'autres fichiers source, utilisez le mot-clé Partial pour indiquer au compilateur que ce code est la suite de la définition de classe d'origine :
Partial Public Class MaClasse
End Class
|
XV-B-1-m. Méthode partielles
A partir de VB 2008. Elle sont présentes dans des Classes Partielles. Et sur des méthodes privées.
Voici la syntaxe.
Partial Private Sub MyMethode ()
End Sub
|
Exemple:
Voici la Classe:
Partial Class Product
Private _Quantity As Integer
Property Quantity () As Integer
. . .
End Property
Partial Private Sub QuantityChanged ()
End Sub
End Class
|
Ici dans la Classe initiale la méthode partielle QuantityChanged() sert à donner la signature.
L'autre Classe partielle, qui est dessous, ajoute des fonctionnalités à la méthode partielle.
Partial Class Product< / b>
Private Sub QuantityChanged ()
MsgBox (" Quantity was changed to " & Me. Quantity )
End Sub
End Class
|
XV-B-2. Classe suite et astuces
 |
Pour la suite de ce chapitre, nous sommes toujours DANS la Classe de l'objet ( et non dans l'application utilisatrice).
|
 DANS la boite.
XV-B-2-a. Me, My, MyClass, MyBase
Dans une classe, on peut utiliser Me, My, MyClass, MyBase pour appeler une methode.
Résumons:
Me c'est l'instance en cours.
Me.Calcul dans une classe, appelle la Sub Calcul qui est dans la Classe.
MyClasse c'est l'instance en cours. Mais à la différence de Me, c'est la méthode de la classe de base (Parente) qui est appelée si cette méthode a été substituée.
MyBase c'est la classe parente.
MyBase est couramment utilisé pour accéder aux membres de la classe de base qui sont substitués ou occultés dans une classe dérivée. MyBase.New permet d'appeler explicitement un constructeur de classe de base (parente) d'un constructeur de classe dérivé.
Voyons un exemple:
On a une classe de base, une classe dérivée et on voit, quand on utilise une méthode de la classe dérivée, ce que retourne Me, MyClass et MyBase.
Class baseClass
Public Overridable Sub testMethod ()
MsgBox (" Base class string (parente) " )
End Sub
Public Sub useMe ()
Me. testMethod ()
End Sub
Public Sub useMyClass ()
MyClass. testMethod ()
End Sub
Public Sub useMyBase ()
MyBase. testMethod ()
End Sub
End Class
Class derivedClass : Inherits baseClass
Public Overrides Sub testMethod ()
MsgBox (" Derived class string (enfant) " )
End Sub
End Class
Class testClasses
Sub startHere ()
Dim testObj As derivedClass = New derivedClass ()
testObj. useMe ()
testObj. useMyClass ()
testObj. useMyBase ()
End Sub
End Class
|
My c'est completement différent, c'est un moyen rapide d'avoir accès à des classes de l'application, de l'ordinateur, des ressources...
Utiliser une icone nommée MyIcon qui est dans les ressources et en faire l'icone du formulaire en cours.
Me. Icon = My. Resources . MyIcon
|
Jouer un son qui est dans les ressources.
My. Computer . Audio . Play (My. Resources . Form1Greeting , AudioPlayMode. Background )
|
XV-B-2-b. Propriété par défaut
Default sert à indiquer que la propriété est la propriété par défaut.
Créons une property par défaut:
Class MyClasse
Default Property MonItem As String
. .
End Property
End Class
|
Maintenant, il n'est plus nécessaire d'utiliser le nom de la propriété quand on y accède.
Dim MyCollection As New MyClasse
|
On peut écrire:
MyCollection. MonItem (index)
|
Ou
Bien sur, il ne peut y avoir qu'une seule property par défaut.
XV-B-2-c. Méthode de Classe avec Shared
Vous pouvez ajouter à votre membre : Shared. Cela signifie que la variable, la propriété ou la méthode déclarée comme Shared est partagée par toutes les instances de la classe: c'est une méthode de classe.
Une méthode déclarée dans une classe sans modificateur Shared est appelée méthode d'instance. Une méthode d'instance opère sur une instance donnée.
Pour une variable non Shared, dans chaque instance la variable a sa valeur propre.
Une variable Shared est commune à toutes les instances de la Class et même à la Classe elle-même sans avoir besoin de l'instancier. C'est une variable de Classe.
L'exemple suivant illustre les règles d'accès aux membres d'instance et Shared :
Class Test
Private _x As Integer
Private Shared _y As Integer
Sub F ()
_x = 1
_y = 1
End Sub
Shared Sub G ()
_x = 1
_y = 1
End Sub
End Class
Shared Sub Main ()
Dim t As New Test ()
t. _x = 1
t. _y = 1
Test. _x = 1
Test. _y = 1
End Sub
|
La méthode F montre que, dans une fonction d'instance membre, un identificateur peut être utilisé pour accéder à des membres d'instance et à des membres Shared. La méthode G montre que, dans une fonction membre Shared, il est erroné d'accéder à une instance membre via un identificateur. En d'autres termes dans une méthode Shared on n'a accès qu'au variable Shared. Quant à la méthode Main, elle montre que, dans une expression d'accès au membre, l'accès aux membres d'instance s'effectue par l'intermédiaire des instances, alors que l'accès aux membres Shared est possible via des types ou des instances.
XV-B-2-d. Création d'un compteur d'instances
Je veux savoir combien il a été crée d'instance de 'Médicament':
'Créons une variable commune à toutes les instances
Private Shared Nb as Integer= 0
|
'Le constructeur va l'incrémenter à chaque instanciation:
'Il suffit de lire sa valeur pour savoir le nombre d'objet Medicament:
Public ReadOnly Property NbInstance ()
Get
NbInstance= Nb
End Get
End Property
End Class
|
XV-B-2-e. Création d'un identifiant unique d'instance
Pour chaque instance, je veux avoir un identifiant unique, un globally unique identifier (GUID).
Private m_ID As System. Guid
Sub New ()
m_ID = System. Guid . NewGuid
End Sub
|
Exemple d'identifiant unique:
Chaque instance ayant un identifiant unique, je peux ainsi 'repérer' les instances (en créant une property ReadOnly afin de lire cet identifiant)
Il existe une très faible probabilité pour que le nouveau GUID soit égal à zéro ou égal à un autre GUID
XV-B-2-f. Singleton
Parfois , on a une classe avec laquelle on veut créer une instance et une seule.
New doit-être toujours private pour empêcher d'instancier avec New
Un singleton est construit grâce à une méthodes de classe nommé 'GetInstance' (sans avoir à instancier).
Shared Function getInstance () As sing
|
Cette fonction qui s'appelle toujours getInstance va servir à instancier une fois la variable Instance.
Cette variable est la base du Singleton. Elle s'appelle Instance (par convention) elle est du même type que la class et contient l'instance unique.
Public Class sing
Shared instance As sing
Private Sub New ()
MyBase. New ()
End Sub
Shared Function getInstance () As sing
If IsNothing (instance) Then
instance = New sing
End If
Return instance
End Function
. . .
. . .
End Class
|
Comment utiliser cette Classe?
Dim t As sing = sing. getInstance
|
Remarque:
- Si on fait ensuite Dim t1 As sing = sing.getInstance c'est la même instance qui est retournée. On ne peut en créer qu'une..
- Si on écrit Dim t As New sing :cela plante.
On peut ajouter une protection contre les multi thread trop rapide avec SyncLock GetType(sing)
Shared Function getInstance () As sing
If IsNothing (instance) Then
SyncLock GetType (sing)
If IsNothing (instance) Then
instance = New sing
end if
End SyncLock
End If
Return instance
End Function
|
XV-B-2-g. Surcharge d'opérateur
A partir de VB 2005, on peut surcharger les opérateurs + - * / mais aussi _ ^ & Like And Or Xor Not < > = << >> CType IsTrue, IsFalse.
Cela signifie que pour cette classe l'opérateur aura le comportement que vous lui donnerez.
Exemple: surchargeons l'opérateur +
Public Classe height
. . .
Public Shared Operator + (ByVal h1 As height, ByVal h2 As height)As height
Return New height (h1. feet + h2. feet , h1. inches + h2. inches )
End Operator
End Structure
|
La routine doit être Shared, de plus si on surcharge certains opérateurs, il faut aussi surcharger leur inverse: si on surcharge '>' , il faut surcharger '<'.
Surcharge de IsTrue, IsFalse CType
Si on teste un boolean, il a la valeur True ou False.
Si par contre je crée une classe nommée 'Personne', je peux définir comment une instance sera considérée comme égale à True. Il faut surcharger l'opérateur IsTrue en lui indiquant dans quelle condition l'instance sera considérée comme =True:
Exemple:
J'ai une instance e de type Personne, si e.Present =True, dans ce cas je veux que e soit considéré comme True; il faut écrire dans la Classe 'personne':
Public Shared Operator IsTrue (ByVal e As Personne) As Boolean
If e Is Nothing Then
Return False
Else
Return e. Present
End If
End Operator
|
Pour définir l'opérateur IsFalse, c'est simple: c'est Not e
Public Shared Operator IsFalse (ByVal e As Personne) As Boolean
Return Not e
End Operator
|
Ensuite je pourrais utiliser des instructions de la forme:
Surcharge de CType:
Je peux définir dans une classe comment CType va fonctionner:
Pour cela dans la classe Personne, je vais définir les 3 possibilités:
Public Shared Widening Operator CType (ByVal e As Personne) As String
Return e. Nom + " " + e. Prenom
End Operator
Public Shared Widening Operator CType (ByVal e As Personne) As Date
If e Is Nothing Then
Return Nothing
Else
Return e. DateDeNaissance
End If
End Operator
Public Shared Widening Operator CType (ByVal e As Personne) As Boolean
If e Is Nothing Then Return False Else Return e. Present
End Operator
|
Ainsi
CType(UnePersonne,String) retourne Nom Prenon
CType(UnePersonne,Date) retourne la date de naissance
CType(UnePersonne,Boolean) retourne True ou False.
XV-B-2-h. Surcharge de ToString
On va créer une classe 'temperature' et on va surcharger ToString pour créer un format propre à 'temperature'.
Public Class Temperature
Private temp As Decimal
Public Sub New (ByVal temperature As Decimal)
Me. temp = temperature
End Sub
Public Overrides Function ToString () As String
Return Me. temp . ToString (" N3 " ) + " °C "
End Function
End Class
|
Pour utiliser::
Dim currentTemperature As New Temperature (23. 6D )
Console. WriteLine (" The current temperature is {0}. " , currentTemperature)
|
XV-C. Créer un composant (Bibliothèque de Classe et de Contrôles)
On a vu qu'on pouvait CREER SES PROPRES CLASSES dans un projet, mais on peut aussi:
- créer une Classe autonome qui sera utilisée par plusieurs autres programmes, c'est une Bibliothèque de Classe.
- créer un contrôle utilisateur utilisé lui aussi par d'autres programmes.
Maintenant nous allons créer des classes ou contrôles, ils seront utilisés par une application cliente.
XV-C-1. Créer une Bibliothèque de classes
Pour créer une bibliothèque de Classe, il faut faire menu 'Fichier', 'Nouveau', 'Projet':
Cliquer sur l'icône 'Bibliothèque de Classes'.
Le nom par défaut est ClassLibrary1 , valider sur Ok.
Dans la fenêtre principale, il y a:
Public Class Class1
End Class
|
On peut écrire le code, la description d'une classe avec ses propriétés, ses méthodes, ses constructeurs...(Voir page précédente)
On peut ajouter une autre Classe (Menu Projet, ajouter une Classe), ou importer une Classe (Menu Projet, Ajouter un élément existant)
 |
Il n'y a pas de procédure Sub Main. (c'est évident, un composant n'est jamais autonome; c'est l'application cliente qui a cette procédure).
Une bibliothèque de classe ne possède pas les composants que possède une application Windows, il n'y a pas d'interface utilisateur, pas de MessageBox, pas de gestion du curseur; c'est l'application cliente qui s'occupe de gérer l'interface.
|
XV-C-1-a. Namespace
Permet de créer un espace de nom dans le composant:
Il peut y avoir plusieurs niveau:
NameSpace Outils
NameSpace Marteau
. . . .
End
End
|
Equivalent à:
NameSpace Outils
Classe Marteau
. . . .
End Class
End
|
Dans l'application il faudra après avoir référencé le composant (la Dll) importer l'espace de nom pour utiliser le composant.
XV-C-1-b. Utilisation du composant
Il faut enfin enregistrer la bibliothèque, la compiler.
Comment utiliser ce composant?
-
Si la bibliothèque de Classe a été compilée, on obtient une DLL:
Il faut la référencer: Ajouter la Dll au projet (Menu Projet, Ajouter une référence)
Importer l'espace de nom par Imports Espace de nom au début du module.
On peut ensuite utiliser la Classe dans l'application cliente.
- On peut travailler en même temps sur l'application cliente et le projet de la bibliothèque de Classe en les chargeant tous les 2 dans une solution.
XV-C-2. Créer un 'contrôle utilisateur' à partir d'un contrôle existant en le modifiant
Permet de créer un contrôle spécifique qui enrichira la 'Boite à outils' :
(Pour les 'anciens' c'est comme un contrôle OCX, sauf que c'est un contrôle .NET et que c'est un fichier .dll)
Exemple: Créer un bouton avec un aspect spécifique à partir du bouton habituel (Ajout d'un cadre).
- Il faut créer une bibliothèque de contrôle:
Pour créer une bibliothèque de Contrôle, il faut faire menu Fichier, Nouveau, Projet, Icône 'Bibliothèque de contrôle Windows' : Nom du projet: WindowControle par exemple.
On obtient une fenêtre qui ressemble à un formulaire mais sans bord,on peut y ajouter un contrôle (un bouton par exemple )
Si on regarde le code correspondant, Vb a crée une Classe UserControl1 qui hérite de la classe Forms.UserControl
Public Class UserControl1
Inherits System. Windows . Forms . UserControl
End Class
|
Il suffit de substituer à UserControl le nom du contrôle que vous voulez utiliser comme base pour hériter de tous ses éléments. On remplace donc par:
Public Class MonBouton
Inherits System. Windows . Forms . Button
End Class
|
Le 'Design' devient:
-
Il faut modifier l'aspect graphique du bouton:
Pour cela si vous voulez modifier l'apparence du contrôle, il faut remplacer la méthode OnPaint de Button par la votre(celle-ci dessine le contrôle). Au sein de cette méthode, vous devez appeler la méthode OnPaint de la base (de la classe mère), puis ajouter vos propres fonctions de dessin.
Il faut donc ajouter la procédure:
Protected Overrides Sub OnPaint (ByVal e As PaintEventArgs)
MyBase. OnPaint (e)
Dim myPen As New Pen (Color. Purple , 3)
e. Graphics . DrawRectangle (myPen, 3, 3, Me. Width - 6, Me. Height - 6)
End Sub
|
On rappelle que l'argument e est le graphique du bouton.
-
Compilation:
Quand le composant est terminé il faut créer la Dll:
Menu 'Générer' puis 'Générer WindowControle'
On obtient WindowControle.Dll dans le répertoire /bin sous les sources.
-
Utilisation du composant dans une autre application VB.
Dans un autre projet VB, si je veux utiliser mon composant MonBouton, il faut l'ajouter dans la boite à outils:
Pour cela cliquer avec le bouton droit de la souris dans la boite à outils. Un menu contextuel s'ouvre, cliquer sur 'Ajouter/Supprimer des éléments' puis dans la fenêtre qui s'ouvre cliquez sur 'parcourir'.. cliquez sur WindowControle.Dll , le composant MonBouton apparaît dans la Boite à outils.
Il suffit de le saisir et de le déplacer dans un des formulaires comme un bouton normal.
Toutes les procédures évènements (Comme MonBouton1_Click) sont disponibles; elles ont été héritées de Button.
Ce qu'il faut comprendre:
C'est que votre nouveau bouton hérite de Control, une classe qui possède un grand nombre d'événements et de méthodes qui n'apparaissent pas dans les listes déroulantes, car inutile dans l'utilisation de l'objet mais très utile dans le comportement d'un objet. C'est le cas de OnPaint OnKeyDown et OnMouseDown... qui déclenchent les évènements Paint, KeyDown, MouseDown. On redéfini ces méthodes (avec Overrides), dans la méthode redéfini on appelle quand même la méthode de la classe mère puis on ajoute les modifications de fonctionnalités .
Exemple:
Différencier une zone droite et une zone gauche sur le bouton:
On utilise l'évènement OnMouseDown, il a pour paramètre e qui contient les coordonnées de la souris (e.x et e.y)
Protected Overrides Sub OnMouseDown (ByVal e As MouseEventArgs)
MyBase. OnMouseDown (e)
If e. X < Me. Width / 2 Then
MessageBox. Show (" Click sur partie gauche " )
Else
MessageBox. Show (" Click sur partie droite " )
End if
End Sub
|
MessageBox peut être remplacé par un raisevent pour déclencher un évènement.
 |
On vient de créer un composant héritant d'un contrôle, puis on en a modifié les fonctionnalités.
|
XV-C-3. Créer un 'contrôle utilisateur' contenant un ou plusieurs contrôles pilotés
Pour créer une bibliothèque de Contrôle (un contrôle utilisateur), il faut faire menu Fichier, Nouveau, Projet, Icône 'Bibliothèque de contrôle Windows' : Nom du projet: WindowControle par exemple.
On obtient une fenêtre qui ressemble à un formulaire mais sans bord,on peut y ajouter un contrôle (un textBox par exemple comme ici) ou plusieurs contrôles.
 |
Ici la zone de fond grise est importante, le contrôle crée correspond à la totalité de la zone grise du formulaire sans bord, ce qui n'est pas pratique quand on utilise le contrôle (j'en ai dessiné un petit, et je ne voyais pas la textebox!!) Il est donc conseillé de réduire la surface.
|
Si on regarde le code correspondant, Vb a crée une Classe UserControl1 qui hérite de la classe Forms.UserControl
Public Class UserControl1
Inherits System. Windows . Forms . UserControl
End Class
|
Dans ce Usercontrol il y a les procédures privées des évènements des composants,
Private Button1_Click
End Sub
|
Bien sur, elles ne seront pas visibles ni accessibles par l'utilisateur du composant.
L'interface du Usercontrol (ce que verra l'utilisateur du composant) est créer de toute pièce comme dans un module de Class.
Si on double-clique sur le fond, on voit quand même apparaître:
Private Sub UserControl1_Load (. .
End Sub
|
Mais il faut rajouter des membres publiques qui seront accessibles à l'utilisateur du composant. On utilise pour cela les 'Property', 'Sub' et 'variables' publiques pour créer une interface. Le code contenu dans ces procédures de l'interface va 'piloter' le ou les contrôles (comme le TextBox1). Ce code modifie (dans notre exemple) le comportement du TextBox initial.
Ici je vais créer une propriété LeTexte qui est le texte qui sera affiché dans le TextBox1. Cette propriété LeTexte va 'piloter' TextBox1.text. Je modifie le comportement de TextBox1.text en empêchant d'afficher Toto (c'est idiot!! ).
Public Property LeTexte () As String
Get
LeTexte= TextBox1. Text
End Get
Set (ByVal Value As String )
If Value < > " toto " Then
TextBox1. Text = Value
End if
End Set
End Property
|
Je génère la solution, ajouter WindowControle.Dll à la boite à outils, je mets le nouveau composant sur un formulaire; il se nommera UserControl1.
Pour utiliser la propriété précédemment écrite :
UserControl1. LeTexte = " lulu "
UserControl1. LeTexte = " toto "
|
 |
On vient de créer un composant avec la Classe UserControl (au lieu de Forms dans un formulaire ), on a écrit son interface.
|
Remarque: si on veut avoir accès à une sub événement du contrôle qui est dans un composant, il faut que dans le composant la Sub évènement soit Public.
Autre exemple:
Voir cet autre remarquable exemple de création d'un composant (par CGi et neo51 sur developpez.com): Composant horloge: code source, explication claire: un régal.
Les sources présentés sur cette page sont libres de droits,
et vous pouvez les utiliser à votre convenance. Par contre cette page de présentation de ces sources constitue une oeuvre intellectuelle protégée par les droits d'auteurs. Copyright © .
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'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts.
Cette page est déposée à la SACD.
|