Cours VB.NETDate de mise à jour : 05/12/2010
XIII. Comprendre le fonctionnement de VB
XIII-A. Comprendre le fonctionnement de VB.net
XIII-A-1. Le Framework.NET le CLR
XIII-A-2. Inconvénients ?
XIII-A-3. Intérêts ?
XIII-A-4. Revoyons en détails le contenu du Framework
XIII-A-5. Framework 1, 2, 3, 3.5, 4
XIII-A-6. Code managé
XIII-A-7. Garbage Collector
XIII-A-8. Compilation
XIII-A-9. Comment voir le code source, le code IL, le code exécutable ?
XIII-A-10. Installation du Framework
XIII-B. Comprendre le code crée par VB
XIII-B-1. Code généré automatiquement lors de la création d'un formulaire ou d'un contrôle
XIII-B-2. Substitution de procédures évènement
XIII-C. Les délégués, les évènements
XIII-C-1. Définition
XIII-C-2. Création d'un délégué avec 'Delegate'
XIII-C-3. Délégué et appel asynchrone
XIII-C-4. Délégué et évènement
XIII. Comprendre le fonctionnement de VB
XIII-A. Comprendre le fonctionnement de VB.net
Comment fonctionne un programme VB.NET ?
XIII-A-1. Le Framework.NET le CLR
Jusqu'à VB6, un programme VB avait besoin d'un RunTime (Fichier Dll: VBRUN300.DLL par exemple pour VB3)
En VB.net, pour qu'un programme fonctionne, il faut installer le Framework.NET:
C'est une plate-forme informatique, une couche entre Windows et l'application VB.
'Framework' veut dire: bibliothèque de classes.
Le Framework.Net est donc la bibliothèque de classes .NET et contient ADO.NET, ASP.NET et Windows Forms.
Cette infrastructure offre un vaste accès à :
- l'ensemble du système d'exploitation.
- une collection de Classes pour fournir des objets utilisables pour créer des programmes.
- des routines d'exécution de programme.
ceci de manière homogène et très fiable.
L'exécutable en Visual Basic
- utilise les objets (WindowsForms, WPF, type de variable,..)du Framework.
- appelle les fonctions (exécution, affichage, gestion de la mémoire, lecture dans une base de donnée..)du Framework.
A la limite on peut considéré le Framework comme une machine virtuelle (comme celle de Java). Il suffirait de porter le FrameWork sous Linux pour que les programmes VB fonctionnent. (il existe bien le projet 'Mono' sous Linux, mais je ne sais pas s'il contient les Windows Forms).
On rappelle que ce qui fait tourner le Framework c'est le CLR (Commnon Language RunTime); de la version 2 à la version 3.5 du Framework, c'était toujours la version 2.0.50727 du CLR qui est utilisée.
Avec le Framework 4 c'est la version 4!! du CLR qui est utilisée.
XIII-A-2. Inconvénients ?
La couche supplémentaire ralentit le programme?
A peine dit Microsoft, cela serait insignifiant! En fait cela ralentit beaucoup. Le Framework, quand il ouvre un formulaire appelle les routines de Windows, c'est bien une couche supplémentaire.
Et s'il y a une nouvelle version du Framework?
Les versions successives devraient être compatible ascendante et descendante!!
En fait, on peut installer le Framework 1 le Framework 2 et le Framework 3. En VB 2008 et 2010 on peut choisir la version du Framework utilisée par un logiciel (en vb 2010 menu 'Projet', 'Propriété de..', onglet 'Compiler', en bas liste:'Framework cible'); les frameworks ne sont donc pas compatibles.
On peut installer à coté la version 1, 2 ,3 ,3.5, 4; Il faut en installer plusieurs si nécessaire (si on installe différents logiciels qui utilisent des Frameworks de version différente) car il n'y a pas de compatibilité ascendante.
XIII-A-3. Intérêts ?
On installe une seule fois le Framework.
Une fois le Framework installé, il suffit pour installer un nouveau programme de n'installer que l'exécutable.
On peut utiliser plusieurs langages.
Nous appelons les fonctions du FrameWork avec Visual Basic mais on peut aussi le faire avec C# et 30 autres langages. La vitesse d'exécution sera la même, les types de variables, les objets seront les mêmes. Plus de problèmes de transfert de paramètres.
Il est même possible de faire cohabiter plusieurs langages dans une même application.
Le code est homogène.
Plus de bidouille, de ficelle de programmation pour contourner les lacunes du langage et l'accès limité au système d'exploitation: Les milliers de Classes du FrameWork donnent accès à une multitude de fonctions et aux services du système d'exploitation, cela nativement.
XIII-A-4. Revoyons en détails le contenu du Framework
Le CLS (Common Language Spécification) représente la partie visible , interface entre les langages VB, C++, C#, J# et le Framework.
Pour interagir entièrement avec des objets managés quel que soit le langage dans lequel ils ont été implémentés, les objets managés ne doivent exposer aux appelants que les fonctionnalités qui sont communes à tous les langages avec lesquels ils doivent fonctionner.
La spécification CLS permet d'optimiser et d'assurer l'interopérabilité des langages.
Si votre composant n'utilise que des fonctionnalités CLS dans des interfaces API qu'il expose à un autre code (y compris des classes dérivées), le composant est assuré d'être accessible à partir d'un langage de programmation prenant en charge la spécification CLS.
Il est important de comprendre que le respect du CLS augmente la sécurité de votre programme.
Il contient deux composants principaux :
- Le Common Language Runtime (CLR).
Il permet:
- Exécution du code managé.
- Gestion de la mémoire (Garbage collector)
- Utilisation des objets du FrameWork
Le runtime peut être considéré comme un agent qui manage le code au moment de l'exécution, qui l'exécute, fournit des services essentiels comme la gestion de la mémoire, la gestion des threads, et l'accès distant.
- La bibliothèque de classes du .NET FrameWork. (FrameWork Class Library)
C'est une collection complète orientée objet, que vous pouvez utiliser pour développer des applications allant des traditionnelles applications à ligne de commande ou à interface graphique utilisateur (GUI, Graphical User Interface) jusqu'à des applications qui exploitent les dernières innovations fournies par ASP.NET, comme les services Web XML et Web Forms.
Par exemple, les classes Windows Forms sont un ensemble complet de types réutilisables qui simplifient grandement le développement Windows. Si vous écrivez une application Web Form ASP.NET, vous pouvez utiliser les classes Web Forms.
Il existe un éventail de tâches courantes de programmation y compris des tâches comme la gestion de chaînes, la collection de données, la connectivité de bases de données, et l'accès aux fichiers.
Il y a 3300 Classes dans le Framework 1!!

Plus d'appel aux Api Windows, on fait tout directement en utilisant les classes du Framework.
XIII-A-5. Framework 1, 2, 3, 3.5, 4
Un Framework est donc un ensemble de Classes.
Le framework 1.0 est utilisé par VB 2003. (année 2002)
Le framework 2.0 est utilisé par VB 2005.(année 2005)
Il contient des classes supplémentaires.
Le framework 3.0 peut être utilisé par VB 2005.
Le framework 3.0 est composé du framework 2.0 auquel s'ajoute WCF (Windows Communication Foundation), WF (Windows Workflow Foundation), WPF (Windows Presentation Foundation) et infocard pour l'authentification des utilisateurs.
Windows Presentation foundation permet de gérer l' affichage des fenêtres. Celui-ci est basé sur directx (version 10).
Le Framework 3.5 utilisé par VB2008.
C'est le Framework 3 auquel s'ajoute AJAX, LINQ et REST.
Le Framework 4 utilisé par VB2010.
Sous Windows 98, XP, il faut installer le framework (avant d'utiliser l'environnement VisualBasic ou un exécutable VB)
Sous Windows Vista le Framework 3 est installé nativement (et les versions précédentes?).
Sous Windows 7 les Framework 1, 1.1, 2, 3, 3.5 sont installés nativement.
Quand on installe vb 2010 cela installe le framework 4.
XIII-A-6. Code managé
Le code écrit pour le FrameWork est dit managé (ou géré): il bénéficie des avantages du Framework: gestion de la mémoire, optimisation de cette mémoire, règles communes, utilisation de plusieurs langages..
Les anciens composants COM sont utilisables mais non managés.
L'interopérabilité entre les codes managés et non managés permet aux développeurs de continuer à utiliser des composants COM et des DLL nécessaires.
XIII-A-7. Garbage Collector
La destruction effective des objets et variables est effectuée par le 'garbage collector' (ou ramasse-miettes).
Avant .NET quand on détruisait une variable, en lui affectant Nothing, la destruction était immédiate (déterministe); si on oubliait (une application) de détruire une variable de multiple fois il y avait problème!!
Maintenant, quand on détruit une variable, elle reste présente en mémoire; c'est le garbage collector qui, quand il a le temps ou quand il n'y a plus de place, détruit la variable et fait le ménage. On ne sait pas quand il va le faire ( non déterministe).
XIII-A-8. Compilation
Le code que vous écrivez en Visual Basic est le code source.
Lors de la génération (compilation) du projet, un code intermédiaire est produit (IL: Intermédiaire Langage), ce code est commun à tous les langages.
Lors de la première exécution d'exécutable (.exe), le code IL est compilé en binaire par le compilateur 'Just in Time' puis exécuté. Les exécutions suivantes seront plus rapides.
En résumé:
Code Source (VB, C#..) => Intermediate Language (IL). par le compilateur syntaxique.
IL => Binaire exécutable par le compilateur 'Just in Time'
XIII-A-9. Comment voir le code source, le code IL, le code exécutable ?
Exemple effectué sur VB 2003, édition professionnelle. Avec les versions Express 2005, 2008, 2010, certaines fenêtres ne sont pas disponibles.
Prenons un exemple: il y a dans une form1 une procédure Button1_Click:
a-Voici le code source:
Private Sub Button1_Click (ByVal sender As System. Object , ByVal e As System. EventArgs ) Handles Button1. Click
TextBox1. Text = " hello "
End Sub
|
on génère ce code source cela donne un fichier .exe (dans le répertoite /bin)
b-Voir le code Intermédiaire (IL):
Avec Ildasm.exe (désassembleur code IL) qui est dans le SDK:
(C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin)
On peut ouvrir un fichier exe (menu 'Fichier'):
Outre le MANIFEST qui contient les méta données de l'assembly, il y a le code IL de l'application.
Si on clique sur la ligne _Button1 , on voit le code IL correspondant à la routine: (un menu de ildasm.exe permet de voir le code source en même temps)
c-Voir le code binaire ( en assembleur)?
Dans l'IDE (pas dans les versions Express), si je stoppe le fonctionnement de la routine (par un point d'arrêt), le menu 'Fenêtre', 'Code machine' permet de voir l'exécutable mais sous forme d'assembleur.
Le véritable code binaire est composé d'instructions ayant chacune un code:
Voici du code exécutable représenté en hexadécimal: 55 8b ec
Comme c'est totalement illisible, on affiche le code binaire sous une autre forme, en langage d'assemblage ou les instructions sont représentées par des mots mnémoniques: push (pour utiliser la pile), mov (pour déplacer un octet en mémoire..), le code est plus lisible.
Voici le code en assembleur de notre routine:
Private Sub Button1_Click (ByVal sender As System. Object , ByVal e As System. EventArgs ) Handles Button1. Click
00000000 push ebp
00000001 mov ebp,esp
00000003 sub esp,8
00000006 push edi
00000007 push esi
00000008 mov dword ptr [ebp- 8],edx
0000000b mov esi,ecx
0000000d nop
TextBox1. Text = " hello "
0000000e mov ecx,esi
00000010 mov eax,dword ptr [ecx]
00000012 call dword ptr [eax+ 000005B8h]
00000018 mov edi,eax
0000001a mov edx,dword ptr ds:[020B1648h]
00000020 mov ecx,edi
00000022 mov eax,dword ptr [ecx]
00000024 call dword ptr [eax+ 000000E8h]
0000002a nop
End Sub
0000002b nop
0000002c pop esi
0000002d pop edi
0000002e mov esp,ebp
00000030 pop ebp
00000031 ret 4
|
On note que pour information et amélioration de la lisibilité, le code source est ajouté.
d-Voir le véritable code binaire:
Si je fais menu 'Déboguer', 'Fenêtre', 'Mémoire'(Version non Express), j'ai un dump de la mémoire en hexadécimal qui montre le véritable code binaire.
XIII-A-10. Installation du Framework
Les applications et contrôles écrits pour le .NET Framework imposent que celui-ci soit installé sur l'ordinateur où ils s'exécutent. Microsoft fournit un programme d'installation redistribuable, Dotnetfx.exe , qui contient les composants Common Language Runtime et .NET Framework nécessaire à l'exécution des applications .NET Framework.
Ou le trouver:
La version Visual Basic Express 2008 (Framework compris) se trouve ici:
http://www.microsoft.com/express/download/
Choisir autre langue=Français .
(Bien choisir la version française car on ne peut pas installer plusieurs versions de langue différente)
L'utilisateur final de l'exécutable installera le FrameWork (une seule fois pour tous les programmes) et l'exécutable (programme simple).
Ou est le Framework?
dans:
c:\Windows\Microsoft.NET\Framework\v3.5\
On voit dans les sous répertoires les DLL qui composent le Framework: System.dll, System.Drawing.dll..
On peut installer à coté la version 1, 2 ,3 ,3.5, 4; Il faut en installer plusieurs si nécessaire (si on installe différents logiciels qui utilisent des Frameworks de version différente) car il n'y a pas de compatibilité ascendante.
On se souvient que sous Windows 7 les Framework de la version 1 à la version 3.5 sont installés.
XIII-B. Comprendre le code crée par VB
Comprendre le code crée par VB quand on crée une formulaire ou un contrôle.
XIII-B-1. Code généré automatiquement lors de la création d'un formulaire ou d'un contrôle
Une application 'Windows Forms' est principalement constituée de formulaires (ou fenêtre), de contrôles et de leurs événements.
Effectivement, pendant la création de l'interface utilisateur de votre application, vous créez généralement une fenêtre contenant des contrôles et des procédures évènements.
Quand vous créez un nouveau projet 'Windows Forms' cela dessine un formulaire, une fenêtre vide et le code correspondant. Ajoutons y un bouton cela donne l'interface utilisateur suivante:
Comme on l'a vu, VB crée le code correspondant et dans ce code une Classe correspondant à la fenêtre
Décortiquons le code:
Vb crée une Class nommé Form1, elle est 'Public' (accessible partout)
Public Class Form1
End Class
|
En VB.Net 2003
L'onglet Form1.vb contient la Class.
Cette Classe hérite des propriétés de la Classe Form (celle ci est fournis par le Framework)
Inherits System. Windows . Forms . Form
|
(On rappelle que la véritable fenêtre, l'objet sera instancié à partir de cette classe)
Ensuite il y a une région (partie du code que l'on peut 'contracter' et ne pas voir ou 'dérouler'; Cette région contient :" Le Code généré (automatiquement) par le Concepteur Windows Form ", si on le déroule en cliquant sur le '+', on voit:
-Le constructeur de la fenêtre: la routine Sub New
MyBase fait référence à la classe de base,
MyBase.New appelle le constructeur de la classe de base (Form dans notre cas)
-Le destructeur de la fenêtre: la routine Sub Dispose
-Le créateur des contrôles de la fenêtre: la procédure Sub InitializeComponent
Elle est nécessaire pour créer les contrôles et définir les propriétés de ces contrôles:
Exemple : création d'un label Me.Label1= NewSystem.Windows.forms.Label
Modification d'une propriété:Me.Label.Text="Hello"
Elle définie aussi les propriétés du formulaire:
Exemple d'un formulaire vide nommé Form1
Public Class Form1
Inherits System. Windows . Forms . Form
#Region " Code généré par le Concepteur Windows Form "
Public Sub New ()
MyBase. New ()
InitializeComponent ()
End Sub
Protected Overloads Overrides Sub Dispose (ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing ) Then
components. Dispose ()
End If
End If
MyBase. Dispose (disposing)
End Sub
Private components As System. ComponentModel . IContainer
< System. Diagnostics . DebuggerStepThrough ()> Private Sub InitializeComponent ()
Me. AutoScaleBaseSize = New System. Drawing . Size (5, 13)
Me. ClientSize = New System. Drawing . Size (292, 266)
Me. Name = " Form1 "
Me. Text = " Form1 "
End Sub
#End Region
End Class
|
Si dans la fenêtre Design on ajoute un bouton Button1 cela ajoute le code:
Cette ligne contenant WithEvents indique qu'il y a une gestion d'évènement sur les boutons.
Friend WithEvents Button1 As System. Windows . Forms . Button
|
Puis dans Sub InitializeComponent()
Cette ligne créée le bouton.
Me. Button1 = New System. Windows . Forms . Button
|
Cette ligne le positionne:
Me. Button1 . Location = New System. Drawing . Point (56, 144)
|
Cette ligne lui donne un nom.
Me. Button1 . Name = " Button1 "
|
Cette ligne détermine sa taille
Me. Button1 . Size = New System. Drawing . Size (104, 24)
|
Cette ligne indique ce qui est affiché sur le bouton
Me. Button1 . Text = " Button1 "
|
Cela donne:
Private components As System. ComponentModel . IContainer
Friend WithEvents Button1 As System. Windows . Forms . Button
< System. Diagnostics . DebuggerStepThrough ()> Private Sub InitializeComponent ()
Me. Button1 = New System. Windows . Forms . Button
Me. SuspendLayout ()
Me. Button1 . Location = New System. Drawing . Point (56 , 144 )
Me. Button1 . Name = " Button1 "
Me. Button1 . Size = New System. Drawing . Size (104 , 24 )
Me. Button1 . TabIndex = 0
Me. Button1 . Text = " Button1 "
|
En VB.Net 2005:
L'onglet Form1.vb contient:
Public Class Form1
Private Sub Form1_Load (ByVal sender As System. Object , ByVal e As System. EventArgs ) Handles MyBase. Load
End Sub
Public Sub New ()
InitializeComponent ()
End Sub
Protected Overrides Sub Finalize ()
MyBase. Finalize ()
End Sub
Private Sub Label1_Click (ByVal sender As System. Object , ByVal e As System. EventArgs ) Handles Label1. Click
End Sub
|
Le Code généré par le 'Concepteur Windows Form' est par contre caché, il n'apparaît pas dans la Class Form1, il faut pour y avoir accès, passer par le menu de droite:
Si on clique sur InitializeComponent, l'onglet Form1.Designer.vb apparaît.
On a ainsi accès à InitialiseComponent et à Dispose qui sont dans une classe Partielle de Form1.
(En VB 2005, une Classe peut être 'découpée' en Classes partielles)
C'est ici qu'il est indiqué que la Class hérite de System.Windows.Forms.Form
Exemple du contenu de Dispose et ItinialiseComponent:
Partial Public Class Form1
Inherits System. Windows . Forms . Form
< System. Diagnostics . DebuggerNonUserCode ()> _
Protected Overloads Overrides Sub Dispose (ByVal disposing As Boolean)
If disposing AndAlso components IsNot Nothing Then
components. Dispose ()
End If
MyBase. Dispose (disposing)
End Sub
Private components As System. ComponentModel . IContainer
< System. Diagnostics . DebuggerStepThrough ()> _
Private Sub InitializeComponent ()
Me. Button1 = New System. Windows . Forms . Button
Me. Label1 = New System. Windows . Forms . Label
Me. SuspendLayout ()
Me. Button1 . FlatStyle = System. Windows . Forms . FlatStyle . System
Me. Button1 . Location = New System. Drawing . Point (47, 38)
Me. Button1 . Name = " Button1 "
Me. Button1 . Size = New System. Drawing . Size (177, 42)
Me. Button1 . TabIndex = 0
Me. Button1 . Text = " Button1 "
. . .
|
En vb 2010:
Il y a dans Form1.vb (Form1(évènement)) la classe partielle Form1 et la Sub New (le constructeur de la classe Form1)qui appelle InitializeComponent().
Public Class Form1
Private Sub Form1_Load (ByVal sender As System. Object , ByVal e As System. EventArgs ) Handles MyBase. Load
End Sub
Private Sub Button1_Click (ByVal sender As System. Object , ByVal e As System. EventArgs ) Handles Button1. Click
End Sub
Public Sub New ()
InitializeComponent ()
End Sub
End Class
|
En ouvrant Form1 sous l'onglet, si on déroule la liste à droite on decouvre les routines New, Finalise, Dispose,InitializeComponent():
< Global. Microsoft . VisualBasic . CompilerServices . DesignerGenerated ()> _
Partial Class Form1
Inherits System. Windows . Forms . Form
< System. Diagnostics . DebuggerNonUserCode ()> _
Protected Overrides Sub Dispose (ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components. Dispose ()
End If
Finally
MyBase. Dispose (disposing)
End Try
End Sub
Private components As System. ComponentModel . IContainer
< System. Diagnostics . DebuggerStepThrough ()> _
Private Sub InitializeComponent ()
Me. Button1 = New System. Windows . Forms . Button ()
Me. Label5 = New System. Windows . Forms . Label ()
Me. SuspendLayout ()
Me. Button1 . Location = New System. Drawing . Point (16, 46)
Me. Button1 . Name = " Button1 "
Me. Button1 . Size = New System. Drawing . Size (261, 35)
Me. Button1 . TabIndex = 0
Me. Button1 . Text = " Button1 "
Me. Button1 . UseVisualStyleBackColor = True
Me. Label5 . AutoSize = True
Me. Label5 . Location = New System. Drawing . Point (69, 130)
Me. Label5 . Name = " Label5 "
Me. Label5 . Size = New System. Drawing . Size (39, 13)
Me. Label5 . TabIndex = 1
Me. Label5 . Text = " Label1 "
Me. AutoScaleDimensions = New System. Drawing . SizeF (6. 0 !, 13. 0 !)
Me. AutoScaleMode = System. Windows . Forms . AutoScaleMode . Font
Me. ClientSize = New System. Drawing . Size (284, 262)
Me. Controls . Add (Me. Label5 )
Me. Controls . Add (Me. Button1 )
Me. Name = " Form1 "
Me. Text = " Form1 "
Me. ResumeLayout (False )
Me. PerformLayout ()
End Sub
Friend WithEvents Button1 As System. Windows . Forms . Button
Friend WithEvents Label5 As System. Windows . Forms . Label
End Class
|
'Partial Class Form1' indique qu'on est dans la classe partielle Form1.
'Inherits System.Windows.Forms.Form' indique que Form1 herite de la Classe Form du Framework.
La Sub 'Dispose' servira à libérer les composants quand on fermera la form. C'est le destructeur.
La Sub 'InitializeComponent()' permet de construire la form (C'est le constructeur); pour chaque composant il y a du code indiquant le nom du composant et ses propriétés.
Me. Button1 = New System. Windows . Forms . Button ()
Me. Button1 . Location = New System. Drawing . Point (16, 46)
Me. Button1 . Name = " Button1 "
Me. Button1 . Size = New System. Drawing . Size (261, 35)
Me. Button1 . TabIndex = 0
Me. Button1 . Text = " Button1 "
Me. Button1 . UseVisualStyleBackColor = True
Friend WithEvents Button1 As System. Windows . Forms . Button
|
Ce code est donc généré automatiquement. Ne pas y toucher!!
Pour VB.Net 2003 et 2005:
Les procédures évènements correspondant au formulaire par exemple sont automatiquement créées:
Private Sub Form1_Load (ByVal sender As System. Object , ByVal e As System. EventArgs ) Handles MyBase. Load
End Sub
|
Attention : Form1_Load est un nom de Sub donné par VB, mais cela pourrait être n'importe quoi, ce qui indique l'évènement déclencheur est ce qui est après Handles ( MyBase.Load ici).
Les 2 paramètres sont:
sender l'objet à la source de l'événement (l'objet quia déclenché l'évènement: ici la form)
e un objet EventArgs qui détaille l'événement qui s'est produit et fournissant des informations sur cet évènement.
On constate qu'il y a une liaison entre la fenêtre Design et le code généré; on pourrait modifier dans le code l'interface utilisateur. C'est déconseillé d'aller trafiquer dans cette zone de "Code généré par le Concepteur Windows Form ", il faut plutôt faire des modifications dans la partie design et dans la fenêtre de propriété.
XIII-B-2. Substitution de procédures évènement
Il est possible de substituer une méthode (utiliser sa propre méthode à la place de la méthode normale qui existe normalement dans un contrôle)
Exemple 1 créer un contrôle simple affichant toujours 'Bonjour'
Il faut créer une classe héritant des 'Control', détourner son évènement OnPaint qui dessine le contrôle ( avec Overides) Dans la nouvelle procédure On Paint simplement afficher 'Bonjour'
Public Class ControleAffichantBonjour
Inherits Control
Overrides Protected Sub OnPaint ( e As PaintEventArgs )
e. Graphics . DrawString (" Bonjour " , Font, new SolidBrush (ForeColor)
End Sub
End Class
|
Cet exemple ne sert strictement à rien!! Pour une fois!!
Il est aussi possible de détourner des évènements.
Dans le chapitre 'Impression' il y a un bel exemple de création de "lien" entre un objet printdocument et la routine évènement PrintPage (imprimer hello avec un printdocument)
Exemple 2 créer un bouton personalisé.
1-Il faut créer une classe qui hérite de Button:
Public Class MonBouton
Inherits System. Windows . Forms . Button
End Class
|
Le 'Design' devient:
2-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 dans la classe, 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.
Dans le chapitre suivant on va utiliser ces connaissances pour, dans le code, créer soi-même des contrôles et leurs évènements.
XIII-C. Les délégués, les évènements
Super complexe? non!! mais vous pouvez sauter ce chapitre.
XIII-C-1. Définition
Un délégué est une référence (un type référence) qui fait référence à une méthode, qui pointe sur une méthode. L'équivalent le plus proche d'un délégué dans d'autres langages est le pointeur de fonction.
On peut créer directement un délégué avec le mot Delegate et AddressOf.
Dans la gestion des évènements des contrôles on crée aussi des délégués avec Handles et AddHandler.
XIII-C-2. Création d'un délégué avec 'Delegate'
Ici on ne parle pas d'évènement
On déclare un délégué (un pointeur):
on l'instance: on le fait pointer sur une fonction F().
Dim M As New D (AddressOf F)
|
Quand on utilise l'instance du délégué, cela exécute la fonction.
M() exécute la fonction F()
AddressOf permet de préciser l'adresse de la procédure F(), le pointeur vers la procédure F().
Exemple hyper simple:
Delegate Sub SimpleDelegate ()
Module Test
Sub MaSub ()
System. Console . WriteLine (" Test " )
End Sub
Sub Main ()
Dim Mondelegue As New SimpleDelegate (AddressOf MaSub)
Mondelegue ()
End Sub
End Module
|
Il n'est pas d'un grand intérêt d'instancier un délégué pour une méthode et d'appeler ensuite immédiatement la méthode via le délégué, puisqu'il serait plus simple d'appeler directement la méthode, mais c'est un exemple.
Exemple avec une fonction et des paramètres:
Bien sur le délégué peut pointer vers une Sub ou une fonction avec des paramètres:
Dans une Classe MaClasse:
On déclare un delegate en indiquant paramètres envoyés et de retour :
Delegate Function maMethodDelegate (myInt As Integer) As [String ]
|
On déclare une fonction:
Public Shared Function maMethode (myInt As Integer) As [String ]
Return myInt. ToString
End Function
|
Ici l'exemple est bête: on donne un Integer, cela retourne une String!!
On crée le délégué:
Dim myD As New maMethodDelegate (AddressOf maClasse. maMethode )
|
myD est maintenant un pointeur sur maClasse.maMethode.
Je peux utiliser myD(2) qui retournera une String "2"
Intérêts d'un délégué par rapport à une fonction?
Un délégué est un type référence qui fait référence à une méthode Shared d'un type ou à une méthode d'instance d'un objet.
Un délégué peut référencer à la fois des méthodes de classe (static) et des méthodes d'instance. Lorsque le délégué référence une méthode d'instance, il stocke non seulement une référence au point d'entrée de la méthode, mais également une référence à l'instance de classe pour laquelle la méthode est appelée. Contrairement aux pointeurs fonction, les délégués sont orientés objet, de type sécurisé et fiables.
Voici un exemple de Microsoft utilisant un délégué avec les 2 types de méthodes.
Imports System
Public Class SamplesDelegate
Delegate Function myMethodDelegate (myInt As Integer) As [String ]
Public Class mySampleClass
Public Function myStringMethod (myInt As Integer) As [String ]
If myInt > 0 Then
Return " positive "
End If
If myInt < 0 Then
Return " negative "
End If
Return " zero "
End Function
Public Shared Function mySignMethod (myInt As Integer) As [String ]
If myInt > 0 Then
Return " + "
End If
If myInt < 0 Then
Return " - "
End If
Return " "
End Function
End Class
Public Shared Sub Main ()
Dim mySC As New mySampleClass ()
Dim myD1 As New myMethodDelegate (AddressOf mySC. myStringMethod )
Dim myD2 As New myMethodDelegate (AddressOf mySampleClass. mySignMethod )
Console. WriteLine (" {0} is {1}; use the sign " " {2} " " . " , 5, myD1 (5), myD2 (5))
Console. WriteLine (" {0} is {1}; use the sign " " {2} " " . " , - 3, myD1 (- 3), myD2 (- 3))
Console. WriteLine (" {0} is {1}; use the sign " " {2} " " . " , 0, myD1 (0), myD2 (0))
End Sub
End Class
|
Les membres d'un délégué sont les membres hérités de la classe System.Delegate. Un délégué contient également un ensemble de constructeurs et de méthodes définis par le système.
L'utilité des délégués réside dans leur anonymat. L'exemple suivant illustre une méthode MultiCall qui appelle de façon répétitive une instance SimpleDelegate :
Sub MultiCall (d As SimpleDelegate, count As Integer)
Dim i As Integer
For i = 0 To count - 1
d ()
Next i
End Sub
|
Pour la méthode MultiCall, l'identité de la méthode cible de SimpleDelegate n'a pas d'importance, pas plus que l'accessibilité qu'a cette méthode, ni le fait qu'il s'agisse d'une méthode Shared ou non partagée.
La seule chose qui importe est que la signature de la méthode soit compatible avec SimpleDelegate.
XIII-C-3. Délégué et appel asynchrone
Quand une procédure A appelle une autre procédure B, cela se passe de manière synchrone: pendant l'exécution de la procédure B, la procédure A est en attente, elle se poursuit après le retour de la procédure B.
Si vous appelé la procédure B à partir d'un délégué avec BeginInvoke , le fonctionnement sera asynchrone, c'est à dire que les 2 procédures se dérouleront en parallèle.
Si la méthode BeginInvoke est appelée, le Common Language Runtime mettra la demande en file d'attente et le retour à l'appelant sera immédiat. La méthode cible sera appelée sur un autre thread . Le thread d'origine, qui a soumis la demande, est libre de poursuivre en parallèle son exécution. La méthode BeginInvoke est donc utilisée pour établir l'appel asynchrone. Elle possède les mêmes paramètres que la méthode à exécuter de façon asynchrone, plus deux paramètres. Le premier paramètre supplémentaire sert quand l'appel asynchrone se termine (voir plus bas); le second paramètre supplémentaire sert à fournir un objet quelconque.
Delegate Function myDelegate (myInt As Integer) As [String ]
Dim ta As New myMethodDelegate (AddressOf MyMethode)
ta. BeginInvoque (2 ,Nothing , Nothing )
|
Ici on a exécuté dans un autre thread, en parallèle, MyMethode.
On reprend l'exemple bête, d'une fonction qui transforme un integer en String.
Public Shared Function myMethode (myInt As Integer) As [String ]
Return myInt. ToString
End Function
|
Le problème est de savoir quand MyMethode se termine et éventuellement récupérer les résultats de MyMethode.
Là intervient le premier paramètre supplémentaire: il est chargé d'indiquer l'adresse de la procédure à exécuter lorsque l'opération asynchrone se termine.
ta. BeginInvoque (2 ,AdresssOf AfficheResultat, Nothing )
|
Ici quand ta se termine cela exécute la procédure AfficheResultat.
Il faut donc créer une procédure AfficheResultat avec comme paramètre une variable de type IAsyncResult qui permet de récupérer le résultat.
Sub AfficheResultat (By Val ia As IAsyncResult)
Dim Resultat As String
Resultat= ta. EndInvoke (ia)
End Sub
|
La méthode EndInvoke est utilisée pour obtenir la valeur de retour de la fonction et les paramètres de sortie.
On peut aussi 'surveiller' si ta est terminé et récupérer le résultat:
Il faut déclarer une variable de type IAsyncResult:
Quand on instance ta avec BeginInvoke cela retourne un IAsyncResult si on écrit:
ia= ta. BeginInvoque (2 ,Nothing , Nothing )
|
Il suffit de tester ia avec ia.IsCompleted pour voir si ta est terminé; a titre d'exemple créons un bouton testant si la procédure asynchrone est terminée.
Private Sub Button1_Click (. . )
Dim Resultat As String
If ia. IsCompleted Then
Resultat= ta. EndInvoke (ia)
Else
MsgBox (" Traitement en cours.. " )
End If
End Sub
|
Il faut cliquer sur le bouton à intervalle régulier pour voir si ta est terminé. (L'exemple est débile car tout cela à de l'intérêt que si la procédure asynchrone est très longue.
XIII-C-4. Délégué et évènement
Les délégués sont utilisés pour lier les événements aux méthodes servant à les gérer. Lorsque l'événement se produit, le délégué appelle la méthode liée.
Délégué crée automatiquement par Visual Basic .NET
Dans le cas d'événements associés à des formulaires ou contrôles, Visual Basic .NET crée automatiquement un gestionnaire d'événements et l'associe à un événement.
Effectivement, en mode design, lorsque vous double-cliquez sur un bouton de commande dans un formulaire, Visual Basic .NET crée automatiquement un gestionnaire d'événements vide et une variable WithEvents pour le bouton de commande, comme dans le code suivant:
Dans la Region " Code généré par le Concepteur Windows Form " il y a:
Friend WithEvents Button1 As System. Windows . Forms . Button
Protected Sub Button1_Click (ByVal sender As System. Object , _
ByVal e As System. EventArgs ) Handles Button1. Click
End Sub
|
WithEvents indique que l'objet Button1 a des évènements.
Le terme Handles provoque l'association d'un événement (Button1.Click situé après Handles) à un gestionnaire d'événements ( la Sub Button1_Click; la Sub pourrait d'ailleurs se nommer différemment, cela n'a pas d'importance).
L'association d'événements aux gestionnaires d'événements se fait au moment de la compilation et ne peut pas être modifiée.
Lorsque vous cliquez sur un Button1 cela déclenche bien la Sub Button1_Click.
Délégué et évènements crées par vous:
On reprend les mêmes concepts que dans le chapitre sur le création de contrôles par code.
Lorsque vous crée par code de toute pièce des contrôles, vous pouvez faire de même avec Handles:
Déclaration dans la partie déclaration du module(en haut):
Private WithEvents Button1 As New Button
Me. Controls . Add (Button1)
Sub OnClique ( sender As Object, EvArg As EventArgs) Handles Button1. Click
End Sub
|
La aussi, l'association d'événements aux gestionnaires d'événements se fait au moment de la compilation et ne peut pas être modifiée. On a bien crée un délégué.
Vous pouvez aussi utiliser la méthode AddHandler:
Dim TB As New System. Windows . Forms . TextBox
Me. Controls . Add (TB)
AddHandler TB. Keyup , AddressOf TextboxKeyup.
Sub TextboxKeyup. (ByVal sender As Object, ByVal e As KeyEventArgs)
. . .
End Sub
|
AddHandler permet donc d'associer à l'évènement TB.Keyup la Sub TextboxKeyup. On a bien crée un délégué.
le mot clé addhandler permet d'associer un événement à une procédure au moment de l'exécution et peut être annulé par RemoveHandler.
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.
|