Accueil
Club Emploi Blogs   TV   Dév. Web PHP XML Python Autres 2D-3D-Jeux Sécurité Windows Linux PC Mac
Accueil Conception Java DotNET Visual Basic  C  C++ Delphi Eclipse MS-Office SQL & SGBD Oracle  4D  Business Intelligence

Cours VB.NET

Date 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:

  Me.Name = "Form1"
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()

'Cet appel est requis par le Concepteur Windows Form.

InitializeComponent()

'Ajoutez une initialisation quelconque après l'appel InitializeComponent()

End Sub


'La méthode substituée Dispose du formulaire pour nettoyer la liste des composants.

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

'Requis par le Concepteur Windows Form

Private components As System.ComponentModel.IContainer

'REMARQUE : la procédure suivante est requise par le Concepteur Windows Form

'Elle peut être modifiée en utilisant le Concepteur Windows Form.

'Ne la modifiez pas en utilisant l'éditeur de code.

<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

'

'Form1

'

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()

'

'Button1

'

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()

' This call is required by the Windows Form Designer.

InitializeComponent()    '<=======Appel d'une routine qui 'initialise les composants de la form

' Add any initialization after the InitializeComponent() call.

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

'Form overrides dispose to clean up the component list.

<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

'Required by the Windows Form Designer

Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer

'It can be modified using the Windows Form Designer.

'Do not modify it using the code editor.

<System.Diagnostics.DebuggerStepThrough()> _

Private Sub InitializeComponent()

Me.Button1 = New System.Windows.Forms.Button

Me.Label1 = New System.Windows.Forms.Label

Me.SuspendLayout()

'

'Button1

'

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()

        ' Cet appel est requis par le concepteur.
        InitializeComponent()

        ' Ajoutez une initialisation quelconque après l'appel 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

    'Form remplace la méthode Dispose pour nettoyer la liste des composants.
    <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

    'Requise par le Concepteur Windows Form
    Private components As System.ComponentModel.IContainer

    'REMARQUE : la procédure suivante est requise par le Concepteur Windows Form
    'Elle peut être modifiée à l'aide du Concepteur Windows Form.  
    'Ne la modifiez pas à l'aide de l'éditeur de code.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        Me.Button1 = New System.Windows.Forms.Button()
        Me.Label5 = New System.Windows.Forms.Label()
        Me.SuspendLayout()
        '
        'Button1
        '
        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
        '
        'Label5
        '
        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"
        '
        'Form1
        '
        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.

 'Création du bouton
        Me.Button1 = New System.Windows.Forms.Button()
      
'Propriété du bouton
        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
        
 'Permet au bouton de recevoir des évènement       
        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) 'Appel à la méthode de la classe de base, ce qui dessine le bouton

Dim myPen As New Pen(Color.Purple, 3)

e.Graphics.DrawRectangle(myPen, 3, 3, Me.Width - 6, Me.Height - 6) 'Ajoute un cadre sur le dessin du bouton

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):

Delegate Sub D()
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() 'On crée un délégué sans paramètres

Module Test
   
   Sub MaSub() 'On crée une Sub
      System.Console.WriteLine("Test")
   End Sub
   
   Sub Main()
      Dim Mondelegue As New SimpleDelegate(AddressOf MaSub) 'le délégué pointe sur la Sub
      Mondelegue()	'On utilise le délégué
   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

' Declares un delegate à partir d'une méthode qui accepte un paramètre integer et retourne une String.
Delegate Function myMethodDelegate(myInt As Integer) As [String]

' Definir les méthodes.
Public Class mySampleClass

' Definir une méthode d'instance.
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 'myStringMethod

' Definir une méthode de classe.
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 'mySignMethod
End Class 'mySampleClass

'Utilisation du délégué

Public Shared Sub Main()

' Instanciation de délégué pour chaque méthode.
Dim mySC As New mySampleClass()
Dim myD1 As New myMethodDelegate(AddressOf mySC.myStringMethod)
Dim myD2 As New myMethodDelegate(AddressOf mySampleClass.mySignMethod)

'Utilisation des délégués.
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 'Main

End Class 'SamplesDelegate


'Le code produit les sorties suivantes:
'
'5 is positive; use the sign "+".
'-3 is negative; use the sign "-".
'0 is zero; use the sign "".
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)  'On met les 2 paramètres supplémentaire à Nothing pour le momment.
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:

Dim ia As 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.

Vos questions techniques : forum d'entraide Accueil - Publiez vos articles, tutoriels, cours et rejoignez-nous dans l'équipe de rédaction du club d'entraide des développeurs francophones. Nous contacter - Copyright 2000..2005 www.developpez.com