346
Projet de développement ou développements d’applications de gestion en VB.Net Philippe ROBILLARD

VB Complet

  • Upload
    phrwav

  • View
    87

  • Download
    10

Embed Size (px)

DESCRIPTION

Syllabus de cours de VB (toutes les bases, jusque Visual Studio 2010)

Citation preview

Page 1: VB Complet

Projet de développement ou

développements d’applications de gestion en

VB.Net

Philippe ROBILLARD

Page 2: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net - i

Plan du cours Le cours est scindé en plusieurs parties précédées d’une liste d’énoncés d’exercices. Le syllabus de chaque partie se distingue des autres par le préfixe attaché à chaque numéro de page. Le cours est destiné aux débutants en programmation et à la mise à niveau de programmeurs chevronnés n’ayant pas l’expérience du .Net. Les différents points de la matière doivent être abordés de manière plus ou moins détaillée et à un rythme plus ou moins soutenu selon le niveau des participants. Pages VBLab Enoncés d’exercices divers

Les pages LaboVB reprennent tous les énoncés d’exercices appropriés à l’ensemble du cours. Chaque énoncé désigne la partie du cours pour laquelle il est pertinent.

Pages VBNF Notions fondamentales de programmation en VB.Net

Les pages VBNF sont destinées à tous. Pour les débutants, certaines pages peuvent être détaillées pendant les leçons et d’autres sommairement abordées. Ces pages doivent être traitées inversement lorsque le cours est dispensé à des programmeurs. Elles exposent les structures des données et les moyens de traitements sous VB.Net.

Pages VBPV Bases de la programmation visuelle et événementielle en VB.Net

Les pages VBPV s’adressent également à tous les étudiants. Elles expliquent les outils de la programmation d’applications pour l’environnement graphique de Windows.

Pages VBPA Programmation avancée en VB.Net

Les pages VBPA sont destinées aux programmeurs qui, maîtrisant bien la programmation visuelle, souhaitent doper leurs applications en y exploitant au mieux les services du Framework.

Pages VBOBJ Programmation orientée objet en VB.Net

Les pages VBOBJ sont uniquement destinées aux programmeurs qui maîtrisent la programmation procédurale et qui souhaitent programmer des applications conçues en orienté objet. Le cours expose les concepts de base de la POO et détaille leur mise en œuvre sous .Net.

Pages VBDB Programmation orientée bases de données en VB.Net

Les pages VBDB s’adressent à tous. Elles expliquent les moyens d’accès aux bases de données et leur exploitation.

Pages VBWEB Eléments de programmation orientée Internet en VB.Net

Les pages VBWEB sont destinées aux programmeurs et aux débutants qui ont déjà acquis par ailleurs des connaissances de la programmation pour Internet. Elles traitent essentiellement de trois concepts : les pages ASP, les services Web et les applications client/serveur.

Pages VBCD Compléments divers en VB.Net

Les pages VBCD présentent divers sujets qui ne sont pas exposés dans le cadre du cours. Elles livrent des compléments d’informations que le lecteur peut consulter selon ses besoins. Elles sont notamment destinées à la présentation d’assistants de l’environnement DotNet, comme l’assistant d’empaquetage par exemple.

Avertissement Les programmes repris dans ce cours sont censés être illustratifs et non être à toute épreuve. Bien que généralement de bonne facture, ils n’ont pas subi de tests approfondis. Le programmeur souhaitant en intégrer dans un développement aura soin de pratiquer les tests nécessaires à la fiabilité de son application.

Page 3: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net - ii

Conventions d'écriture Afin de permettre au lecteur de différencier rapidement diverses parties du texte, la rédaction et la mise en page de ce cours sont réalisées autant que possible dans le respect des règles suivantes. Mots du langage et mots du programmeur. Les mots des langages sont écrits dans le texte courant en Courier New gras. Il en est de même pour les mots utilisés par le programmeur dans les lignes de codes des exemples, mais ceux-ci sont francisés. Exemples :

… le contrôle d’accès n'est pas assuré car la variable Cinq déclarée Public est accessible … … les portées Public et Friend sont …

Programme complet et extraits. Les lignes de codes des exemples sont cadrées à gauche lorsqu'elles constituent un programme complet, même si son code est entrecoupé de textes explicatifs, et elles sont décalées d'un retrait vers la droite lorsqu'elles constituent un extrait de programme. Exemple de programme complet : Module MonModule Sub Main() Console.WriteLine("Bonjour") End Sub End Module Exemple d'extrait de programme :

Sub Main() Console.WriteLine("Bonjour") End Sub

Codes et commentaires. Les lignes et fins de lignes qui sont des commentaires sont écrites en plus petit et en italique. Certaines d'entre elles présentent des résultats d'affichages : l'italique est supprimé pour les caractères effectivement affichés. Exemples : ' La Sub Main suivante a pour but d'afficher le mot Bonjour à l'écran

Sub Main() ' Entête de la procédure Console.WriteLine("Bonjour") ' Affichage : Bonjour End Sub ' Fin de la procédure

Longues lignes de codes. Les lignes de codes trop longues pour tenir sur la largeur de la page sont continuées cadrées à droite à la ligne suivante. Cette typographie signifie donc que le code ainsi représenté doit être programmé sur une seule ligne. Exemple :

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

Divers, abus de langage, particularités. Les mots dont le sens premier est détourné, ceux représentant pour la première fois un objet ou un concept particulier, ceux dont la définition est donnée plus loin, ou simplement ceux qui méritent une attention spéciale dans le contexte, et qui ne sont pas des mots du langage, ni des extraits des lignes de codes des exemples, sont écrits en italique dans le texte courant. Exemples :

… la variable de type valeur est bien traitée comme telle … … seule la référence d'un membre de type référence est conservée …

Page 4: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net - iii

Lectures utiles (offertes sous format électronique en début de cours) * Christophe Darmangeat. – Algorithmique et programmation – Adaptation (avec l’autorisation de l’auteur) à la

programmation sous VB.Net du cours de Ch. Darmangeat (Université Paris 7 – http://www.pise.info). * Eléments de SQL : éléments d’algèbre relationnelle (non mathématique) et approche inédite de conception de

requêtes. Table des matières des pages VBNF : Notions fondamentales de programmation en VB.Net Historique simplifié du Basic ............................................................................................................................................. 4 La technologie .NET et Visual Studio .Net ........................................................................................................................ 4 L’environnement de développement Visual Studio ............................................................................................................ 5

Application Console ................................................................................................................................................. 6 Application Windows ............................................................................................................................................... 7

Les entrées et les sorties de données ................................................................................................................................. 10 Applications Console ............................................................................................................................................. 10

Les entrées.................................................................................................................................................... 10 Les sorties .................................................................................................................................................... 11

Applications Windows ........................................................................................................................................... 12 Les types et structures de données .................................................................................................................................... 13

Brève révision de notions fondamentales ............................................................................................................... 13 Les variables ................................................................................................................................................ 13 Les structures composées (tableaux) ............................................................................................................ 13 Les structures complexes (types utilisateurs) ............................................................................................... 14

Déclarations des variables. ..................................................................................................................................... 15 Les types de données .............................................................................................................................................. 19 Notation littérale de données .................................................................................................................................. 20 Déclaration des tableaux ........................................................................................................................................ 20 Définition des structures ......................................................................................................................................... 22

Les structures de tableaux ............................................................................................................................ 23 Les tableaux de structures ............................................................................................................................ 23

Les énumérations .................................................................................................................................................... 24 Les instructions de contrôles et moyens de traitements .................................................................................................... 25

Brève révision de notions fondamentales ............................................................................................................... 25 La séquence .................................................................................................................................................. 25 La rupture de séquence ................................................................................................................................ 25 L’alternative ................................................................................................................................................. 25 Les itérations ................................................................................................................................................ 25

Ni contrôle, ni traitement ........................................................................................................................................ 26 Les commentaires ........................................................................................................................................ 26 La continuation ............................................................................................................................................ 26 L’économie de dactylographie ..................................................................................................................... 26 Le séparateur d’instructions ......................................................................................................................... 26

Les instructions de contrôles .................................................................................................................................. 27 La rupture de séquence ................................................................................................................................ 27 L’alternative ................................................................................................................................................. 27 La sélection .................................................................................................................................................. 29 Les itérations ................................................................................................................................................ 30

Les boucles logiques .......................................................................................................................... 30 La boucle arithmétique ...................................................................................................................... 31 Une boucle ni logique, ni arithmétique .............................................................................................. 32

L’appel de sous-programmes ....................................................................................................................... 32 L’exécution de programmes externes. ......................................................................................................... 33 Le mot de la fin ............................................................................................................................................ 33

Les moyens de traitements ..................................................................................................................................... 34 Les opérateurs .............................................................................................................................................. 34

L’affectation ...................................................................................................................................... 34 La concaténation ................................................................................................................................ 34 Les opérateurs arithmétiques ............................................................................................................. 34 Les formes contractées de l’affectation ............................................................................................. 34

Page 5: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net - iv

Les opérateurs relationnels ................................................................................................................ 34 Les opérateurs logiques ..................................................................................................................... 35 Les opérateurs binaires ...................................................................................................................... 36 Ordre d’évaluation des expressions et hiérarchie des opérateurs ...................................................... 36

Les procédures et fonctions intrinsèques, et les méthodes ........................................................................... 36 Déclarations des procédures et des fonctions ............................................................................................... 37

Déclarations des arguments des procédures, fonctions et méthodes ................................................. 39 Type de donnée des arguments .......................................................................................................... 41 Valeur de retour des fonctions ........................................................................................................... 41

La récursivité ................................................................................................................................................ 42

Table des matières des pages VBPV : Bases de la programmation visuelle et événementielle en VB.Net Programmation orientée objet, visuelle et événementielle ................................................................................................. 5 Une application Windows (Form et Button) .................................................................................................................. 6

Deux propriétés pour commencer : Name et Text ................................................................................................. 9 Propriétés généralement associées à la propriété Text : BackColor, ForeColor et Font .............................. 10

Quelques propriétés communes à la plupart des composants visuels .................................................................... 10 Quelques méthodes communes à la plupart des composants visuels ..................................................................... 11 Quelques événements communs à la plupart des composants visuels ................................................................... 11

Débogage et gestion des erreurs ....................................................................................................................................... 13 Le pas à pas ............................................................................................................................................................ 14 Le point d’arrêt ....................................................................................................................................................... 14 L’évaluation des variables en cours d’exécution .................................................................................................... 15 L’insertion d’envois de messages ........................................................................................................................... 15 La gestion des erreurs ............................................................................................................................................. 17 Générer des erreurs ................................................................................................................................................. 18

L’essentiel des composants visuels et de leurs membres ................................................................................................. 19 Le formulaire : Form ............................................................................................................................................. 19

Propriétés ..................................................................................................................................................... 19 Méthodes ...................................................................................................................................................... 21 Evénements .................................................................................................................................................. 22

Membres communs à la plupart des composants visibles ...................................................................................... 22 Propriétés ..................................................................................................................................................... 22 Méthode ....................................................................................................................................................... 23 Evénements .................................................................................................................................................. 23

L’étiquette : Label ............................................................................................................................................... 23 Propriétés ..................................................................................................................................................... 23

La boîte de texte : TextBox ................................................................................................................................. 24 Propriétés ..................................................................................................................................................... 24 Méthodes ...................................................................................................................................................... 25 Evénement .................................................................................................................................................... 25

La case à cocher : CheckBox ............................................................................................................................... 25 Propriétés ..................................................................................................................................................... 25 Evénements .................................................................................................................................................. 25

Le bouton radio : RadioButton ......................................................................................................................... 26 Les groupes d’objets : GroupBox et Panel ........................................................................................................ 27

Différences essentielles ................................................................................................................................ 27 L’étiquette hyperlien : LinkLabel ...................................................................................................................... 27 La boîte de liste : ListBox .................................................................................................................................. 28

Propriétés ..................................................................................................................................................... 28 Méthodes ...................................................................................................................................................... 29 Evénement .................................................................................................................................................... 29 Quelques exemples ...................................................................................................................................... 29

La liste de cases à cocher : CheckedListBox ................................................................................................... 30 La liste déroulante : ComboBox ............................................................................................................................ 30 La liste de visualisation : ListView .................................................................................................................... 30

Propriétés ..................................................................................................................................................... 30 Méthodes ...................................................................................................................................................... 32

Page 6: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net - v

Evénements .................................................................................................................................................. 32 Quelques exemples ...................................................................................................................................... 32

Les vues en arborescence : TreeView ................................................................................................................. 34 Propriétés ..................................................................................................................................................... 34 Méthodes ...................................................................................................................................................... 34 Evénément .................................................................................................................................................... 35 Lecture d’un TreeView ................................................................................................................................ 35

Le calendrier mensuel : MonthCalendar .......................................................................................................... 36 Le sélectionneur de date : DateTimePicker .................................................................................................... 36 La boîte à image : PictureBox .......................................................................................................................... 37 Le diviseur : Splitter ........................................................................................................................................ 38

Une réalisation ............................................................................................................................................. 38 Son code ............................................................................................................................................. 38 Son résultat ........................................................................................................................................ 38

Le contrôle à onglets : TabControl ................................................................................................................... 39 Le menu : MainMenu et MenuStrip .............................................................................................................. 40 La minuterie : Timer ............................................................................................................................................ 41

Propriétés ..................................................................................................................................................... 41 Méthodes ...................................................................................................................................................... 41 Evénement .................................................................................................................................................... 41 Exemple ....................................................................................................................................................... 41

Les boîtes de dialogue ...................................................................................................................................................... 42 L’ouverture de fichiers : OpenFileDialog ...................................................................................................... 42

Propriétés ..................................................................................................................................................... 42 Méthode ....................................................................................................................................................... 43 Exemple ....................................................................................................................................................... 43

L’enregistrement de fichiers : SaveFileDialog .............................................................................................. 43 L’explorateur de dossiers : FolderBrowserDialog ....................................................................................... 43 Les polices de caractères : FontDialog ............................................................................................................. 44 Les couleurs : ColorDialog .............................................................................................................................. 44 L’impression des documents .................................................................................................................................. 45

Le moteur d’impression : PrintDocument .................................................................................................... 45 La mise en page : PageSetupDialog ............................................................................................................. 47 La sélection de l’imprimante : PrintDialog .................................................................................................. 47 La fenêtre de prévisualisation : PrintPreviewDialog .................................................................................... 47 Le composant de prévisualisation : PrintPreviewControl ............................................................................ 47 Exemple récapitulatif ................................................................................................................................... 47

Programmation multi formulaires ..................................................................................................................................... 49 L’ouverture de formulaires secondaires ................................................................................................................. 49 Le passage de données entre formulaires ............................................................................................................... 49 L’InputBox à l’ancienne ......................................................................................................................................... 55 Résumé des modes de communications abordés .................................................................................................... 56

L’exploitation des mémoires de masse ............................................................................................................................. 57 Les dossiers ............................................................................................................................................................ 57

Liste des unités ............................................................................................................................................. 57 Liste des dossiers d’une unité ...................................................................................................................... 57 Liste des fichiers d’un dossier ...................................................................................................................... 57 Liste des sous dossiers et fichiers d’un dossier ............................................................................................ 57 Créer un dossier ........................................................................................................................................... 58 Copier un dossier ......................................................................................................................................... 58 Déplacer un dossier ...................................................................................................................................... 58 Renommer un dossier ................................................................................................................................... 58 Supprimer un dossier ................................................................................................................................... 58

Les fichiers ............................................................................................................................................................. 59 Créer un fichier ............................................................................................................................................ 59 Copier un fichier .......................................................................................................................................... 59 Déplacer un fichier ....................................................................................................................................... 59 Renommer un fichier ................................................................................................................................... 59 Supprimer un fichier .................................................................................................................................... 59

Page 7: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net - vi

Création, enregistrement et lecture d’un fichier texte .................................................................................. 60 Création, enregistrement et lecture d’un fichier de données à accès séquentiel .......................................... 61 Création, enregistrement et lecture d’un fichier de données à accès aléatoire ............................................. 62

Les modes d’ouvertures ..................................................................................................................... 62 Les types d’accès ............................................................................................................................... 62 Tableau récapitulatif des associations permises du mode d’ouverture et du type d’accès ................ 62 Quelques membres de la classe FileStream ................................................................................. 62 L’écriture et la lecture du fichier ....................................................................................................... 63 Exemple complet ............................................................................................................................... 64 La protection des données des fichiers lors des accès simultanés ..................................................... 70

Création, enregistrement et lecture d’un fichier binaire ............................................................................... 72 Inventaire des principaux outils concernant les dossiers et les fichiers ................................................................. 75

Méthodes communes des classes Directory et File ...................................................................................... 75 Propriétés communes des classes DirectoryInfo et FileInfo ........................................................................ 75 Méthodes communes des classes DirectoryInfo et FileInfo ......................................................................... 75 Méthodes spécifiques de la classe Directory................................................................................................ 75 Propriétés spécifiques de la classe DirectoryInfo ........................................................................................ 75 Méthodes spécifiques de la classe DirectoryInfo ......................................................................................... 75 Méthodes spécifiques de la classe File ......................................................................................................... 76 Propriétés spécifiques de la classe FileInfo.................................................................................................. 76 Méthodes spécifiques de la classe FileInfo .................................................................................................. 76 La classe Stream ........................................................................................................................................... 77 Propriétés et méthodes de la classe StreamWriter ....................................................................................... 77 Méthodes de la classe StreamReader ........................................................................................................... 77 Méthodes de la classe BinaryWriter ............................................................................................................ 77 Méthodes de la classe BinaryReader ............................................................................................................ 78

Table des matières des pages VBPA : Programmation avancée en VB.Net Autant savoir....................................................................................................................................................................... 3

Les traitements en ligne de commande .................................................................................................................... 3 Les assemblages ....................................................................................................................................................... 4

Manifeste de l'assemblage .............................................................................................................................. 5 Assemblages mono fichier et multi fichiers ................................................................................................... 5 Le désassemblage ........................................................................................................................................... 6 Espace de noms .............................................................................................................................................. 7 Nom complet .................................................................................................................................................. 7 Instructions au niveau de l'espace de noms .................................................................................................... 7 Références, Imports et Declare ...................................................................................................................... 8

Arguments d'appel d'une application ........................................................................................................................ 9 Application Console ....................................................................................................................................... 9 Application Windows .................................................................................................................................... 9

A partir d’une session Dos ................................................................................................................... 9 A partir de Windows .......................................................................................................................... 10

Les régions ............................................................................................................................................................. 11 Les fonctionnalités du Framework ................................................................................................................................... 12

L'espace de noms System ....................................................................................................................................... 12 La classe System.Object .............................................................................................................................. 12

Méthodes ............................................................................................................................................ 12 La classe String ............................................................................................................................................ 13

Propriété ............................................................................................................................................. 13 Méthodes ............................................................................................................................................ 13

La classe DateTime ...................................................................................................................................... 16 Propriétés ........................................................................................................................................... 17 Méthodes ............................................................................................................................................ 17

La classe RegEx ........................................................................................................................................... 19 Membres ............................................................................................................................................ 22

La classe Math ............................................................................................................................................. 25 Propriétés ........................................................................................................................................... 25 Méthodes ............................................................................................................................................ 25

Page 8: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net - vii

La classe VBMath ........................................................................................................................................ 25 L'espace de nom System.Drawing .......................................................................................................................... 26

La classe Graphics ....................................................................................................................................... 26 L'espace de noms System.Collections .................................................................................................................... 31

Les classes offertes ....................................................................................................................................... 31 Membres communs à la plupart des collections........................................................................................... 31

Propriétes ........................................................................................................................................... 31 Méthodes ............................................................................................................................................ 31

La classe ArrayList ...................................................................................................................................... 32 Propriété ............................................................................................................................................. 32 Méthodes ............................................................................................................................................ 32

La classe BitArray ........................................................................................................................................ 35 Propriété ............................................................................................................................................. 35 Méthodes ............................................................................................................................................ 35

La classe Hashtable ...................................................................................................................................... 36 Propriétés ........................................................................................................................................... 36 Méthodes ............................................................................................................................................ 36

La classe SortedList ..................................................................................................................................... 37 Méthodes ............................................................................................................................................ 37

La classe Queue ........................................................................................................................................... 38 Méthodes ............................................................................................................................................ 38

La classe Stack ............................................................................................................................................. 39 Méthodes ............................................................................................................................................ 39

Une dernière comparaison ........................................................................................................................... 40 La classe CollectionBase ............................................................................................................................. 41 L’espace de noms Collections.Generic ........................................................................................................ 43

Table des matières des pages VBOBJ : Programmation orientée objet en VB.Net Introduction ........................................................................................................................................................................ 3

Depuis 1960 .............................................................................................................................................................. 3 Quelles différences ? ................................................................................................................................................ 3

Programmation orientée objet............................................................................................................................................. 4 Classe et structure ..................................................................................................................................................... 4 Gestion de la mémoire .............................................................................................................................................. 8

Ramasse-miettes ............................................................................................................................................. 9 Enveloppement, contrôle d’accès, encapsulation ..................................................................................................... 9

Une propriété par défaut .............................................................................................................................. 12 L'héritage ................................................................................................................................................................ 13

Programmation de l'héritage ........................................................................................................................ 14 Membre statique ................................................................................................................................ 15 Dérivations publique, protégée et privée ........................................................................................... 18 Constructeurs et destructeurs ............................................................................................................. 19 Constructeur par recopie .................................................................................................................... 21

Interface et Implements ................................................................................................................................ 22 Le polymorphisme .................................................................................................................................................. 26

Classe abstraite et méthode virtuelle ............................................................................................................ 26 Surdéfinition, surcharge et masquage .......................................................................................................... 30 La surcharge des opérateurs ......................................................................................................................... 33

L'interdiction de dériver ......................................................................................................................................... 33 Les événements ...................................................................................................................................................... 34

Emission d'événements ................................................................................................................................ 34 Réception et traitement d'événements .......................................................................................................... 35

Aspects avancés ................................................................................................................................................................ 37 La délégation .......................................................................................................................................................... 37 Programmation asynchrone .................................................................................................................................... 40

La commande asynchrone Shell ................................................................................................................... 40 Programmation de méthodes asynchrones ................................................................................................... 41

Le multithreading ................................................................................................................................................... 43

Page 9: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net - viii

Les processus actifs ...................................................................................................................................... 43 Synchrone, asynchrone et multithreading .................................................................................................... 44 Programmation et gestion du multithreading ............................................................................................... 45

Accès à une ressource partagée ......................................................................................................... 45 Accès exclusif à une ressource .......................................................................................................... 47 Instanciation d'un thread par délégation ............................................................................................ 48 Echange d'informations avec un thread ............................................................................................. 48 Le marshaling .................................................................................................................................... 51 Programmation asynchrone et multithreading : une synthèse ........................................................... 56

La persistance ......................................................................................................................................................... 57 Table des matières des pages VBDB : Programmation orientée bases de données en VB.Net L'ADO.Net .......................................................................................................................................................................... 4

L'accès aux sources de données................................................................................................................................ 4 Les modes d'accès aux bases de données ................................................................................................................. 4

Le mode connecté .......................................................................................................................................... 4 Le mode déconnecté ....................................................................................................................................... 5

Les bases de données Access .............................................................................................................................................. 5 Création d'une base Access ...................................................................................................................................... 5 Compactage d'une base Access ................................................................................................................................ 6 Réglage d'un pilote ODBC pour Access .................................................................................................................. 7

Le mode connecté ............................................................................................................................................................... 8 La connexion ............................................................................................................................................................ 8

Fonctionnalités de l'objet Connection ............................................................................................................ 8 Propriétés ............................................................................................................................................. 8 Méthodes .............................................................................................................................................. 9 Evénements .......................................................................................................................................... 9

L'objet Command ................................................................................................................................................... 10 Propriétés ........................................................................................................................................... 10 Méthodes ............................................................................................................................................ 10 Exemple ............................................................................................................................................. 11

L'objet DataReader ................................................................................................................................................. 11 Propriétés ........................................................................................................................................... 11 Méthodes ............................................................................................................................................ 12 Exemples ............................................................................................................................................ 12

L'objet Transaction ................................................................................................................................................. 13 Exemple ............................................................................................................................................. 13

Connexion polyvalente ........................................................................................................................................... 14 Obtention de la chaîne de connexion ..................................................................................................................... 15

Le mode déconnecté ......................................................................................................................................................... 16 La connexion en lecture ......................................................................................................................................... 16

L'objet DataAdapter ..................................................................................................................................... 16 Deux méthodes de lecture ............................................................................................................................ 16 Les objets DataSet et DataTable .................................................................................................................. 16

Propriété du DataSet .......................................................................................................................... 16 Propriété du DataSet et du DataTable ................................................................................................ 16 Propriétés du DataTable ..................................................................................................................... 16 Méthodes du DataSet et du DataTable............................................................................................... 17 Usage simple du DataSet ................................................................................................................... 17 Usage simple du DataTable ............................................................................................................... 18

Création d'une base de données en mémoire .......................................................................................................... 19 Sauvegarde de la base mémoire dans une base Access ................................................................................ 22

Utilisation des relations et contraintes d'intégrité ................................................................................................... 23 Lectures des données ................................................................................................................................... 23

Lecture linéaire .................................................................................................................................. 23 Lectures hiérarchiques ....................................................................................................................... 23

Les contraintes d'intégrités ........................................................................................................................... 24 Quelques cas ...................................................................................................................................... 24

Page 10: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net - ix

Mise à jour de la base source et gestion des conflits .............................................................................................. 25 Bases de données et composants visuels .......................................................................................................................... 29

La liaison à une base de données ............................................................................................................................ 29 La présentation des données ................................................................................................................................... 29

Lecture et affichage d'une simple table ........................................................................................................ 31 Lecture et affichage de tables liées par une relation .................................................................................... 31 La recherche d'un enregistrement................................................................................................................. 33

La manipulation des données ................................................................................................................................. 34 L'édition contrôlée d'un enregistrement ....................................................................................................... 34 L'ajout d'un enregistrement .......................................................................................................................... 36 La suppression d'un enregistrement ............................................................................................................. 37

La mise à jour de la base ........................................................................................................................................ 38 Le composant DataGridView ................................................................................................................................. 40 L’impression des données par CrystalReport ......................................................................................................... 41

Table des matières des pages VBWEB : Eléments de programmation orientée Internet en VB.Net Introduction ........................................................................................................................................................................ 3 Les Web Forms ................................................................................................................................................................... 4

La page HTML ......................................................................................................................................................... 4 La page ASPX .......................................................................................................................................................... 6

Les composants ASP visuels .......................................................................................................................... 8 L'objet Page ......................................................................................................................................... 9 Le composant TextBox ...................................................................................................................... 11 Le composant HyperLink .................................................................................................................. 11 Le composant LinkButton ................................................................................................................. 11 Le composant ImageButton ............................................................................................................... 11 Le composant Image .......................................................................................................................... 12 Le composant HiddenField ................................................................................................................ 12 Le composant Menu........................................................................................................................... 12 Le composant Table ........................................................................................................................... 13 Les composants CheckBox, CheckBoxList, RadioButton et RadioButtonList ................................. 13 Le composant Panel ........................................................................................................................... 16 Les composants View et MultiView .................................................................................................. 16 Le composant FileUpload .................................................................................................................. 17

Liaisons de composants à une base de données ........................................................................................... 18 Connexion à une base Access ............................................................................................................ 18 Les composants ListBox et DropDownList ....................................................................................... 19

Les services Web .............................................................................................................................................................. 20 Introduction ............................................................................................................................................................ 20

Cycle de fonctionnement ............................................................................................................................. 20 Utilisation d'un service Web .................................................................................................................................. 21

Recherche du service ................................................................................................................................... 21 Mise en place du service .............................................................................................................................. 21 Exploitation du service ................................................................................................................................. 23

Création d'un service Web ...................................................................................................................................... 24 Exploitation du service ................................................................................................................................. 26

Exemple d'exploitation synchrone ..................................................................................................... 26 Exemple d'exploitation asynchrone ................................................................................................... 26

Création et exploitation d'un service avec contrôle d'accès ......................................................................... 27 Le Net Remoting .............................................................................................................................................................. 29

Le choix entre Service Web et Net Remoting ........................................................................................................ 29 Fonctionnement du Net Remoting ......................................................................................................................... 29 Programmation du Net Remoting ........................................................................................................................... 30

Création d'une application serveur élémentaire ........................................................................................... 30 Création d'une application cliente ................................................................................................................ 31 Démarrage d'une application serveur par une application cliente ................................................................ 32

Page 11: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net - x

Table des matières des pages VBCD : Compléments divers en VB.Net Le Basic : son histoire en un coup d’œil ............................................................................................................................. 3 Le Visual Studio et "son" framework ................................................................................................................................. 3 Améliorations du langage dans Visual Basic 2005 ............................................................................................................ 4

Introduction .............................................................................................................................................................. 4 My ............................................................................................................................................................................ 4 Les commentaires XML ........................................................................................................................................... 6 Génériques ................................................................................................................................................................ 7 Instruction Using ...................................................................................................................................................... 8 Instruction Continue ................................................................................................................................................. 8 Mot Global ............................................................................................................................................................... 8 Opérateur IsNot ........................................................................................................................................................ 9 Instruction TryCast ................................................................................................................................................... 9 Surcharge des opérateurs et opérateurs de conversion ............................................................................................. 9 Accessibilité des mécanismes d'accès aux propriétés .............................................................................................. 9 Limites explicites des tableaux ................................................................................................................................. 9 Accesseurs aux événements personnalisés ............................................................................................................. 10 Types partiels ......................................................................................................................................................... 10 Événements de niveau application ......................................................................................................................... 11 Avertissements du compilateur .............................................................................................................................. 11 Nouveaux types numériques .................................................................................................................................. 11 Instances par défaut ................................................................................................................................................ 12

La documentation du code source .................................................................................................................................... 13 Commentaires XML ............................................................................................................................................... 13 Les commentaires ordinaires .................................................................................................................................. 14 Les commentaires spécialisés ................................................................................................................................. 14 Les signets .............................................................................................................................................................. 15

Usage de fichiers de paramètres ....................................................................................................................................... 16 La lecture de fichiers XML .................................................................................................................................... 16 Le fichier ressource programmé ............................................................................................................................. 17 Synthèse des fichiers de paramètres ....................................................................................................................... 17

Fichier de ressources ajouté à l’espace My ................................................................................................... 17 Fichier de paramètres ajouté à l’espace My .................................................................................................. 17 Fichier XML de paramètres ......................................................................................................................... 17 Fichier de paramètres programmé ................................................................................................................ 17

L’empaquetage ................................................................................................................................................................. 18

Page 12: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 1

Enoncés des labos

en VB.Net

Page 13: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 2

Contenu des pages VBLab Modalités Enoncés des labos 1. BonjourConsole (/100) (cf. Pages VBNF : Application Console) 2. BonjourWindows (/100) (cf. Pages VBNF : Application Windows) 3. Débile (/200) (cf. Pages VBPV : Propriétés et événements communs à la plupart des composants visuels) c 4. Debug (/100) (cf. Pages VBNF + VBPV : Déclaration des arguments … et Débogage) 5. DesChoix (/100) (cf. Pages VBPV : CheckBox et RadioButton) c 6. MesAmis (/200) (cf. Pages VBPV : Le composant ListView) c 7. BonneImpression (/150) (cf. Pages VBPV : Menu et impression) 8. MesAmisEnFichier (/200) (cf. Pages VBPV : Les boîtes de dialogues + Exploitation des mémoires de masse) c 9. SauvezLes (/100) (cf. Pages VBPV : Minuterie) c 10. UnBonArgument (/200) (cf. Pages VBPV + VBPA : Mémoires de masse + Arguments d'appel …) c 11. Remplace (/150) (cf. Pages VBPA : La classe String) c 12. LeBonMoment (/100) (cf. Pages VBPA : DateTime et RegEx) c 13. UneCollection (/200) (cf. Pages VBNF et VBPA : Structure + CollectionBase) c 14. MesPremiers (/200) (cf. Pages VBOBJ : Encapsulation et abstraction) c 15. MesPersonnes (/300) (cf. Pages VBPA et VBOBJ : CollectionBase + Propriété par défaut) c 16. MesFractions (/100) (cf. Pages VBOBJ : Surcharge des opérateurs) c 17. EncoreDesPersonnes (/400) (cf. Pages VBPA et VBOBJ : CollectionBase + Propriété par défaut + Persistance) c 18. UneDBPersonnes (/400) (cf. Pages VBDB : Bases de données et composants visuels) c 19. CarnetDAdresses (/600) (cf. Pages VBDB : Bases de données et composants visuels) 20. AutreCarnetDAdresses (/800) (cf. Pages VBDB : Bases de données et composants visuels) Travaux complémentaires 21. Tableaux (/100) (cf. Pages VBNF : Les tableaux) 22. StructuresTableaux (/100) (cf. Pages VBNF : Les structures de tableaux) 23. TableauxStructures (/100) (cf. Pages VBNF : Les tableaux de structures) 24. DeclareProc (/100) (cf. Pages VBNF : Déclarations des procédures et des fonctions) 25. ToString (/100) (cf. Pages VBPA : La classe System.Object et la classe String) c 26. Sinus (/100) (cf. Pages VBPA : La classe Graphics) 27. Async (/200) (cf. Pages VBOBJ : Synchrone, asynchrone et multithreading) c 28. ListeDesPersonnes (/400) (cf. Pages VBPV et VBDB : ListBox, Impression et DataGridView) 29. MesAmisWeb (/400) (cf. Pages VBWEB : Les composants ASP visuels + Liaison à une base de données) c 30. JeuDuPendu (/400) (cf. Pages VBWEB : Programmation du Net Remoting) c Exercices divers 31. LesImpairs (cf. Pages VBNF: Itérations) 32. SomEntiers (cf. Pages VBNF : Itérations) 33. DesMots (cf. Pages VBNF et VBPV : Les tableaux + Boutons + Boîte de texte) c 34. MonRepertoire (cf. Pages VBPV : ListView + Impression) c 35. DevineNombre (cf. Pages VBPV : Générer des erreurs + Form + Programmation multi formulaires) c 36. ChercheEtRemplace (cf. Pages VBPV et VBPA : Mémoires de masse + Classes String et RegularExpressions) c 37. RepertoireTelephones (cf. Pages VBPA : Collections génériques) c 38. DesEtudiants (cf. Pages VBOBJ : Constructeurs et destructeurs, constructeur par recopie) c 39. MailAGogo (cf. Pages VBDB : OleDB et DataReader) c 40. DBMails (cf. Pages VBDB : Création d'une base Access + Mode connecté) c 41. DBRepertoire (cf. Pages VBDB : Bases de données et composants visuels) c Corrigé existant (document Word, sauf pour le labo 18 où il s’agit des fichiers sources de l’application)

Page 14: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 3

Modalités Chaque labo doit être considéré comme une application à part entière et, à ce titre, doit avoir un dossier qui lui est spécialement réservé sur l'ordinateur où a lieu son développement. Ce dossier doit recevoir un nom représentatif de son contenu. Pour un labo, le nom est composé du numéro et du nom du labo séparés par un souligné (ex. 1_BonjourConsole si le nom du premier labo est BonjourConsole). Ce dossier DOIT contenir TOUT le nécessaire à son labo et un document Word contenant les copies documentées (à l’image de ce qui est fait dans le support de cours) de quelques écrans représentatifs des expérimentations effectuées. Le cas échéant, ce document contiendra les explications requises par l’énoncé du labo. Certains labos ne consistent qu’en la production d’un document Word. Celui-ci doit également être stocké dans un dossier au nom du labo. Ce(s) dossier(s) de labos est (sont) compressé(s) dans un fichier dont le nom est celui de l’étudiant. Ce fichier ZIP (ou RAR) doit être transmis par e-mail à [email protected] ou à [email protected]. Ainsi donc, par exemple, l’étudiant Jules Denface envoyant les labos 1 et 2, transmettra un fichier DENFACE.ZIP qui contiendra les dossiers 1_BonjourConsole et 2_BonjourWindows, chacun de ceux ci contenant tous les fichiers du labo correspondant. Ces directives permettent d'automatiser la réception des labos et leur rangement sur le disque à partir duquel ils sont expérimentés et évalués. Aussi, les envois ne respectant pas ces directives sont refusés et leurs labos reçoivent la cote nulle. Les indications /100, /200, … représentent le poids de chaque labo en matière de cotation et sont fonction du temps nécessaire à la réalisation des labos. Ces temps sont estimés en sachant que les étudiants disposent des codes du cours en fichiers non protégés et qu’ils peuvent donc effectuer des copier/coller de ces codes offerts pour amorcer leurs labos en réalisant une grande économie de dactylographie. A défaut d’indication, le poids d’un labo vaut 100. Chaque énoncé de labo est assorti d’une indication des pages du cours auxquelles il se rapporte principalement. Le nécessaire à la réalisation d’un labo est presque toujours contenu ces pages. Les quelques vitamines parfois nécessaires sont des membres proches de ceux expliqués dans le cours et il n’est donc jamais nécessaire d’entreprendre de longues recherches dans les pages d’aide ou sur Internet pour mener à bien un labo. De plus l’objet des labos est l’expérimentation de la matière enseignée et non la mise en œuvre d’autres techniques. Pour chacun des labos, les fichiers VB comptent pour la moitié des points et le document Word pour l’autre moitié, mais un document Word seul ne compte pas. La cote attribuée au document Word n’excède jamais celle attribuée au projet VB. Pour la détermination de celle-ci, les fichiers VB sont testés et leur bon fonctionnement vérifié. La qualité du projet est également évaluée sur base des noms attribués aux différents fichiers, objets et variables qui le composent, sur base du respect de l’énoncé, de la pertinence des copies d’écrans réalisées, … Répartition des points : Les interrogations éventuelles et l’examen (ou le travail de fin de cours) fournissent ensemble une cote A sur 100. L’ensemble des labos fournit une cote B sur 100. Le poids de chacune de ces 2 cotes dans la cote globale est déterminé par la règle suivante :

Résultats obtenus pour les cotes A et B Cote globale Cote A

(Interrog. + TFF et/ou examen) Cote B

(les labos) % de A % de B

>= 60 >= 50 75 25 >= 60 < 50 50 50 <60 >= 50 100 0 <60 < 50 50 50

Échéances : Sauf indication contraire, un labo doit être transmis au plus tard la veille du cours suivant celui où a été énoncé ce labo. Un faible retard (jusqu’au lendemain à midi) entraîne la division de sa cote par 2. Au-delà, la cote devient nulle.

L’objet des labos est l’expérimentation de la matière enseignée et non la mise en œuvre d’autres techniques !

Page 15: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 4

Enoncés des labos 1. BonjourConsole (/100) (cf. Pages VBNF : Application Console)

Réalisez une application Console qui demande le nom de l'utilisateur et lui affiche ensuite Bonjour suivi du nom. Exemple supposant que l'utilisateur s'appelle Jules : Quel est ton nom ? Bonjour Jules 2. BonjourWindows (/100) (cf. Pages VBNF : Application Windows)

Réalisez une application Windows qui demande le nom de l'utilisateur et lui affiche ensuite Bonjour. Cette application doit utiliser un Label pour la question, un TextBox pour l'encodage et un MessageBox pour l'affichage demandé. 3. Débile (/200) (cf. Pages VBPV : Propriétés et événements communs à la plupart des composants visuels) c

Réalisez une application Windows ouvre un formulaire contenant un bouton en son centre. La largeur du formulaire doit être supérieure à 3 fois la largeur du bouton, et la hauteur doit être supérieure à 3 fois celle du bouton. Libellé du bouton : "Clique-moi si tu peux". Cette application doit déplacer le bouton dans une des 9 zones possibles à chaque tentative de clic. Chaque zone doit avoir être occupée par le bouton à l'occasion de 9 tentatives de clic. Les traits interrompus de l’illustration suivante ne doivent pas être implémentés dans le labo. 4. Debug (/100) (cf. Pages VBNF + VBPV : Déclaration des arguments … et Débogage)

Réalisez une application Console qui met en œuvre les codes des exemples contenus dans les pages VBNF pour illustrer la déclaration des arguments des procédures, fonctions et méthodes. Exécutez cette application en pas à pas et en évaluant diverses valeurs de sorte à vérifier les résultats annoncés dans les pages VBNF. 5. DesChoix (/100) (cf. Pages VBPV : CheckBox et RadioButton) c Réalisez une application d'expérimentation des codes proposés à propos des RadioButton et de sorte à les appliquer également aux CheckBox en indiquant dans ce cas, si un CheckBox est coché ou décoché. 6. MesAmis (/200) (cf. Pages VBPV : Le composant ListView) c

Réalisez une application de gestion des personnes avec un numéro de téléphone pour chacune. La liste des personnes doit être un composant ListView avec 2 colonnes visibles et intitulées Noms et Numéros. Le formulaire doit présenter deux TextBox, l'un pour le nom et l'autre pour le numéro. Ils doivent servir à l'encodage des informations des personnes en ajout et en recherche. Trois boutons libellés Ajout, Cherche et Supprime doivent activer les procédures correspondantes. Si lors d'un ajout, le nom de la personne encodée existe déjà dans la liste, l'ajout doit être annulé et la procédure de recherche activée. La recherche d'une personne, qui s'effectue uniquement sur le nom, doit sélectionner l'enregistrement de la personne dans la liste et, si la personne n'existe pas, un MessageBox doit en informer l'utilisateur. Si la personne existe, son numéro de téléphone doit être présenté dans le TextBox approprié. La sélection d'une personne dans la liste doit provoquer l'affichage de son nom et de son numéro de téléphone dans les TextBox correspondants.

1 2 3

4

7 8

6

9

Page 16: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 5

7. BonneImpression (/150) (cf. Pages VBPV : Menu et impression)

Expérimentez l'exemple récapitulatif proposé dans le cours à propos des impressions en remplaçant les cinq boutons par un menu. 8. MesAmisEnFichier (/200) (cf. Pages VBPV : Les boîtes de dialogues + Exploitation des mémoires de masse) c

Améliorez le labo n°6 en y intégrant les boîtes de dialogues permettant la désignation des fichiers en ouverture et à l'enregistrement et en y implémentant la sauvegarde des données de la liste, ainsi que son chargement par la lecture d'un fichier. Il convient d'ajouter deux boutons Charger et Enregistrer au formulaire du labo n°6. 9. SauvezLes (/100) (cf. Pages VBPV : Minuterie) c Améliorez le labo n°8 précédent en y intégrant une minuterie qui comptabilise le temps d'exécution de l'application à partir du moment où des modifications ont été réalisées. Toutes les 5 minutes, un MessageBox doit alors rappeler à l'utilisateur qu'il serait prudent de sauvegarder ses données. 10. UnBonArgument (/200) (cf. Pages VBPV + VBPA : Mémoires de masse + Arguments d'appel …) c Réalisez une application Windows à lancer en ligne de commande avec deux paramètres, le premier désignant le chemin complet d'un dossier et le second désignant les fichiers à inventorier. L'application ouverte doit afficher un composant ListBox présentant tous les noms de fichiers ayant l'extension désignée dans le dossier également désigné, ainsi que dans tous ses sous-dossiers. L'application doit donc réaliser le même travail que la commande Dir utilisée avec les paramètres /B et /S, comme par exemple : Dir X:\MesDonnees\*.TXT /B /S lorsque ses arguments d'appel sont X:\MesDonnees\ et *.TXT . 11. Remplace (/150) (cf. Pages VBPA : La classe String) c

Réalisez une application Windows qui recherche dans une chaîne donnée, une autre chaîne dont seuls quelques caractères de chaque extrémité sont connus, et la remplace par une chaîne également donnée. 12. LeBonMoment (/100) (cf. Pages VBPA : DateTime et RegEx) c

Réalisez une application Windows qui calcule des échéances de payements de factures sur base d'une date encodée au format jj/mm/aaaa vérifié par l'usage des expressions régulières. Les échéances calculées doivent être affichées au format Jour jj Mois aaaa et correspondre à l'une des échéances 30 ou 60 jours, de date de facture ou de fin de mois, selon le choix de l'utilisateur. 13. UneCollection (/200) (cf. Pages VBNF et VBPA : Structure + CollectionBase) c

Créez une collection de personnes et une application pour son exploitation en mode Console. La collection inspirée de la Classe MesChaines décrite à la fin des pages VBPA doit gérer des structures Personnes dont les champs sont Nom de type String, DateNais de type Date et Telephone de type String. La collection doit garder les informations ordonnées sur le champ Nom. 14. MesPremiers (/200) (cf. Pages VBOBJ : Encapsulation et abstraction) c

Créez une application qui remplit un ListBox avec les nombres premiers compris entre deux valeurs obligatoirement impaires encodées dans des TextBox. Cette application doit exploiter la classe MesPremiers décrite dans le cours. 15. MesPersonnes (/300) (cf. Pages VBPA et VBOBJ : CollectionBase + Propriété par défaut) c

Modifiez la classe ClTableauDePersonnes décrite dans le cours de sorte que les objets ClPersonne gérés soient constamment ordonnés par ordre croissant ou décroissant à la guise du programmeur client. Il faut notamment doter la classe fournie des membres SensTri, Add et ChangeOrdre, ainsi que d'une procédure locale de tri. Un constructeur approprié peut s'avérer utile. L’application doit gérer un tableau de personnes et non une collection. La consultation des pages CollectionBase peut fournir l’inspiration nécessaire à la programmation des membres demandés. Expérimentez la nouvelle classe dans une application Windows.

Page 17: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 6

16. MesFractions (/100) (cf. Pages VBOBJ : Surcharge des opérateurs) c

Complétez la classe Fraction du cours en y implémentant la soustraction, la division et les six opérateurs relationnels de base, ainsi qu'une méthode CType permettant la conversion vers un réel Double. La méthode CType permettant la conversion vers une chaîne doit subsister. Complétez aussi l'application Console du cours de sorte à permettre l'expérimentation de tous les opérateurs. 17. EncoreDesPersonnes (/400) (cf. Pages VBPA et VBOBJ : CollectionBase + Propriété par défaut + Persistance) c

Modifiez et complétez les classes ClPersonne et ClTableauDePersonnes. La classe ClPersonne doit gérer les noms et numéros de téléphones des personnes, ainsi que leurs localités. La classe ClTableauDePersonnes doit intégrer les fonctionnalités et membres tels qu'énoncés au labo n°15, mais aussi ses propres moyens de persistances. Un membre supplémentaire ChoixClef doit permettre à l'utilisateur d'obtenir la liste des personnes ordonnées sur le nom (par défaut) ou sur la localité. Expérimentez la nouvelle classe dans une application Windows qui doit permettre l'ajout de personnes ainsi que les chargements et sauvegardes de l'instance active de ClTableauDePersonnes. 18. UneDBPersonnes (/400) (cf. Pages VBDB : Bases de données et composants visuels) c

L'application de gestion des personnes et des localités présentée dans le cours à partir du titre Bases de données et composants visuels est construite progressivement au gré des concepts développés. L'objet de ce labo est de reconstituer cette application et de l'améliorer en y intégrant la sélection d'une base de données de type Access. L'usage de boîtes de dialogues permettant la désignation des fichiers en ouverture s'impose. L'utilisateur doit pouvoir choisir une base encore inexistante qui est alors créée. Pour fonctionner à partir d'une base de données vide, l'application doit permettre l'encodage distinct de localités et de personnes. C'est pourquoi, un menu (ou un jeu de boutons) doit permettre l'ajout des localités et l'ajout des personnes par des formulaires spécifiques. L'accès au formulaire MesDonnées présenté dans le cours ne peut avoir lieu tant qu'aucune personne n'est encodée, de même que l'accès au formulaire d'encodage des personnes ne peut avoir lieu tant qu'aucune localité n'est encodée. Le contrôle de l’encodage d’une personne doit être amélioré à l’instar de ce qui est fait dans le cours pour les localités (donc aussi implémenter les concepts de nature ergonomique qui permettent l’ajout et la modification à partir d’encodages sur les zones de présentation des données). 19. CarnetDAdresses (/600) (cf. Pages VBDB : Bases de données et composants visuels)

En partant de l’application du labo précédent, réalisez une application gérant une base de données selon les conditions suivantes.

L’application doit permettre la gestion (recherche et balayage pour consultation, ajout, modification et suppression, avec confirmation et

annulation possibles) distincte de chaque entité permanente (ou signalétique). Mais l’encodage d’un champ qui ne correspond pas à une donnée connue doit provoquer l’ouverture du formulaire de gestion correspondant avec par défaut la donnée encodée dans la gestion fille. Par exemple, l’encodage dans la gestion des personnes d’une localité inconnue doit provoquer l’ouverture du formulaire de gestion des localités en présentant l’information qui vient d’être encodée au niveau des personnes. Il doit en être de même pour l’encodage d’un pays inexistant dans la gestion des localités. Au retour dans le formulaire de départ, l’information proposée doit être celle qui vient d’être confirmée dans le formulaire de gestion appelé, ou l’information initiale de l’enregistrement en cas d’annulation. Afin de préserver la cohérence de la base de données, il faut empêcher la suppression d’un pays qui comporte une localité habitée. La suppression d’un pays ne comportant aucune localité habitée doit provoquer la suppression de toutes les localités de ce pays. De plus, la suppression d’une localité habitée ne peut avoir lieu. Les modifications des champs Pays, CPost, Localite et NomPers doivent donner lieu à confirmation, de même que le déménagement d’une personne vers une autre localité. Lors d’une modification des champs CPost et Localite, la demande de confirmation ne peut avoir lieu que lorsque les deux zones de saisie sont abandonnées par l’utilisateur.

TPays IdPays Entier court (non auto) Pays Texte

TLocalite IdLoc Entier (non auto) CPost Texte Localite Texte XIdPays Entier court

TPers IdPers Entier (non auto) NomPers Texte PrenomPers Texte AdrPers Texte Telephone Texte EMail Texte XIdLoc Entier

Page 18: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 7

L’encodage de doublons sur les champs CPost, Localite et NomPers doit donner lieu à confirmation. Les doublons sur le champ Pays et sur la paire CPost/Localite sont interdits. L’utilisateur doit pouvoir choisir la base de données sur laquelle il souhaite travailler et, le cas échéant, créer une nouvelle base. La fermeture de l’application doit provoquer le compactage de la base. 20. AutreCarnetDAdresses (/800) (cf. Pages VBDB : Bases de données et composants visuels)

En partant de l’application du labo précédent, réalisez une application gérant une base de données selon les conditions suivantes. L’application doit permettre la gestion (recherche et balayage pour consultation, ajout, modification et suppression, avec confirmation et annulation possibles) distincte de chaque entité permanente (ou signalétique). Mais l’encodage d’un champ qui ne correspond pas à une donnée connue doit provoquer l’ouverture du formulaire de gestion correspondant avec par défaut la donnée encodée dans la gestion fille. Par exemple, l’encodage dans la gestion des personnes d’une localité inconnue doit provoquer l’ouverture du formulaire de gestion des localités en présentant l’information qui vient d’être encodée au niveau des personnes. Il doit en être de même pour l’encodage d’un pays inexistant dans la gestion des localités. Au retour dans le formulaire de départ, l’information proposée doit être celle qui vient d’être confirmée dans le formulaire de gestion appelé, ou l’information initiale de l’enregistrement en cas d’annulation. Afin de préserver la cohérence de la base de données, il faut empêcher la suppression d’un pays qui comporte une localité habitée. La suppression d’un pays ne comportant aucune localité habitée doit provoquer la suppression de toutes les localités de ce pays. De plus, la suppression d’une localité habitée et la suppression d’une ligne de communication utilisée par une personne ne peuvent avoir lieu. Les modifications des champs Pays, CPost, Localite, Libelle et NomPers doivent donner lieu à confirmation, de même que le déménagement d’une personne vers une autre localité et la suppression d’une ligne de communication d’une personne. Lors d’une modification des champs CPost et Localite, la demande de confirmation ne peut avoir lieu que lorsque les deux zones de saisie sont abandonnées par l’utilisateur. L’encodage de doublons sur les champs CPost, Localite et NomPers doit donner lieu à confirmation. Les doublons sur les champs Pays et Libelle, ainsi que sur les paires CPost/Localite et XIdPers/XIdCom, sont interdits. Ce dernier point n’empêche pas une personne de disposer de plusieurs lignes de communications et, à ce propos, il convient que l’interface utilisateur permette le choix aisé d’une ligne parmi d’autres. L’utilisateur doit pouvoir choisir la base de données sur laquelle il souhaite travailler et, le cas échéant, créer une nouvelle base. La fermeture de l’application doit provoquer le compactage de la base.

TPays IdPays Entier court (non auto) Pays Texte

TCommunication IdCom Entier court (non auto) Libelle Texte

TLocalite IdLoc Entier (non auto) CPost Texte Localite Texte XIdPays Entier court

TNumero XIdPers Entier XIdCom Entier court Numero Texte

TPers IdPers Entier (non auto) NomPers Texte PrenomPers Texte AdrPers Texte XIdLoc Entier

Page 19: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 8

Travaux complémentaires 21. Tableaux (/100) (cf. Pages VBNF : Les tableaux) Etablissez en Word un tableau comparatif des possibilités offertes, avantages et inconvénients, de chaque mode d’utilisation de tableaux proposés dans les 5 exemples du cours. 22. StructuresTableaux (/100) (cf. Pages VBNF : Les structures de tableaux) Créez un exemple de définition de structure dans laquelle l’usage d’un tableau est pertinent. Définissez la structure et son ou ses tableau(x). Fournissez une page Word (pas d’application). 23. TableauxStructures (/100) (cf. Pages VBNF : Les tableaux de structures) Créez un exemple pertinent d’usage d’un tableau de structures. Définissez la ou les structure(s) et le tableau. Fournissez une page Word (pas d’application). 24. DeclareProc (/100) (cf. Pages VBNF : Déclarations des procédures et des fonctions) Etablissez en Word un tableau comparatif des possibilités offertes, similitudes et oppositions, des attributs de portée Overloads, Overrides, Overridable, NotOverridable et MustOverride. 25. ToString (/100) (cf. Pages VBPA : La classe System.Object et la classe String) c Pour certains types de données tels que les Date, Time et les numériques, la méthode ToString offre des possibilités de formatage semblables à celles de la méthode Format. Par exemple F.ToString(".###") présente le réel F avec 3 décimales. Réalisez une application Windows qui permet l’encodage d’un nombre réel, d’un nombre de caractères pour la partie entière et d’un nombre de décimales, et réalise ensuite l’affichage du nombre réel formaté comme souhaité par la méthode ToString. 26. Sinus (/100) (cf. Pages VBPA : La classe Graphics) Réalisez une application mettant en œuvre l’exemple du cours (tracé d’une sinusoïde) en permettant à l’utilisateur d’encoder en degrés d’angle les limites qu’il souhaite pour la représentation de la sinusoïde. 27. Async (/200) (cf. Pages VBOBJ : Synchrone, asynchrone et multithreading) c Réalisez l’application Console permettant l’expérimentation des modes de traitements synchrone, asynchrone et multithreads, en y intégrant les affichages de temps comme indiqué dans le cours. Etablissez un tableau comparatif des temps (semblable à celui du support) pour chacun de ces modes : mise en exécution, délai de reprise par l’appelant et durée de l’exécution. Outre les fichiers du projet et les copies d’écran, ce labo implique la livraison d’une page Word présentant le tableau réalisé. 28. ListeDesPersonnes (/400) (cf. Pages VBPV et VBDB : ListBox, Impression et DataGridView)

Réalisez une application de consultation de la base de données créée au labo 18. L'usage d’une boîte de dialogue permettant la désignation de la base à consulter s'impose. La fenêtre principale doit présenter la liste des personnes, chacune avec sa localité, dans un DataGridView. Aucune modification de données de ce composant ne peut affecter la base de données. L’ajout d’enregistrements doit être impossible. Un menu doit proposer : Autre base, Impression et Quitter. Le menu Impression doit offrir le choix entre : Personnes sélectionnées et Toutes les localités. Les impressions doivent se faire via un formulaire secondaire qui présente les données préparées selon le type d’impression demandé dans un ListBox. L’impression des lignes sélectionnées, doit fournir le nom et la localité de chaque personne de la sélection. L’impression de toutes les localités, ne tient pas compte d’éventuelles sélections. Il doit fournir toutes les localités et pour chacune, la liste des habitants. Ce formulaire secondaire présente un menu qui propose : Imprimer et Quitter. La première option provoque l’impression effective du contenu du ListBox, la seconde ferme le formulaire et rend la main au formulaire principal. Cette application doit être absolument distincte de celle du labo 18.

Page 20: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 9

29. MesAmisWeb (/400) (cf. Pages VBWEB : Les composants ASP visuels + Liaison à une base de données) c

Réalisez une page Web dynamique de gestion des personnes avec un numéro de téléphone pour chacune. La liste des personnes doit être présentée dans un composant DropDownList lequel doit également servir à la recherche d’une personne. La page aussi doit présenter deux TextBox, l'un pour le nom et l'autre pour le numéro. Ils doivent servir à l'encodage des informations lors de l’ajout d’une personne. Le TextBox utilisé pour le numéro de téléphone doit présenter en permanence le numéro d’appel de la personne dont le nom est visible dans le DropDownList. Deux boutons libellés Ajout et Supprime doivent activer les procédures correspondantes. Si lors d'un ajout, le nom de la personne encodée existe déjà dans la liste, l'ajout doit être ignoré. Les nom et numéro d’appel de chaque personne doivent être stockés dans une base de données Access. 30. JeuDuPendu (/400) (cf. Pages VBWEB : Programmation du Net Remoting) c

Réalisez une application client/serveur permettant aux utilisateurs de divers ordinateurs d’utiliser les services d’un serveur pour jouer au pendu. A l’initialisation du jeu, le serveur doit : Prendre un mot au hasard dans un fichier stocké sur l’ordinateur serveur; Déterminer le nombre de points à comptabiliser pour ce mot; Présenter le mot avec toutes les lettres intérieures remplacées par des points. Pendant le jeu, à chaque proposition de lettre à placer, le serveur doit : Placer la lettre proposée dans le mot aux endroits adéquats si elle convient ou comptabiliser les erreurs si la lettre

ne convient pas. Le démarrage du serveur nécessite l’attribution d’un port de communication et le démarrage d’un client nécessite l’adresse IP du serveur et le numéro de port employé. Les interfaces graphiques ci-après conviennent bien à cette application (cette illustration présente le serveur, mais aussi trois clients connectés).

Page 21: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 10

Exercices divers 31. LesImpairs (cf. Pages VBNF: Itérations) Réalisez une application Console qui affiche les N premiers entiers positifs impairs. La valeur de N doit être donnée par l’utilisateur et le traitement se réalise à l’aide d’une seule boucle For. Exemple (en supposant que l’utilisateur réponde 5 à la question) : Valeur de N ? 5 1 3 5 7 9 32. SomEntiers (cf. Pages VBNF : Itérations) Réalisez une application Windows qui calcule et affiche la somme de tous les entiers compris dans un intervalle désigné par deux nombres Min et Max. 33. DesMots (cf. Pages VBNF et VBPV : Les tableaux + Boutons + Boîte de texte) c Réalisez une application permettant la gestion d’un maximum de 10 mots stockés dans un vecteur de chaînes. La gestion doit permettre l’ajout et la suppression de mots ainsi que le balayage du vecteur. Seuls les mots réellement encodés doivent être présentés lors du balayage. L’illustration ci contre est un modèle d’interface qui convient très bien à cette application. 34. MonRepertoire (cf. Pages VBPV : ListView + Impression) c Réalisez une application permettant la gestion d’un répertoire d’adresses dans un composant ListView. Des TextBox doivent constituer les zones d’encodages des données qui sont ensuite ajoutées dans le ListView par le clic d’un bouton Ajout. Deux autres boutons, Supprime et Imprime, doivent provoquer les actions attendues sur les lignes sélectionnées du ListView. L’illustration ci contre représente l’interface de cette application. 35. DevineNombre (cf. Pages VBPV : Générer des erreurs + Form + Programmation multi formulaires) c L’application demandée ici est un jeu qui consiste à faire deviner par l’utilisateur un nombre pensé par l’ordinateur. A l’ouverture du formulaire de base présenté ci-contre, un nombre caché d’une valeur allant de 1 à 100, est calculé à partir du millième de seconde du temps actuel fournit par Date.Now.Millisecond. Le joueur tente alors de trouver ce nombre caché par des encodages successifs et il est aidé à chaque coup par l’indication que le nombre encodé est trop grand ou trop petit. Lorsqu’il trouve enfin le nombre caché, un message de félicitation est exprimé ainsi que le nombre de coups joués. Pour l’expérimentation de la gestion des erreurs, de la génération des erreurs et de la communication entre formulaires, la vérification du nombre encodé doit être confiée à une fonction qui retourne True en cas de réussite et qui dans le cas contraire, génère une erreur dont le message est Trop grand ou Trop petit. Ce message doit être passé à une MessageBox originale qui se présente sous la forme d’un bouton du fait que sa taille n’excède pas celle du bouton qu’elle contient (cf. illustrations). C’est le texte de ce bouton qui doit exprimer le message.

Page 22: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 11

36. ChercheEtRemplace (cf. Pages VBPV et VBPA : Mémoires de masse + Classes String et RegularExpressions) c L’application demandée doit faire la copie d’un fichier texte en remplaçant une chaîne donnée par une autre. Les recherches de fichiers doivent utiliser les DialogBox adéquates. L’information du nombre de remplacements effectués par ligne doit être affichée dans un ListBox. Il ne doit pas y être fait mention des lignes pour lesquelles il n’y a pas de remplacement. Le traitement terminé doit être signalé par un message approprié dans le ListBox et indiquer le nombre total de remplacements réalisés. Le comptage des remplacements doit être réalisé à l’aide de la classe RegularExpressions (la chaîne recherchée est supposée ne jamais contenir de métacaractère) et les remplacements doivent être réalisés par la méthode adéquate de la classe String. 37. RepertoireTelephones (cf. Pages VBPA : Collections génériques) c Créez une collection générique de structures Personne contenant les noms et numéros de téléphones de personnes, toujours en ordre alphabétique croissant des noms. Réalisez une application Console qui permet, sur base d’un menu, l’ajout et la suppression de personnes, l’affichage de toutes les personnes de la collection ainsi que l’affichage des informations d’une personne recherchée indifféremment sur base de son nom ou de son numéro de téléphone. 38. DesEtudiants (cf. Pages VBOBJ : Constructeurs et destructeurs, constructeur par recopie) c Créez la classe ClEtudiant en complétant le code qui est livré ci-dessous et implémentez-la ensuite dans une application Windows qui gère une classe d’étudiants nommée MaClasse et qui est une ArrayList de ClEtudiant. L’affichage des étudiants d’une classe se réalise dans une ListView nommée LVEtudiants. Les boutons BNouveau, BDouble, BCopie et BSupprime commandent les opérations correspondantes. Le click d’un entête de colonne recopie la cote correspondante de la ligne sélectionnée dans une TextBox nommée TCote où elle peut être modifiée. La validation d’une cote modifiée est commandée par le bouton BCoteOK. La création d’un nouvel étudiant implique qu’il ait un nom et un nombre de cours. Cette dernière valeur est plafonnée à 10 pour les besoins de cet exercice. Ce nombre de cours définit la taille d’un vecteur CoteEtud créé et initialisé à zéro dans l’instance. La création d’un double ne nécessite que la sélection d’une ligne de la ListView. Le double est en tous points identique à son modèle. La copie nécessite un nom dans la TextBox TNom et la sélection d’une ligne. Sauf pour son nom qui lui est propre, la copie est en tous points identique à son modèle. La suppression concerne la ligne sélectionnée. Cette suppression doit libérer la mémoire en conséquence. L’arrêt de l’application provoque l’émission de l’événement FormClosed. Il convient d’en profiter pour libérer la mémoire encore occupée par d’éventuelles instances et tout autre objet.

L’intérêt de l’exercice réside davantage dans la réalisation de ClEtudiant, l’application Windows ne servant qu’à l’expérimentation de cette classe.

Page 23: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 12

Public Class ClEtudiant Implements IDisposable #Region "Données et membres privés" Private NomEtud As String Private CoteEtud() As Single Private NbreCotes As Integer Private MaxNbCote As Integer = 10 Private WriteOnly Property NbreCours() As Integer ' ... ... ... End Property #End Region #Region "Interface de la classe" Public ReadOnly Property NombreCours() As Integer ' ... ... ... End Property Public Property Nom() As String ' ... ... ... End Property Public Property Cote(ByVal Index As Integer) As Single ' ... ... ... End Property

#End Region #Region "Constructeurs" Public Sub New(ByVal ArgNom As String, ByVal ArgNbreCours As Integer) ' ... ... ... End Sub Public Overloads Function Clone() As ClEtudiant ' Ce constructeur doit permettre à une instance de se cloner elle-même : C = A.Clone() ' ... ... ... End Function Public Overloads Function Clone(ByVal ArgSource As ClEtudiant) As ClEtudiant ' Ce constructeur doit permettre à une instance d’en cloner une autre : C = A.Clone(B) ' ... ... ... End Function Public Function Copy(ByVal ArgNom As String, ByVal ArgSource As ClEtudiant)

As ClEtudiant ' Le nom de l’étudiant cible doit être différent de celui de l’étudiant source ' ... ... ... End Function #End Region #Region "Destructeur" ' ... ... ... #End Region

End Class

Page 24: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 13

39. MailAGogo (cf. Pages VBDB : OleDB et DataReader) c Créez l’application Windows MailAGogo qui permet l’envoi d’un message de plusieurs expéditeurs à plusieurs destinataires. Les adresses des serveurs SMTP et les adresses des messageries sont encodées par une application distincte dans deux tables Access (TServSMTP et TAdrMail). Elles sont chargées sur l’action d’un bouton BCharge pour remplir les objets de présentations CBServeur (un ComboBox), LBExpediteurs et LBDestinataires (deux ListBox). Un TextBox nommé TObjet permet l’encodage de l’objet du message et un RichTextBox nommé RTBMessage permet l’encodage du message proprement dit. Un bouton BEnvoi lance l’émission des mails. A toutes fins utiles, voici le code qui permet d’envoyer un message d’un expéditeur vers un destinataire.

Dim Mail As System.Net.Mail.MailMessage Dim smtp As System.Net.Mail.SmtpClient Dim Expediteur As String Dim Destinataire As String Try

' Désigner le serveur SMTP smtp = New System.Net.Mail.SmtpClient("smtp.unprovider.com")

' Instancier un mail Mail = New System.Net.Mail.MailMessage

' Préciser les codages employés pour l'objet du message et pour le message Mail.SubjectEncoding = System.Text.UTF8Encoding.UTF8 Mail.BodyEncoding = System.Text.UTF8Encoding.UTF8

' Désigner l’objet du message situé ici dans un TextBox nommé TObjet Mail.Subject = TObjet.Text

' Désigner le message situé ici dans un RichTextBox nommé RTBMessage Mail.Body = RTBMessage.Text

' Donner les adresses des expéditeur et destinataire Expediteur = "[email protected]" Destinataire = "[email protected]" Mail.From = New System.Net.Mail.MailAddress(Expediteur) Mail.To.Add(New System.Net.Mail.MailAddress(Destinataire))

' Expédier le message smtp.Send(Mail)

' Libérer la mémoire Mail.Dispose() Catch ex As ApplicationException MessageBox.Show(ex.Message) Finally

smtp = Nothing Try ' Libérer la mémoire dans un Try car si le message Mail.Dispose() ' s’est bien passé, c’est déjà chose faite. Catch End Try Mail = Nothing

End Try 40. DBMails (cf. Pages VBDB : Création d'une base Access + Mode connecté) c Créez une application Windows qui permet la création et la gestion d’une base de données Access contenant les adresses des serveurs SMTP et les lieux d’émission, ainsi que celles des expéditeurs et destinataires potentiels des messages électroniques. La base de données doit répondre aux règles de gestion des données suivantes :

- Un serveur SMTP est accessible de plusieurs lieux - Un seul serveur SMTP est accessible à partir d’un lieu donné - Une adresse de messagerie est de type Expéditeur ou de type Destinataire, mais pas les deux - Les doublons d’adresses SMTP sont interdits

Page 25: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 14

- Les doublons de lieux sont interdits - Les doublons sur la paire Adresse-Type sont interdits - Les doublons d’adresses de messagerie sont permis.

Le modèle physique illustré ici permet cette gestion des données. Dans la mesure où les contraintes de développement exposées ci après imposent le contrôle des règles de gestion des données par la programmation, la relation entre les entités TServSMTP et TLieux ne doit pas obligatoirement être mise en place dans la base. L’attribut Type de l’entité TAdrMail peut avoir pour valeur 1 (si Expéditeur) ou 2 (si Destinataire). Les codes SQL de création des entités et de la relation sont :

CREATE TABLE TServSMTP (IdSMTP Int, ServSMTP TEXT (40)) ALTER TABLE TServSMTP ADD PRIMARY KEY (IdSMTP) CREATE TABLE TLieux (Lieu TEXT (40), XIdSMTP Int) ALTER TABLE TLieux ADD FOREIGN KEY (XIdSMTP) REFERENCES TServSMTP (IdSMTP) CREATE TABLE TAdrMail (AdrMail TEXT (40), Type Byte)

L’interface demandée est celle illustrée ci contre. Outre un bouton BDonnees destiné au choix de la base à ouvrir ou à créer, l’interface est constituée de deux GroupBox (GPBConsultation et GPBEncodage). L’accès à GPBEncodage doit être le seul possible au moment de la création d’une nouvelle base. Le bouton BDonnees doit être rendu inactif de sorte à empêcher une double sélection de base de données. Le GroupBox GPBConsultation contient quatre ListBox (LBLieux, LBServeur, LBExpediteur et LBDestinataire) et une TextBox (TServeurDuLieu) qui présente le serveur du premier lieu présenté dans LBLieux à l’ouverture, et le serveur du lieu sélectionné par la suite. Le GroupBox GPBEncodage contient quatre TextBox (TLieux, TServeur, TExpediteur et TDestinataire), un ComboBox (CBServeur) et trois boutons (BVideZone, BAjout et BSuppression). Le bouton BVideZone commande l’effacement de toutes les zones de textes du GroupBox. Les boutons BAjout et BSuppression activent les procédures correspondantes pour chacune des zones non vides. Une confirmation doit être demandée à l’utilisateur pour chacune des informations à ajouter ou à supprimer. La sélection d’une donnée dans un des ListBox de GPBConsultation doit provoquer la réplication de cette donnée dans la TextBox correspondante de GPBEncodage. Ce dispositif doit notamment permettre de désigner les informations à supprimer. Le respect des règles de gestion des données doit être vérifié par le code de chaque procédure d’ajout ou de suppression. La création d’enregistrements orphelins dans TLieux doit être évitée. L’ajout ou la suppression d’enregistrement doit provoquer la mise à jour de tous les objets de présentation des données. Pour faire simple, une procédure ChargeEtAffiche() est utilisée à l’ouverture de la base et à chaque modification (il serait préférable de programmer les mises à jours des ListBox et ComboBox, mais ce n’est pas l’objet de cet exercice). L’objet de cet exercice étant de réaliser une application exclusivement en mode connecté, l’initialisation des objets de liaison à la base de données peut être faite dès l’ouverture ou la création et ils peuvent être détruits en réponse de l’événement FormClosed. Les déclarations de ces objets peuvent être globales et, outre les Imports, les premières lignes de codes peuvent être les suivantes : Public Class FDBMail Dim FlgDB As Integer ' -1 = pas de base, 0 = nouvelle base, 1 = base opérationnelle Dim NomDB As String Dim MaConnexion As New OleDbConnection Dim MaCommande As New OleDbCommand Dim DesAdresses As OleDbDataReader ' … … …

Page 26: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBLab - 15

41. DBRepertoire (cf. Pages VBDB : Bases de données et composants visuels) En partant de l’application décrite dans le cours, réalisez une application gérant une base de données selon les conditions suivantes. L’application doit permettre la gestion (recherche et balayage pour consultation, ajout, modification et suppression, avec confirmation et annulation possibles) distincte de chaque entité permanente (ou signalétique). Mais l’encodage d’un champ qui ne correspond pas à une donnée connue doit provoquer l’ouverture du formulaire de gestion correspondant avec par défaut la donnée encodée dans la gestion fille. Par exemple, l’encodage dans la gestion des personnes d’un moyen de télécommunication inconnu doit provoquer l’ouverture du formulaire de gestion des moyens de télécommunication en présentant l’information qui vient d’être encodée au niveau des personnes. Au retour dans le formulaire de départ, l’information proposée doit être celle qui vient d’être confirmée dans le formulaire de gestion appelé, ou l’information initiale de l’enregistrement en cas d’annulation. Afin de préserver la cohérence de la base de données, il faut empêcher la suppression d’un moyen de télécommunication qui est utilisé par une personne. Les modifications des champs NomPers, Libelle et Numero doivent donner lieu à confirmation, de même que la suppression d’une ligne de communication d’une personne. L’encodage de doublons sur le champ NomPers doit donner lieu à confirmation. Les doublons sur le champ Libelle, ainsi que sur la paire XIdPers/XIdCom, sont interdits. Ce dernier point n’empêche pas une personne de disposer de plusieurs lignes de communications et, à ce propos, il convient que l’interface utilisateur permette le choix aisé d’une ligne parmi d’autres. L’utilisateur doit pouvoir choisir la base de données sur laquelle il souhaite travailler et, le cas échéant, créer une nouvelle base. La fermeture de l’application doit provoquer le compactage de la base.

TCommunication IdCom Entier court (non auto) Libelle Texte

TNumero XIdPers Entier XIdCom Entier court Numero Texte

TPers IdPers Entier (non auto) NomPers Texte PrenomPers Texte

Page 27: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 1

Notions fondamentales de programmation

en VB.Net

Page 28: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 2

Tables des matières des pages VBNF Historique simplifié du Basic ............................................................................................................................................. 4 La technologie .NET et Visual Studio .Net ........................................................................................................................ 4 L’environnement de développement Visual Studio ............................................................................................................ 5

Application Console ................................................................................................................................................. 6 Application Windows ............................................................................................................................................... 7

Les entrées et les sorties de données ................................................................................................................................. 10 Applications Console ............................................................................................................................................. 10

Les entrées.................................................................................................................................................... 10 Les sorties .................................................................................................................................................... 11

Applications Windows ........................................................................................................................................... 12 Les types et structures de données .................................................................................................................................... 13

Brève révision de notions fondamentales ............................................................................................................... 13 Les variables ................................................................................................................................................ 13 Les structures composées (tableaux) ............................................................................................................ 13 Les structures complexes (types utilisateurs) ............................................................................................... 14

Déclarations des variables. ..................................................................................................................................... 15 Les types de données .............................................................................................................................................. 19 Notation littérale de données .................................................................................................................................. 20 Déclaration des tableaux ........................................................................................................................................ 20 Définition des structures ......................................................................................................................................... 22

Les structures de tableaux ............................................................................................................................ 23 Les tableaux de structures ............................................................................................................................ 23

Les énumérations .................................................................................................................................................... 24 Les instructions de contrôles et moyens de traitements .................................................................................................... 25

Brève révision de notions fondamentales ............................................................................................................... 25 La séquence .................................................................................................................................................. 25 La rupture de séquence ................................................................................................................................ 25 L’alternative ................................................................................................................................................. 25 Les itérations ................................................................................................................................................ 25

Ni contrôle, ni traitement ........................................................................................................................................ 26 Les commentaires ........................................................................................................................................ 26 La continuation ............................................................................................................................................ 26 L’économie de dactylographie ..................................................................................................................... 26 Le séparateur d’instructions ......................................................................................................................... 26

Les instructions de contrôles .................................................................................................................................. 27 La rupture de séquence ................................................................................................................................ 27 L’alternative ................................................................................................................................................. 27 La sélection .................................................................................................................................................. 29 Les itérations ................................................................................................................................................ 30

Les boucles logiques .......................................................................................................................... 30 La boucle arithmétique ...................................................................................................................... 31 Une boucle ni logique, ni arithmétique .............................................................................................. 32

L’appel de sous-programmes ....................................................................................................................... 32 L’exécution de programmes externes. ......................................................................................................... 33 Le mot de la fin ............................................................................................................................................ 33

Les moyens de traitements ..................................................................................................................................... 34 Les opérateurs .............................................................................................................................................. 34

L’affectation ...................................................................................................................................... 34 La concaténation ................................................................................................................................ 34 Les opérateurs arithmétiques ............................................................................................................. 34 Les formes contractées de l’affectation ............................................................................................. 34 Les opérateurs relationnels ................................................................................................................ 34 Les opérateurs logiques ..................................................................................................................... 35 Les opérateurs binaires ...................................................................................................................... 36 Ordre d’évaluation des expressions et hiérarchie des opérateurs ...................................................... 36

Les procédures et fonctions intrinsèques, et les méthodes ........................................................................... 36 Déclarations des procédures et des fonctions ............................................................................................... 37

Déclarations des arguments des procédures, fonctions et méthodes ................................................. 39

Page 29: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 3

Type de donnée des arguments .......................................................................................................... 41 Valeur de retour des fonctions ........................................................................................................... 41

La récursivité ................................................................................................................................................ 42

Page 30: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 4

Historique simplifié du Basic

1964 Langage compilé sur gros système (dérivé du Fortran) Fin des ‘70 Arrivée de la micro informatique

Versions interprétées du langage. L’interpréteur est stocké en ROM. Parfois, il est à la fois le langage de programmation et le langage du système.

1979 Elaboration du projet IBM PC (Personnal Computer). Microsoft produit l’OS (MS-Dos) et l’interpréteur Basic pour ces PC. Versions : Basica et GWBasic.

Fin des ‘80 Jusque fin ‘90

Quick Basic Environnement de développement pour DOS. (Editeur de code source, débogueur, éditeur de liens, compilateur)

1991 Visual Basic (VB 1.0 à 6.0) Environnement de développement pour Windows. 2000 à 2003 Maturation de Visual Studio

qui inclut VB.Net (VB 7.0 et 7.1) L’environnement DotNet repose sur un Framework qui doit être supporté par l’OS (Operating System). Les différentes versions de Visual Studio, et donc de VB.Net, sont essentiellement des adaptations aux versions successives de Windows.

2006 Version 2005 (VB 8.0) 2007 Version 2008 (VB 9.0) 2009 Version 2010 (VB … ) … … …

La technologie .NET et Visual Studio .Net La technologie .NET est un standard proposé par Microsoft, en alternative à la plate-forme J2EE de Sun, pour le développement d'applications d'entreprises multi niveaux, basées sur des composants. L’environnement de développement Visual Studio offre au programmeur toute l’assistance utile à l’exploitation des classes du Framework dans le langage de son choix (parmi ceux proposés, selon le package), pour la création d’applications de types très divers répertoriées en deux grandes catégories : les projets Windows et les sites Web. L’inventaire complet des nombreuses librairies du Framework peut être consulté sur le site de Microsoft à l’adresse http://msdn.microsoft.com/fr-fr/library.

Visual Studio

Applications Système, Console, Windows, Web, …, en code

CIL ou MSIL (Microsoft …)

Framework (intégré à l’OS)

FCL Framework Class

Library

CLR Common Language

Runtime

JIT Just-In-Time

Environnement de

développement multi langages (VB, C#, C++, …)

Compilateur CIL Common Intermediaire Language

FCL fournit les librairies d’objets exploitables par l’OS.

JIT fournit le code exécutable, fonction par fonction, juste au moment opportun.

CLR gère l’utilisation de la mémoire par les différents codes en exécution.

Page 31: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 5

Les projets Windows concernent la programmation d’applications devant fonctionner sous un système d’exploitation Windows. Ces projets se divisent essentiellement en deux catégories : les applications graphiques et les applications console.

Les applications graphiques utilisent les Windows Forms et s’exécutent en mode graphique dans l’environnement Windows. Elles sont toutes ces applications qui présentent leur propre interface graphique utilisateur (GUI). Les applications console sont toutes celles qui sont lancées en ligne de commande en session DOS.

Les Sites Web concernent la programmation d’application devant fonctionner par l’usage d’une connexion à l’Internet. Deux catégories se retrouvent ici : les Sites Web Asp et les Services Web.

Les Sites Web Asp utilisent les Web Forms accessibles par les navigateurs Internet. Ces sites sont destinés à être installés sur le serveur Microsoft Internet Information Services (IIS) pourvu du Framework.

Les Services Web sont des entités logicielles fournissant des fonctionnalités à d'autres applications via une connexion Internet.

A ces deux grandes catégories bien différenciées, il faut ajouter le développement des applications Client/Serveur qui entrent dans la catégorie des projets Windows de par leur mode de développement, mais qui entrent aussi dans la catégorie des Services Web de par leur mode de fonctionnement.

L’environnement de développement Visual Studio Dès Visual Studio chargé, sa page de démarrage propose le choix entre la création d’un nouveau projet ou bien l’ouverture d’un existant, et cela en précisant d’emblée la catégorie : projet Windows ou site Web. Pour les projets Windows il faut également choisir le langage et, dans tous les cas, il faut nommer le projet et lui désigner un emplacement sur une mémoire de masse.

Le type d’application se définit par le choix d’un modèle parmi ceux proposés ci-contre. Voici la signification de quelques uns d’entre eux : Application Windows :

Application graphique ordinaire pour Windows. Bibliothèque de classe :

Classe réutilisable ou composant qui peut être partagé avec d'autres projets.

Bibliothèque de contrôle Windows :

Contrôle personnalisé destiné à être intégré à une application Windows.

Page 32: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 6

Service Windows : Application résidente dépourvue d'interfaces graphiques.

Service Web ASP.Net :

Service Web XML conçu avec ASP.NET, ses fonctionnalités peuvent être publiées et appelées par des applications tierces extérieures.

Application Smart Device (jusqu’à la version 2008, n’existe plus dans la version 2010) :

Application ASP.NET prévue pour être consultée sur des PDA (Personal Digital Assistants), GSM et autres périphériques mobiles.

Application Web ASP.Net :

Application prévue pour être consultée via un navigateur Internet. Application console :

Application en lignes de commandes dépourvues d'interfaces graphiques. Il n’entre pas dans le cadre de ce cours d’examiner en détail l’ensemble des fonctionnalités de l’environnement de développement, mais seulement l’essentiel permettant la réalisation des petits programmes d’expérimentation nécessaires à la compréhension des concepts développés. Diverses fonctionnalités, de même que certaines différences entre les versions 2003 et les suivantes, sont expliquées dans ces pages quand c’est nécessaire.

Application Console A l’issue des préalables à la création d’applications décrits à la page précédente, et après le choix d’une application de type Console, Visual Studio présente son interface de travail illustrée ci dessous.

L’explorateur de solutions (ex-explorateur de projet) présente les référenceset les fichiers du projet. Un fichier sélectionné là peut recevoir un nom représentatif dans la fenêtre des propriétés située en-dessous par défaut.

La feuille de travail est munie d’onglets permettant l’accès immédiat à tout fichier ouvert. Ses listes déroulantes permettent la sélection des objets (liste de gauche) et des méthodes ou événements les concernant (liste de droite).

Exécution de l’application

Cette page de propriétés du projet s’obtient par le menu Projet/Propriétés du projet. Elle permet de désigner l’objet de démarrage et de définir le nom de l’exécutable (Nom de l’assembly).

Page 33: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 7

Une fois l’interface en place, il n’y a plus qu’à écrire les codes requis par l’application envisagée. En cours d’encodage, Visual Studio fournit une aide contextuelle qui constitue un précieux aide-mémoire pour le programmeur. Voici le code de cette première application Console …

… et le résultat de son exécution

Application Windows Le développement d’une application de type Windows présente une interface plus élaborée. L’explorateur de solutions propose des boutons supplémentaires, la feuille de travail présente d’emblée un Form vierge sur un onglet étiqueté [Design], et une boîte à outils présente les contrôles qui peuvent être dessinés sur ce Form. A ce stade, la page de code contient très peu de choses. Pourtant, le Concepteur Windows Form, c'est-à-dire l’assistant qui écrit notamment le code nécessaire à la présentation du Form et des contrôles qui y sont dessinés par le programmeur, a déjà enregistré les informations dans leur état actuel. Ces informations et le code correspondant sont modifiés au fur et à mesure de la mise au point du Design par le programmeur. Alors que sous la version 2003, le code du concepteur était contenu dans une Region (regroupement de codes) de chaque formulaire, depuis la version 2005, ce code est contenu dans un fichier distinct présenté sur l’illustration précédente comme un sous fichier de Form1.vb. Ce fichier

Pour passer du Form graphique à sa page de code et vice versa.

Obtenir l’information de tous les fichiers du projet, ou seulement les plus utiles.

Page 34: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 8

est en fait une partie de la classe Form1 de l’illustration précédente. Il s’agit d’une application de cette fonctionnalité nouvelle depuis 2005 qui permet d’écrire du code d’une classe sur plusieurs fichiers. Il suffit de faire précéder le mot Class du mot Partial pour que le code soit reconnu comme appartenant à la classe désignée. Voici le contenu de ce fichier nommé Form1.Designer.vb dans cet exemple.

<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) If disposing AndAlso components IsNot Nothing Then components.Dispose() End If MyBase.Dispose(disposing) 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() components = New System.ComponentModel.Container() Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.Text = "Form1" End Sub End Class

Des composants sont placés sur le Form et la taille de ce dernier est ajustée aux besoins d’une hypothétique application. Dans le code généré par le Concepteur Windows Form, la procédure privée InitializeComponent a été modifiée et la déclaration événementielle du contrôle Button a été ajoutée. Voici un extrait de ces modifications.

Me.MonBouton = New System.Windows.Forms.Button Me.SuspendLayout() ' ' MonBouton ' Me.MonBouton.Location = New System.Drawing.Point(84, 28) Me.MonBouton.Name = "MonBouton" Me.MonBouton.Size = New System.Drawing.Size(126, 23) Me.MonBouton.TabIndex = 1 Me.MonBouton.Text = "Fin" Me.MonBouton.UseVisualStyleBackColor = True ' ' Form1 ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.ClientSize = New System.Drawing.Size(282, 75) Me.Controls.Add(Me.MonBouton) Me.Name = "Form1" Me.Text = "Form1" Me.ResumeLayout(False) ' Déclaration des contrôles avec prise en compte de leurs événements ' Friend WithEvents MonBouton As System.Windows.Forms.Button

Page 35: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 9

La procédure InitializeComponent décrit les composants ajoutés et leurs propriétés modifiées. Ordinairement, le programmeur ne se soucie pas du code généré par le Concepteur Windows Form, code qu’il n’est d’ailleurs pas judicieux de modifier autrement que par l’onglet Design et la fenêtre des propriétés (cf. la remarque dans le code de Partial Class Form1 à la page précédente). Après cette illustration de l’interface de développement d’une application Windows, voici une autre application de type Windows, équivalente celle-ci à l’application Console réalisée précédemment. Une fois l’interface de travail en place et le Form vierge présent sur la feuille de travail, la page de code est activée par le clic du bouton adéquat de l’explorateur de solutions en vue de définir une procédure événementielle. Form1 Evénements est choisi dans la liste déroulante de gauche et l’événement souhaité peut alors être sélectionné dans celle de droite. Ces sélections réalisées, la feuille de code présente le corps de la procédure événementielle. Il reste à y écrire le code qui permettra à cette application de réaliser dans l’interface graphique de Windows ce que l’application Console réalise en session Dos. Voici le code de cette première application Windows …

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

MessageBox.Show("Bonjour", "", MessageBoxButtons.OK) End End Sub

… et le résultat de son exécution

Après avoir illustré l’environnement de développement par la réalisation d’une application affichant « Bonjour » sous Dos et d’une autre effectuant le même affichage sous Windows, il convient d’aborder les concepts de base en programmation VB.Net. Pour l’étude de ces concepts, c’est le contexte procédural de la programmation qui est important et, autant que possible, les aspects POO (Programmation Orientée Objet) sont évités. Ces derniers sont étudiés en détail par la suite. L’approche de cette étude est scindée en plusieurs parties, respectant ainsi la structure décrite par le schéma du traitement rationnel de l’information de Von Newman.

Les entrées et sorties de données Le stockage des données en mémoire

o Les types et structures de données Le traitement des données

o Les instructions de contrôle o Les moyens de traitements

Page 36: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 10

Les entrées et les sorties de données Il s’agit, pour la description des moyens d’entrer et de sortir l’information des systèmes, de faire la distinction entre ceux utilisés dans les applications Console et ceux utilisés dans les applications Windows.

Applications Console Il existe sous DOS/Windows trois périphériques standard appelés : 1. périphérique d'entrée standard - désigne par défaut le clavier et porte le n° 0 2. périphérique de sortie standard - désigne par défaut l'écran et porte le n° 1 3. périphérique d'erreur standard - désigne par défaut l'écran et porte le n° 2 En VB.NET, le flux de lecture Console.In lit les données provenant du périphérique 0, le flux d'écriture Console.Out écrit sur le périphérique 1, et le flux d'écriture Console.Error écrit sur le périphérique 2. Les instructions d’entrées et de sorties de données sont des méthodes des objets Console.In, Console.Out et Console.Error. Le premier est de type StreamReader et les deux autres sont de type StreamWriter (ces types présenteront de l’intérêt lors de l’usage des fichiers). Les propriétés In et Out peuvent être omises lorsque les périphériques sont bien, comme par défaut, le clavier et l’écran. Les entrées Console.Read Cette méthode lit le premier caractère présent dans le flux. L’encodage doit se terminer par un <Enter>. La méthode retourne un nombre entier représentant le code ASCII du caractère lu. Toutefois, la valeur de retour peut être affectée à une chaîne de caractères qui contient alors les chiffres composants le numéro ASCII du caractère lu. Attention, l’usage de cette méthode pour la saisie d’un caractère laisse dans le flux les caractères constituant la fin de ligne. Ces caractères peuvent être lus par des Read() supplémentaires. Il faut le dire, Read() n’est pas la meilleure méthode de lecture du clavier. Par exemple, le code ci-dessous …

Dim I As Integer Dim S As String I = Console.Read() ' Lecture d’un caractère Encodage A Console.WriteLine(" 1 ==> I = " & I) ' Affichage 1 ==> I = 65 I = Console.Read() ' Lecture de la fin de ligne Console.WriteLine(" 2 ==> I = " & I) ' Affichage 2 ==> I = 13 I = Console.Read() Console.WriteLine(" 3 ==> I = " & I) ' Affichage 3 ==> I = 10 S = Console.Read() ' Lecture d’un caractère Encodage A Console.WriteLine(" 4 ==> S = " & S) ' Affichage 1 ==> S = 65 I = Console.Read() ' Lecture de la fin de ligne Console.WriteLine(" 5 ==> I = " & I) ' Affichage 2 ==> I = 13 I = Console.Read() Console.WriteLine(" 6 ==> I = " & I) ' Affichage 3 ==> I = 10

Les Read() contenus dans les lignes de codes désignées par 'Lecture de la fin de ligne n’attendent aucune action sur le clavier. Elles puisent chacune un caractère encore présent dans le flux et fournissent successivement à la variable I les valeurs 13 et 10. Ces valeurs constituent bien la séquence de fin de ligne.

Page 37: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 11

Console.ReadLine Cette méthode lit toute la ligne tapée au clavier, vide le flux des caractères de fin de ligne, et restitue dans une variable de type String les caractères utiles de la ligne. Par exemple, le code ci-dessous …

Dim S As String S = Console.ReadLine() ' Encodage Bonjour Console.WriteLine("==> S = " & S) ' Affichage ==> S = Bonjour

Console.ReadKey Cette méthode lit un seul caractère tapé au clavier et renvoie une valeur de type ConsoleKeyInfo sans attendre l’appui de la touche <Enter>. C’est la méthode à utiliser de préférence à toute autre après le message "Pressez une touche pour continuer …". Utilisée sans paramètre, ou avec le paramètre False, le caractère de la touche pressée est affiché (saisie avec écho). Avec le paramètre True, le caractère de la touche pressée n’est pas affiché (saisie sans écho).

Dim N As ConsoleKeyInfo Console.Write("Pressez une touche pour continuer …") Console.ReadKey(True) ' Saisie sans écho et valeur ignorée N = Console.ReadKey(True) ' Encodage A

Console.WriteLine(N.Key) ' Affichage 65 Console.WriteLine(N.KeyChar) ' Affichage A

Les sorties Console.Write La méthode Write() affiche à l’écran une chaîne de caractères, représentant les valeurs qui lui sont passées par paramètres, sans y ajouter les caractères de fin de ligne. Il en résulte qu’après cela, un autre affichage ou une saisie se fait à la fin de la ligne affichée.

Dim S As String Console.Write("Votre nom : ") S = Console.ReadLine() Console.Write(S) Console.Write(" est valide")

Console.WriteLine La méthode WriteLine() affiche à l’écran une chaîne de caractères, représentant les valeurs qui lui sont passées par paramètres, et y ajoute les caractères de fin de ligne. Il en résulte qu’après cela, un autre affichage ou une saisie se fait au début de la ligne suivante.

Dim S As String Console.WriteLine("Votre nom : ") S = Console.ReadLine() Console.WriteLine(S) Console.WriteLine(" est valide")

Les méthodes Write et WriteLine acceptent la définition d’une chaîne composée de caractères à afficher, de codes de formatage et de la désignation de la position des valeurs livrées par les variables (cf. l’étude de la méthode Format de la classe String, dans les pages VBPA). Par exemple, à la suite des lignes de codes précédentes :

Console.WriteLine("Est-ce {0} ?", S) ' Affichage Est-ce Pierre ?

Evolution à l'écran : Affichage Votre nom : _ Encodage Votre nom : PierreAffichage de S Pierre Affichage suiv. Pierre est valide

Evolution à l'écran : Affichage Votre nom :

_ Encodage Pierre Affichage de S Pierre Affichage suiv. est valide

Page 38: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 12

Applications Windows Les entrées/sorties standard (clavier-écran) se font essentiellement à l’aide d’objets possédant des propriétés de type texte qui peuvent être utilisées pour l’affichage et/ou l’entrée d’informations : Label, TextBox, MessageBox, … Par exemple, le code ci-dessous …

… et le résultat de son exécution

Affichage Encodage Affichage

Page 39: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 13

Les types et structures de données

Brève révision de notions fondamentales Les variables Alors que le système utilise la mémoire à sa manière, en l’adressant électriquement sur base du numéro d’ordre de chaque emplacement, le programmeur utilise des étiquettes représentatives à ses yeux des valeurs utilisées dans son programme. Ainsi, lorsque le système range à l’adresse 55123456 la valeur 0.21, le programmeur voit cette valeur sous le nom TauxTVA. Le programmeur utilise des noms aussi représentatifs que possible du rôle des valeurs étiquettées. La variable est un conteneur (l’emplacement mémoire) nommé de valeurs. Outre d’être nommé, ce conteneur (à l’instar de ceux servant à la collecte sélective d’objets) doit être prédestiné à un type donné de valeurs. Attention : Une variable ne peut contenir qu’une seule valeur à la fois. Donner une valeur à une

variable revient à remplacer toute autre valeur qu’elle pouvait contenir. Les langages donnent des moyens d’organisation des variables en structures plus ou moins complexes selon les cas. Ces structures ont toujours pour but de permettre le stockage de plusieurs valeurs derrière une seule et même étiquette de variable. Ces structures de données sont appelées types composés (ou structures composées) lorsqu’elles rassemblent des éléments d’un même type et types complexes (ou structures complexes) lorsqu’elles rassemblent des éléments de types différents. Les structures composées (tableaux) La plus simple des structures généralement proposées permet d’assembler plusieurs variables d’un même type en une seule variable de ce type. Cette structure porte le nom de Table, ou Tableau, ou Vecteur, ou Matrice selon leurs dimensions et selon les auteurs. Certains langages proposent également des types de valeurs qui sont déjà des assemblages de structures élémentaires (les chaînes de caractères sont toujours des assemblages de type « vecteur » de structures élémentaires de type « caractère »). Une variable élémentaire (reprise ici pour comparaison car ce n’est pas une structure) : On accède à la variable et à son contenu par son nom : A

Dim A as Integer A = 12

Pour les amateurs de géométrie : le point Une structure Table à 1 dimension (Vecteur) regroupant 4 variables élémentaires : On accède à la variable par son nom et à son contenu par son nom et son indice : A

Dim A(4) as Integer A(C) = 12 AC

Pour les amateurs de géométrie : la droite Une structure Table à 2 dimensions (Matrice) regroupant 8 variables élémentaires : On accède à la variable par son nom et à son contenu par son nom et ses indices :

Dim A(2,4) as Integer A A(L,C) = 12

Pour les amateurs de géométrie : le plan AL,C

A

A

A

A

A

2

3

4

1

A

A

A

2

3

4

1 A

A

A

2

3

4

1

A

1 2

1 2 … n C

L

1

2 … n1C

2

Page 40: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 14

Une structure Table à 3 dimensions regroupant 16 variables élémentaires : On accède à la variable par son nom et à son contenu par son nom et ses indices :

Dim A(2,2,4) as Integer A A(P,L,C) = 12

Pour les amateurs de géométrie : l’espace AP,L,C La représentation qu’on se fait de ces structures est avant tout une vue de l’esprit. L’imagination du programmeur limite le nombre de dimensions allouées à une table bien avant les capacités techniques des systèmes. VB.Net autorise jusqu’à 32 dimensions ! Les structures complexes (types utilisateurs) Il s’agit d’une organisation regroupant différents types de variables élémentaires et/ou de structures diverses :

Private Structure Etudiant NomEtud As String PrenomEtud As String DateNaisEtud As String SexeEtud As Byte NbreCoursEtud As Byte CoursEtud() As String PointEtud(,) As Integer End Structure Private Structure Classe NiveauClas As Byte NbreEtudClas As Byte EtudClas() As Etudiant End Structure Dim NomVarClas As Classe

Voici définis deux types structurés de données : le type Etudiant et le type Classe. Une variable NomVarClas de type Classe est directement réservée. Le type Etudiant est composé de variables élémentaires et de structures fournies par le langage. Le type Classe intègre un « type utilisateur », à savoir Etudiant qui est le type de données préalablement défini par le programmeur. Chacun de ces types possède une ou plusieurs sous-structures de type tableau dynamique : CoursEtud et EtudClas sont des vecteurs et PointEtud est une matrice. Ces structures complexes peuvent être visualisées comme ceci :

On accède à la variable par son nom et à son contenu par les noms des membres suivis, le cas échéant, des indices ou autres vitamines nécessaires compte tenu de leur type de structure. En poursuivant l’exemple ci-dessus :

NomVarClas.NiveauClas = 3 NomVarClas.EtudClas(12).PointEtud(1,1) = 75

NiveauClas NbreEtudClas EtudClas

NomEtud PrenomEtud DateNaisEtud SexeEtud NbreCoursEtud CoursEtud PointEtud

C

A

A

A

2

3

4

1 A

A

A

2

3

4

1

A

1 2

A

A

A

2

3

4

1 A

A

A

2

3

4

1 A

1 2

A

A

A

2

3

4

1 A

A

A

2

3

4

1

1 2 1

2

1

2 … n1L

2

P

2

1

NomVarClas

Page 41: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 15

Déclarations des variables. La déclaration des variables peut être implicite en VB. Cela signifie qu’une variable acquiert le type de la donnée qui lui est affectée.

X = "Bonjour" assigne à X le type String et lui affecte la valeur Bonjour. Cependant, cette facilité doit être délaissée par le programmeur qui lui préférera la déclaration explicite des variables.

Dim X As String X = "Bonjour" La déclaration implicite, qui n’existe d’ailleurs pas dans tous les langages (Fortran, Cobol, Pascal, C, C++), présente l’inconvénient d’exposer le programmeur à une correction fastidieuse des fautes de frappes. La déclaration explicite des variables ne met pas le programmeur à l’abri de telles fautes, mais celles-ci lui sont signalées immédiatement pour autant qu’il ait placé la directive Option Explicit On en tête de chaque page de code. Dans Visual Studio .Net, cette option peut être fixée dans les propriétés du projet, sous l’onglet Compiler. La déclaration d’une variable se compose de trois parties essentielles :

- la définition de la portée de la variable - la définition de son nom - la définition de son type

Private X As String

La portée est définie par les mots Dim, Private, Public, Static, Protected, Friend, Protected Friend, Shadows, Shared, ReadOnly et par certaines associations de ces mots. Elle détermine l’espace d’accessibilité aux variables. Il existe trois principaux lieux de déclarations des variables où l’emploi de ces mots est requis :

- en tête de modules ou de classes - à l’intérieur de procédures et de fonctions - à l’intérieur de blocs de codes

Dim

La variable déclarée avec Dim en tête d’un module ou d’une classe est accessible de n'importe quel endroit de ce module ou de cette classe où elle n’est pas redéfinie (effet de masque), mais pas dans une sous-classe. Si elle est déclarée à l’intérieur d’une procédure, d’une fonction ou d’un bloc de code, elle n’est visible que dans cette partie du code où elle bénéficie de l’effet de masque.

Public Class Class1 Dim VarGlobClass1 As Integer Dim UneAutreVar As Integer Private Sub MaSub() Dim VarLocMaSub As Byte VarGlobClass1 = 11 ' Visible dans sa classe UneAutreVar = 12 ' Celle-ci aussi VarLocMaSub = 13 ' N’est visible que dans MaSub If VarGlobClass1 > 100 then Dim VarBlocIf As Byte VarBlocIf = 14 ' Visible dans son bloc de code Else Dim UneAutreVar As Byte ' Visible dans son bloc de code et masque la

' variable de même nom déclarée en tête de Class1 ' VarBlocIf = 15 ' Erreur : la variable n'est pas visible ici End If End Sub

Un bloc de code ne bénéficie pas de l’effet de masque par rapport aux variables déclarées dans sa procédure ou sa fonction. Dans l’exemple ci contre, il serait interdit de redéclarer VarLocMaSub dans le bloc If, tandis qu’il est permis de redéclarer UneAutreVar.

Un nom de variable, de même que celui de tout autre objet, doit être aussi représentatif que possible du rôle de l’objetdans le code. Il est idéalement écrit en lettres majuscules et minuscules (non accentuées) combinées de sorte à faciliter la lecture. Il peut contenir des chiffres et le caractère de soulignement, mais il ne peut commencer par un chiffre.

Page 42: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 16

Private Class SousClass Private Sub UneSUb() ' VarGlobClass1 = 16 ' Erreur : la variable n'est pas visible ici End Sub End Class End Class

Private

La variable déclarée avec Private en tête d’un module ou d’une classe est accessible de n'importe quel endroit de ce module ou de cette classe où elle n’est pas redéfinie (effet de masque), mais pas dans une sous-classe.

Public Class Class1 Private VarGlobClass1 As Integer Private Sub MaSub() VarGlobClass1 = 11 ' Visible dans sa classe End Sub Private Class SousClass Private Sub UneSUb() ' VarGlobClass1 = 16 ' Erreur : la variable n'est pas visible ici End Sub End Class End Class

Public

La déclaration Public ne peut se faire qu’en tête d’un module ou d’une classe. La variable ainsi déclarée est accessible dans sa classe, mais pas dans une sous-classe. Elle est aussi accessible de n'importe quel endroit de l’application en cours de développement par la désignation d’une instance de sa classe ou du nom de son module. De plus, si une variable est ainsi déclarée dans une classe, elle est aussi accessible de toute instance de la classe compilée et référencée dans un projet. Attention, la déclaration Public d’une variable de classe est en infraction avec le contrôle d’accès préconisé en POO.

Module MonModule Public VarGlobMonModule As Integer Sub Main() VarGlobMonModule = 17 ' Visible dans tout le code de son module End Sub End Module Public Class Class1 Public VarGlobClass1 As Integer Private Sub MaSub() VarGlobClass1 = 11 ' Visible dans sa classe MonModule.VarGlobMonModule = 1 ' Visible via le nom du module End Sub Private Class SousClass Private Sub UneSUb() Dim VarLoc As Class1 ' VarGlobClass1 = 16 ' Erreur : la variable n'est pas visible ici VarLoc.VarGlobClass1 = 16 ' Visible via le nom d’une variable de type Class1 End Sub End Class End Class

Page 43: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 17

Static

La déclaration Static n’est permise qu’à l’intérieur d’une procédure, d’une fonction, ou d’un bloc de code et elle n’est visible que dans cette portée où elle est déclarée. Une telle variable n’est initialisée qu’au premier appel du code qui la contient et conserve ainsi la dernière valeur qui lui est affectée dans cette partie du programme. Cette valeur est donc récupérable d’un appel à l’autre du code contenant la déclaration.

Private Sub UneSub() Static Passage As Integer = 1 ' … code de la procédure MessageBox.Show("Passage n° : " & Passage) Passage = Passage + 1 End Sub

Protected

La déclaration Protected ne peut se faire qu’en tête d’une classe. La variable ainsi déclarée est accessible dans sa classe, bien sûr, mais aussi dans toute classe qui en dérive (héritage).

Public Class Class1 Protected VarClass1 As Integer Private Sub MaSub() VarClass1 = 11 ' Visible dans sa classe End Sub End Class Public Class Class2 Inherits Class1 ' Héritage de Class1 : Class1 peut ne pas être écrite ici, ' mais être une dll compilée et référencée dans le projet

Private Sub MaSub() Dim VarObjet As New Class1 VarObjet.VarClass1 = 1 ' Visible via le nom de la variable de type Class1 End Sub End Class

Friend

La déclaration Friend peut se faire en tête d’une classe ou en tête d’un module et est dans ce cas, synonyme de Public mais seulement dans le projet en cours. La variable déclarée avec Friend en tête d’une classe est accessible à partir de toute instanciation de sa classe dans le projet.

Public Class Class1 Friend VarClass1 As Integer Private Sub MaSub() VarClass1 = 11 ' Visible dans sa classe End Sub End Class Public Class Class2 ' Class2 appartient au même projet que Class1 Private Sub MaSub() Dim VarObjet As Class1 VarObjet.VarClass1 = 1 ' Visible via le nom de la variable de type Class1 End Sub End Class

La variable Passage n’est initialisée qu’au premier appel de la procédure UneSub. Passage garde ensuite sa dernière valeur affectée par Passage = Passage + 1 et ce, jusqu’à l’arrêt du programme.

Page 44: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 18

Protected Friend

La déclaration Protected Friend ne peut se faire qu’en tête d’une classe. La variable déclarée avec Protected Friend en tête d’une classe est accessible à partir de toute instanciation de sa classe dans le projet et dans d’autres projets à partir d’instances dérivées de sa classe compilée et référencée.

Public Class Class1 Protected Friend VarClass1 As Integer ' … VarClass1 est visible dans Class1, partout dans le projet en cours via le ' nom d’une variable de type Class1, et dans les classes héritant de Class1 End Class

Shadows

La déclaration Shadows ne peut se faire qu’en tête d’une classe et n’a d’intérêt que dans une classe dérivée. En effet, elle sert à redéclarer dans la classe dérivée un élément de même nom qui existerait dans la classe mère en masquant ce dernier. Elle palie ainsi à l’absence d’effet de masque dans ce contexte.

Public Class Class1 Protected VarClass1 As Integer Protected DateNais As Date End Class Public Class Class2 Inherits Class1 ' Héritage de Class1 : Class1 peut ne pas ' être écrite ici, mais être une dll ' compilée et référencée dans le projet

Private Sub MaSub() ' Dim DateNais As String ' Erreur : conflit de nom avec Class1 Dim Shadows DateNais As String ' Le membre de Class1 est masqué ' … End Sub End Class

Shared

La déclaration Shared ne peut se faire qu’en tête d’une classe. La variable ainsi déclarée est visible dans sa classe et dans ses éventuelles sous-classes ainsi que, selon la déclaration de portée qui lui est associée (Public, Protected, Friend, …), dans toute instance de sa classe. Mais attention, elle est unique pour toutes les instances et si sa valeur est modifiée dans une instance, elle l’est également dans toutes les autres.

Public Class Class1 Friend Shared VarClass1 As Integer = 0 ' La variable est initialisée à 0 End Class Public Class Class2 Private Sub MaSub() Dim VarObjet As Class1 VarObjet.VarClass1 = 15 ' La valeur 0 affectée dans Class1 est End Sub ' remplacée par la valeur 15 End Class Public Class Class3 Private Sub MaSub() Dim VarObjet As Class1 MessageBox.Show(VarObjet.VarClass1) ' Affichage de la valeur 15 End Sub End Class

Page 45: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 19

ReadOnly (une variable qui n’en est plus une)

La déclaration ReadOnly ne peut se faire qu’en tête d’un module ou d’une classe. La variable ainsi déclarée est visible selon la déclaration de portée qui lui est associée, mais seulement en lecture. Cette variable a donc le comportement d’une constante.

Public ReadOnly Pi As Single = 3.141592 Const (une variable qui n’en est pas une) Le mot Const n’est pas une déclaration de portée mais celle d’une constante. Employé seul ou accompagné d’une des précisions Private, Public, Protected, Friend, Protected Friend et Shadows, il permet la déclaration d’une donnée dont la valeur ne peut plus être modifiée en cours d’exécution. Avec Const employé seul, la constante a une portée locale si elle est déclarée à l’intérieur d’une procédure ou d’une fonction, sinon elle est accessible à tout le code qui se trouve dans la région de la déclaration.

Public Const Pi As Double = 3.141592

Les types de données

Désignations Tailles Plages de valeurs

System.Boolean (type valeur)

2 octets True ou False

System.Byte (type valeur)

1 octet 0 à 255 (non signés).

System.SByte (type valeur)

1 octet -128 à 127.

System.Char (type valeur)

2 octets 0 à 65 535 (non signés).

System.DateTime (type valeur)

8 octets 0:00:00 le 1er janvier 0001 à 23:59:59 le 31 décembre 9999.

System.Decimal (type valeur)

16 octets 0 à +/-79 228 162 514 264 337 593 543 950 335 sans décimale ou 0 à +/-7,9228162514264337593543950335 avec 28 décimales. Le plus petit nombre différent de zéro est : +/-0,0000000000000000000000000001 (+/-1E-28).

System.Single (type valeur)

4 octets -3,402823E+38 à -1,401298E-45 pour les valeurs négatives ; 1,401298E-45 à 3,402823E+38 pour les valeurs positives.

System.Double (type valeur)

8 octets -1,79769313486231E+308 à -4,94065645841247E-324 pour les valeurs négatives et 4,94065645841247E-324 à 1,79769313486231E+308 pour les valeurs positives.

System.Int16 (type valeur)

2 octets -32 768 à 32 767. Synonyme autorisé : Short

System.Int32 (type valeur)

4 octets -2 147 483 648 à 2 147 483 647. Synonyme autorisé : Integer

System.Int64 (type valeur)

8 octets -9 223 372 036 854 775 808 à 9 223 372 036 854 775 807. Synonyme autorisé : Long

System.UInt16 (type valeur)

2 octets 0 à 65 535. Synonyme autorisé : UShort

System.UInt32 (type valeur)

4 octets 0 à 4 294 967 295. Synonyme autorisé : UInteger

System.UInt64 (type valeur)

8 octets 0 à 18 446 744 073 709 551 615. Synonyme autorisé : ULong

System.Object (type référence)

4 octets ou 8 octets

N'importe quel type peut être référencé par une variable de type Object. (4 octets sur un système 32 bits, 8 octets sur un système 64 bits)

La variable Pi est accessible selon les possibilités offertes par la portée Public, mais elle ne peut être modifiée en nul autre endroit que celui-ci.

La constante Pi est accessible selon les possibilités offertes par la portée Public, mais elle ne peut être modifiée en nul autre endroit que celui-ci.

Page 46: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 20

System.String (type référence)

Dépend de l’implémentation

0 à environ 2 milliards de caractères Unicode (2 octets par caractère).

Type utilisateur System.ValueType (type valeur)

Dépend de l’implémentation

Chaque membre de la structure présente une plage déterminée par son type de données.

Chaque type de donnée repris dans le tableau précédent est assorti d’une indication type valeur ou type référence. La différence entre les deux réside dans la manière de leur réserver de la place en mémoire et de les initialiser. La déclaration d’une variable de type valeur provoque la réservation de son emplacement en mémoire et son initialisation à la valeur nulle (sauf pour une variable de type DateTime pour laquelle la valeur d’initialisation est 111000, c'est-à-dire 01/01/0001 01:01:01). La déclaration d’une variable de type référence provoque la réservation en mémoire de l’emplacement nécessaire pour stocker l’adresse où sont effectivement stockées les données désignées par cette variable. L’initialisation par défaut d’une telle variable, qui est donc un pointeur, est Nothing. Une variable de type référence doit être initialisée par des valeurs ou par l’usage de l’opérateur New. Cette initialisation doit permettre au compilateur d’évaluer et de réserver l’espace nécessaire en mémoire, et d’initialiser la variable avec l’adresse de la zone ainsi réservée.

Notation littérale de données La notation littérale d’une donnée est l’expression de sa valeur dans le code. Par exemple : Pi = 3.141592

Integer 145, -7, &FF & est, dans ce cas, le préfixe pour l’hexadécimal Long 100000L Double 134.789, -45E-18 Single 134.789F, -45E-18F F comme float pour désigner la simple précision Decimal 100000D Char "A"c c, pour transformer la chaîne de caractères "A" en caractère 'A' Date New Date(2006, 3, 1) instanciation d'un objet Date initialisé à 01/03/2006 #1/3/2006# forme ordinaire du littéral de type Date et Time (#2:25:30 PM#)

"01/03/2006" littéral de type Date et Time sous forme de chaîne String "aujourd'hui" si la chaîne doit contenir le caractère ", on double celui-ci comme

dans "abcd""e" pour représenter la chaîne abcd"e.

Déclaration des tableaux Contrairement aux variables élémentaires de type valeur dont il a été question jusqu’à présent, les tableaux sont de type référence. Alors que Dim X As Integer réserve la place en mémoire pour un entier, Dim T() As Integer réserve la place en mémoire pour l’adresse d’un tableau d’entiers et non pour le tableau lui-même. Le tableau T doit être spécialement initialisé pour que la référence désigne effectivement son contenu. Comme indiqué précédemment, l’accès aux données d’un tableau se fait par la désignation de son nom flanqué des indices nécessaires. L’indice minimal est toujours 0 et l’indice maximal est celui indiqué par l’initialisation. Par exemple, Dim T(5) As Integer réserve l’espace pour 6 entiers, tous initialisés à 0, accessibles par un indice variant de 0 à 5. Ou encore Dim T() As Integer = {0, 1, 2, 3, 4, 5} réserve l’espace pour 6 entiers, respectivement initialisés avec les valeurs 0, 1, 2, 3, 4 et 5, également accessibles par un indice variant de 0 à 5. Les tableaux sont des objets tous dérivés d’une classe Array. Ils possèdent des propriétés et des méthodes exploitables par le programmeur, certaines par l’instance utilisée, d’autres à partir de la classe Array. Voici à titre d’exemple, les propriétés Length et Rank, ainsi que les méthodes GetLength, Reverse et Sort :

Length Contient le nombre total d’éléments réservés pour le tableau. Rank Contient le nombre d’indices nécessaires pour l’accés aux données. GetLength Fournit le nombre d’éléments réservés pour une dimension donnée du tableau. Sort Retourne le tableau trié par ordre croissant. Après cela, Reverse donnera le tableau dans l’ordre

inverse.

Page 47: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 21

Dim L, C As Integer ' Alloue l’espace pour 2 entiers L et C ' Tableau 1 ' Alloue l’espace pour 5 entiers initialisés à 0 Dim Tab1(4) As Integer For C = 0 To Tab1.Length – 1 ' Balayage de Tab1 de 0 jusqu’à son indice maximal Tab1(C) = C ' Affectation à Tab(C) de la valeur de l’indice Next C Console.WriteLine("NbItem : " & Tab1.Length & " NbDim : " & Tab1.Rank) For C = 0 To Tab1.Length – 1 ' Affichage : NbItem : 5 NbDim : 1 Console.WriteLine("Tab1(" & C & ")=" & Tab1(C)) Next C ' Affichage : Tab1(0) = 0 ' Tab1(1) = 1 ' … … … ' Tableau 2 ' Alloue l’espace pour 5 entiers initialisés avec les valeurs énumérées Dim Tab2() As Integer = New Integer(4) {200, 203, 202, 201, 204} Console.WriteLine("NbItem : " & Tab2.Length & " NbDim : " & Tab2.Rank) ' Affichage : NbItem : 5 NbDim : 1 Array.Sort(Tab2) ' Tri de Tab2 ' Tableau 3 ' Alloue l’espace pour 3 x 5 entiers initialisés avec les valeurs énumérées Dim Tab3(,) As Integer = New Integer(2, 4) {{300, 301, 302, 303, 304}, _

{310, 311, 312, 313, 314}, _ {320, 321, 322, 323, 324}}

Console.WriteLine("NbItem = " & Tab3.Length & " NbDim = " & Tab3.Rank) ' Affichage : NbItem : 15 NbDim : 2 ' Tableau 4 ' Alloue l’espace pour 1 matrice d’entiers. Tab4 est initialisée à Nothing Dim Tab4(,) As Integer = New Integer(,) {} ' Alloue 3 lignes de 3 colonnes à Tab4. La plage est initialisée à 0 ReDim Tab4(2, 2) Console.WriteLine("NbItem = " & Tab4.Length & " NbDim = " & Tab4.Rank) ' … ' Affichage : NbItem : 9 NbDim : 2 ' Alloue 2 colonnes de plus à chaque ligne de Tab4. La plage ajoutée est initialisée à 0 ' Cette opération ne peut concerner que l’indice de droite ReDim Preserve Tab4(2, 4) ' Preserve pour conserver le contenu Console.WriteLine("Tab4 : NbItem = " & Tab4.Length & " NbDim = " & Tab4.Rank) ' Affichage : NbItem : 15 NbDim : 2 Console.WriteLine(Tab4.GetLength(0) & " ligne(s)") ' Affichage : 3 ligne(s) Console.WriteLine(Tab4.GetLength(1) & " colonne(s)") ' Affichage : 5 colonne(s) ' Tableau 5 ' Alloue l’espace pour 3 vecteurs d’entiers qui peuvent être de longueurs différentes. Dim Tab5()() As Integer = New Integer(2)() {} ' Chaque Tab5() vaut Nothing Console.WriteLine("NbItem = " & Tab5.Length & " NbDim = " & Tab5.Rank) For L = 0 To 2 ' Affichage : NbItem : 3 NbDim : 1 ' Alloue l’espace de chaque Tab5() pour L entiers initilisés à 0 Tab5(L) = New Integer(L) {} For C = 0 To L Tab5(L)(C) = C * 10 + L ' Affectation des cellules C du tableau Tab5(L) Next C Next L Console.WriteLine("NbItem = " & Tab5.Length & " NbDim = " & Tab5.Rank) ' Affichage : NbItem : 3 NbDim : 1 Console.WriteLine("NbItem = " & Tab5(0).Length & " NbDim = " & Tab5(0).Rank) ' Affichage : NbItem : 5 NbDim : 1

Page 48: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 22

Définition des structures Les structures sont les types de données définis par le programmeur. Elles sont un assemblage de types divers tels que types de base du langage, tableaux et autres structures préalablement définies. Comme les variables élémentaires (et contrairement aux tableaux qui sont de type référence) les structures sont de type valeur. La déclaration d’une variable d’un type utilisateur entraîne donc immédiatement la réservation de sa place en mémoire. Cependant, si une structure contient des membres de type référence, ces derniers doivent être explicitement initialisés par des valeurs ou par l’usage de l’opérateur New. L’espace réservé en mémoire pour une variable d’une telle structure ne contient, pour ces membres de type référence, que l’espace nécessaire au stockage d’une adresse. Une structure ne peut pas être définie à l'intérieur d'une procédure ou d'une fonction. Il s’agit d’une déclaration de type de donnée et non encore d’une variable. Les indicateurs de portée qui sont applicables au niveau de la définition d’une structure sont les mêmes que ceux des variables à l’exception de Dim, Static, Shared et ReadOnly, ceux-ci ne pouvant être employés. Une structure définie à l'extérieur d'une classe ou d'un module doit être déclarée Public. Les membres d’une structure utilisent les indicateurs de portées Dim, Private, Public, Shadows, Shared et ReadOnly, soit tous à l’exception de ceux ordinairement réservés aux classes et de Static qui ne peut s’employer qu’à l’intérieur d’une procédure ou une fonction. Seuls Dim et Shared sont réellement dignes d’intérêt :

Dim le membre est visible partout où la structure est visible Shared le membre est visible partout où la structure est visible, mais unique pour toutes les

variables du même type Private le membre est invisible partout et donc, sans intérêt Public le membre est visible partout où la structure est visible, comme avec Dim Shadows le membre est visible partout où la structure est visible mais sans intérêt, les membres

d’une structure bénéficiant toujours de l’effet de masque ReadOnly le membre est visible partout où la structure est visible mais non modifiable, ce qui ne

présente pas d’intérêt du fait qu’on ne peut initialiser un membre de structure lors de sa définition, sauf s’il est déclaré Const

Les variables de type structure se déclarent comme toute autre variable là où le type défini par la structure est visible. Une fois la variable déclarée, elle donne accès à tous les membres de la structure à l’exception de ceux déclarés Private. Cependant, la portée de la structure limite la portée permise pour une variable. C’est donc la portée inscrite dans la définition de la structure qui détermine la visibilité des variables et des membres.

Private Structure Test ' Visible dans sa classe ou son module Dim VarTest1 As Integer Public VarTest2 As Integer Shared VarTest3 As Integer ' Unique pour toutes les variables de type Test ReadOnly VarTest4 As Integer ' Ne peut recevoir aucune valeur Const VarTest5 As Integer = 101 ' Ne peut être modifiée End Structure ' Public MaVarTest As Test ' Erreur : ne peut être Public car Test est Private Public Sub MaSub() Dim T As Test Dim TT As Test T.VarTest1 = 10 T.VarTest3 = 13 ' T.VarTest4 = 14 ' Erreur : le membre est ReadOnly ' T.VarTest5 = 15 ' Erreur : le membre est une constante Console.WriteLine(T.VarTest1) ' Affichage : 10 Console.WriteLine(T.VarTest2) ' Affichage : 0 initialisation par défaut Console.WriteLine(T.VarTest3) ' Affichage : 13 Console.WriteLine(TT.VarTest3) ' Affichage : 13 il n’y a eu aucune affection ! Console.WriteLine(T.VarTest4) ' Affichage : 0 initialisation par défaut Console.WriteLine(T.VarTest5) ' Affichage : 101 End Sub

Page 49: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 23

Les structures de tableaux Un tableau contenu dans une structure doit être initialisé comme tout autre tableau. Mais il ne peut l’être lors de la définition de la structure. Il doit être initialisé avant utilisation, après la déclaration d’une variable du type de la structure concernée. L’accès aux données se fait par le nom de la variable suivi du nom de son tableau, lui-même accompagné des indices nécessaires.

Private Structure MesTab Dim Tab1() As Integer Dim Tab2(,) As Integer Dim Tab3()() As Integer End Structure Sub Main() Dim L, C As Integer Dim MT As MesTab MT.Tab1 = New Integer(4) {} ' Alloue l’espace pour 5 entiers MT.Tab2 = New Integer(,) {} ' Initialisation de MT.Tab2 à Nothing ReDim MT.Tab2(2, 4) ' et allocation d’espace pour 3 x 5 entiers MT.Tab3 = New Integer(2)() {} ' Alloue l’espace pour 3 vecteurs d’entiers For L = 0 To 2 MT.Tab3(L) = New Integer(4) {} ' Alloue l’espace pour chaque vecteur For C = 0 To 4 MT.Tab3(L)(C) = C * 10 + L ' Affectation des cellules C du Next C ' tableau MT.Tab3(L) Next L End Sub

Les tableaux de structures Un tableau de structures doit être initialisé comme tout autre tableau. Si la structure concernée contient un tableau, ce dernier doit être initialisé comme indiqué ci avant. L’accès aux données se fait par le nom du tableau accompagné des indices nécessaires, suivi du nom du membre concerné de la structure.

Private Structure MaStruct Dim Nbre As Byte Dim Tab() As Integer End Structure Sub Main() Dim L, C As Integer Dim TS(5) As MaStruct ' Alloue l’espace pour 6 structures MaStruct ' Initialisation du tableau contenu dans chaque structure For L = 0 To 5 TS(L).Nbre = L ' Affectation d’une valeur à Nbre TS(L).Tab = New Integer(2) {} ' Alloue l’espace pour 3 entiers dans For C = 0 To 2 ' chaque Tab TS(L).Tab(C) = C * 10 + L ' Affectation d’une valeur à chaque Next C ' Tab(C) de TS(L) Next L ' Affichage de toutes les valeurs du tableau de structures For L = 0 To 5 Console.WriteLine("Structure " & L) Console.WriteLine("...Valeur de Nbre = " &

TS(L).Nbre) Console.WriteLine("...Valeurs de Tab") For c = 0 To 2 Console.WriteLine("...Cel " & C & " = " &

TS(L).Tab(C)) Next C Next L End Sub

Affichage : Structure 0 ...Valeur de Nbre = 0...Valeurs de Tab ...Cel 0 = 0 ...Cel 1 = 10 ...Cel 2 = 20 Structure 1 ...Valeur de Nbre = 1...Valeurs de Tab ...Cel 0 = 1 ...Cel 1 = 11 ... …

Page 50: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 24

Les énumérations Une énumération ne peut pas être définie à l'intérieur d'une procédure ou d'une fonction. Il s’agit d’une déclaration de type de donnée et non encore d’une variable. Les indicateurs de portée Public, Private, Protected, Friend, Protected Friend et Shadows lui sont applicables. Une énumération définie à l'extérieur d'une classe ou d'un module doit être déclarée Public. Les variables de type énumération se déclarent comme toute autre variable là où le type défini par l’énumération est visible. Une fois la variable déclarée, elle donne accès à tous les champs de l’énumération. Contrairement aux structures, les champs de l’énumération n’acceptent aucun indicateur de portée. La portée de l’énumération limite la portée permise pour une variable. C’est donc la portée inscrite dans la définition de l’énumération qui détermine la visibilité des variables et des champs. Une variable de type énumération peut être affectée de n’importe quelle valeur de type numérique entier. Il incombe au programmeur de s’assurer de la pertinence de telles affectations. Le bon usage consiste à n’affecter une telle variable que par des valeurs appartenant à sa plage de valeurs. Mieux encore, de n’affecter cette variable que par un des noms de champs de l’énumération. Les champs de l’énumération sont tous des constantes d’un type entier. Ce type de donnée est éventuellement précisé lors de la déclaration de l’énumération comme étant Byte, Integer, Long ou Short, à l’exclusion même de leur synonyme (Int16, Int32, …). Par défaut, le type d'une énumération est Integer. A défaut d’affectation explicite dans la définition de l’énumération, les champs ont pour valeur l’entier correspondant à leur position de 0 à N-1 (N étant le nombre de champs). Si un champ n’est pas affecté dans la définition, il a pour valeur celle de son prédécesseur incrémentée d’une unité. Plusieurs champs peuvent avoir la même valeur.

Private Enum Jours Lundi = 0 : Mardi = 1 : Mercredi = 2 Jeudi = 3 : Vendredi = 4 : Samedi = 5 : Dimanche = 6 End Enum Private Enum Saisons As Byte Printemps Ete Automne Hiver End Enum Private Enum Divers As Integer Chose = 5 : Bidule = 7 : Truc : Machin = 8 : Bazar = 17 End Enum ' Public JJ As Jours ' Erreur : JJ ne peut être Public car Jours est Private Public Sub MaSub() Dim J As Jours ' La déclaration d’une telle variable Dim S As Saisons ' n’est utile qu’à son affectation ' J = "dksh" ' Erreur à l’exécution : J est entier MessageBox.Show(J & " Lundi = " & Jours.Lundi) ' Affichage : 0 Lundi = 0 J = 21 ' Pas d’erreur, mais un non-sens MessageBox.Show(J & " Mardi = " & Jours.Mardi) ' Affichage : 21 Mardi = 1 J = Jours.Jeudi ' Le bon usage MessageBox.Show(J & " Jeudi = " & Jours.Jeudi) ' Affichage : 3 Jeudi = 3 ' S.Hiver = 31 ' Erreur : le champ est une constante MessageBox.Show(S & " Eté = " & Saisons.Ete) ' Affichage : 0 Eté = 1 MessageBox.Show("Bidule = " & Divers.Bidule) ' Affichage : Bidule = 7 MessageBox.Show("Truc = " & Divers.Truc) ' Affichage : Truc = 8 MessageBox.Show("Machin = " & Divers.Machin) ' Affichage : Machin = 8 End Sub

Les énumérations sont des aides au service du programmeur à qui elles présentent des valeurs sous des noms représentatifs. Ainsi par exemple, VB offre des énumérations comme celle des différents jeux de boutons disponibles pour la MessageBox : OK, OKCancel, AbortRetryIgnore, YesNoCancel, YesNo, et RetryCancel, chacun de ces mots étant bien plus significatif que la valeur qu’il représente, soit respectivement 0, 1, 2, 3, 4 et 5.

Page 51: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 25

Les instructions de contrôles et moyens de traitements

Brève révision de notions fondamentales La séquence C’est la structure de programmation la plus élémentaire. Toutes les instructions ainsi programmées s’exécutent l’une après l’autre, successivement, séquentiellement et sans aucun contrôle. Le code suivant constitue une séquence :

Dim J As Jours Dim S As Saisons Dim D As Divers MessageBox.Show(J & " Lundi = " & Jours.Lundi) J = 21 MessageBox.Show(J & " Mardi = " & Jours.Mardi) J = Jours.Jeudi MessageBox.Show(J & " Jeudi = " & Jours.Jeudi) MessageBox.Show("Bidule = " & Divers.Bidule)

La rupture de séquence Les langages offrent diverses solutions permettant le contrôle de l’exécution de certaines séquences. La plus élémentaire de ces solutions est la rupture de séquence. Le programmeur peut interrompre le déroulement d’une séquence en détournant son cours normal vers d’autres codes. La rupture de séquence permet aussi la ré-exécution de codes en détournant le déroulement de la séquence vers son début.

La rupture de séquence inconditionnelle. La rupture de séquence conditionnelle. Bien que présente sous l’une ou l’autre forme dans tous les langages, l’instruction "Aller à" doit toujours être remplacée par l’usage d’alternatives bien construites ou d’itérations judicieusement organisées. Cette instruction n'est tolérable que dans les langages pauvres en contrôles d’alternatives et d’itérations (script MS-DOS, Assembleur, … ). L’alternative L’alternative est une instruction de contrôle de l’exécution du code qui permet ordinairement le choix entre deux séquences distinctes. Dans des formes imbriquées, elle permet le choix d’une séquence parmi plusieurs. Les itérations Les instructions de contrôle d’itérations sont nombreuses. Elles permettent toutes l’organisation et le contrôle de répétitions de séquences. Certaines permettent un nombre déterminé de répétitions, d’autres autorisent la répétition jusqu’à la réalisation d’une condition préalablement définie.

Condition

Aller àAction(s) répétées

Action(s)

Aller à

Action(s)

Action(s) ignorées

Action(s) ignorées

Page 52: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 26

Ni contrôle, ni traitement Les instructions de contrôles sont les codes régissant l’exécution d’une ou plusieurs autres instructions sans réellement affecter les données. Les moyens de traitements sont les opérateurs, les fonctions et les procédures, ainsi que toute autre instruction dont le rôle premier est la transformation et/ou la production de données. Cette distinction généralement ignorée en informatique est faite ici pour mettre en évidence les deux grandes familles d’outils qu’offrent les langages :

- les outils d’organisation et de contrôle des programmes - les outils de traitements des données

Les outils d’organisation et de contrôle des programmes sont les différentes instructions de structuration des programmes, dont essentiellement l’alternative et les itérations. Les outils de traitement des données sont les opérateurs, les procédures et les fonctions intrinsèques du langage ainsi que celles développées par le programmeur. Outre ces instructions, les langages offrent des possibilités d’améliorer l’organisation et la lisibilité du code sans que cela change son ordre d’exécution, ni ses données. Les commentaires L’apostrophe ( ' ) ou le mot clé REM placé en début de ligne ou avant une partie de phrase de code est la marque de commentaire du langage. Sa présence empêche le compilateur de traiter ce qui est écrit à sa droite. Elle est utile pour l’insertion de commentaires et pour l’isolation provisoire de codes pendant la mise au point d’un programme.

' Ceci est un commentaire … REM … de même que cette phrase

La continuation Le caractère de soulignement ( _ ) précédé d’un espace est la marque de continuation. Elle permet la coupure d’une ligne de code trop longue et sa continuation à la ligne suivante, ou simplement d’améliorer la lisibilité du code. Par exemple, dans le code suivant, la marque de continuation permet d’initialiser le tableau Tab3 en montrant la matrice ainsi réalisée.

Dim Tab3(,) As Integer = New Integer(2, 4) { {300, 301, 302, 303, 304}, _ {310, 311, 312, 313, 314}, _ {320, 321, 322, 323, 324} }

L’économie de dactylographie L’instruction With suivie d’un nom de variable d’un type structuré permet d’accéder aux données sans devoir répéter le nom de la variable à chaque fois.

Les 4 lignes de codes suivantes … … peuvent s’écrire With NomVarStruct NomVarStruct.Nbre = 0 .Nbre = 0 NomVarStruct.Tab(1) = 11 .Tab(1) = 11 NomVarStruct.Tab(2) = 12 .Tab(2) = 12 NomVarStruct.Tab(3) = 13 .Tab(3) = 13 End With

Le séparateur d’instructions Le double point ( : ) permet l’écriture de plusieurs instructions sur une même ligne. L’abus du séparateur d’instructions nuit souvent à la lisibilité du code. Les lignes de codes précédentes peuvent s’écrire :

With NomVarStruct .Nbre = 0 : .Tab(1) = 11 : .Tab(2) = 12 : .Tab(3) = 13 : End With

Page 53: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 27

Les instructions de contrôles La rupture de séquence Bien que l’usage des branchements de type "Aller à" (Goto) soit à bannir, voici l’illustration de l’emploi de ces instructions.

Private Sub Branchement() MessageBox.Show("Message 1") GoTo Etiq1 ' Branch. incond. à Etiq1 Etiq2: MessageBox.Show("Message 4") GoTo Etiq5 ' Retour après Goto Etiq2 Etiq1: MessageBox.Show("Message 2") GoTo Etiq3 ' Branch. incond. à Etiq3 Etiq4: MessageBox.Show("Message 5") Exit Sub ' Fin de procédure Etiq3: MessageBox.Show("Message 3") GoTo Etiq2 ' Branch. incond. à Etiq2 Etiq5: GoTo Etiq4 ' Retour après Goto Etiq3 End Sub

Les étiquettes (Etiq1, Etiq2, …) doivent être placées en première colonne du code (pas de retrait, pas d'indentation, pas d'espace). L’alternative Le contrôle d’une alternative se réalise toujours par l’évaluation d’une condition. Plusieurs alternatives peuvent être assemblées en cascades ou imbriquées pour réaliser des choix complexes.

If Condition_réalisée Then Action If Condition_réalisée Then Action1 Else Action2

Meilleure écriture :

If Condition_réalisée Then Action(s)_contrôlée(s) End If

If Condition_réalisée Then Action(s)_contrôlée(s) Else Autre(s)_action(s)_contrôlée(s) End If

Condition

Action(s) contrôlée(s)

Condition

Action(s) contrôlée(s)

Action(s) contrôlée(s)

1 2

Si l’usage d’alternatives bien construites et d’itérations judicieusement organisées permet de créer du code sans rupture de séquence, il est toutefois pratique, par facilité, de briser certains déroulements de traitements notamment par l’emploi des instructions :

Exit Do Exit For Exit Function Exit Sub

Spaghetti ?

Page 54: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 28

If Total > 50 Then Console.WriteLine("Trop cher") If Total > 50 Then Console.Write("Trop cher ") Else Console.Write("Prix OK ")

Meilleure écriture :

If Total > 50 Then Console.WriteLine("Trop cher") End If If Total > 50 Then Console.Write("Trop cher ") Else Console.Write("Prix OK ") End If

Les alternatives, comme toutes les structures de contrôles, peuvent être imbriquées. Cela signifie qu’une ou plusieurs actions contrôlées peuvent elles-mêmes être des alternatives. Les alternatives peuvent également être assemblées en cascades. L’usage de plusieurs alternatives permet l’évaluation de plusieurs conditions.

If Total > 50 Then ' Il doit y avoir autant de End If que de If If Remise = 0.50 then Total = Total * 0.50 Console.WriteLine("Prix OK") else Console.WriteLine("Trop cher") End If Else Console.WriteLine("Prix OK") End If If NombreCote = 3 Then ' Il doit y avoir autant de End If que de If Console.WriteLine("Triangle") Else If NombreCote = 4 Then Console.WriteLine("Quadrilatère") Else If Heure = 16 Then Console.WriteLine("Post meridian") End If End If End If

Pour ce dernier exemple, la forme suivante est meilleure car plus lisible et ne nécessitant qu’un seul End If :

If NombreCote = 3 Then ' Un seul End If car un seul If Console.WriteLine("Triangle") ElseIf NombreCote = 4 Then Console.WriteLine("Quadrilatère") ElseIf Heure = 16 Then Console.WriteLine("Post meridian") End If If Choix = 1 Then ' Toutes les conditions évaluent les différentes Console.WriteLine("Saisie") ' valeurs possibles d’une même variable ElseIf Choix = 2 Then Console.WriteLine("Calcul ") ElseIf Choix = 3 Then Console.WriteLine("Affichage") ElseIf Choix = 0 Then Console.WriteLine("Fin") Else Console.WriteLine("Mauvais choix") End If

Page 55: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 29

Select Case Heure Case 8 To 12 Console.WriteLine("Matin") Case 13 To 18 Console.WriteLine("Après-midi") Case 19 To 22 Console.WriteLine("Soir") Case 22 To 24 , 0 To 7 Console.WriteLine("Nuit") Case Else Console.WriteLine("Heure invalide !")End select

La sélection La sélection est une forme élaborée de l’alternative qui remplace avantageusement toute cascade d’alternatives, chacune vérifiant sa condition sur une seule et même variable commune. Ce schéma au tracé simplifié exprime très bien la sélection sur les valeurs de v.

Select Case Choix Case 1 Console.WriteLine("Saisie") Case 2 Console.WriteLine("Calcul ") Case 3 Console.WriteLine("Affichage") Case 0 Console.WriteLine("Fin") Case Else Console.WriteLine("Mauvais choix") End Select

Outre d’admettre des valeurs discrètes comme dans l’exemple ci-dessus, la sélection en VB autorise aussi l’indication de plages de valeurs.

Condition 1 sur variable v

Action(s) contrôlée(s)

Action(s) contrôlée(s)

1

2

Condition 2 sur variable v

Condition … sur variable v

Action(s) contrôlée(s)

Action(s) contrôlée(s)

n

Condition n sur variable v

Action(s) sur imprévus

Action(s) contrôlée(s)

Action(s) contrôlée(s)

1 2 Action(s) contrôlée(s)

…Action(s)

contrôlée(s)

n Action(s) sur imprévus

Selon valeur de v 1

2 … n autres

Page 56: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 30

Les itérations Les codes contrôlés par une itération sont exécutés plusieurs fois, soit jusqu’à ce qu’une condition prédéterminée d’arrêt soit réalisée, soit un nombre prédéterminé de fois. Il s'agit des boucles logiques dans le premier cas, et des boucles arithmétiques dans le second. Les boucles logiques Les boucles logiques sont représentées par les instructions "Tant que" et "Jusqu’à", l’une et l’autre se programmant avec l’évaluation de la condition Avant ou Après le code contrôlé. Evaluation de la condition avant le code contrôlé : Evaluation de la condition après le code contrôlé : Le code contrôlé ne sera peut-être jamais exécuté. Le code contrôlé sera exécuté au moins une fois. Il faut, dans un cas comme dans l’autre, que le code contrôlé contienne une instruction modifiant une ou plusieurs variables participant à la condition de sorte à permettre la sortie de la boucle après un certain nombre d’itérations.

N = 0 While N < 5 ' "Tant que" avec évaluation Avant (Forme 1) N = N + 1 Console.WriteLine(N) ' Affichage des valeurs de 1 à 5 End While Do While N < 10 ' "Tant que" avec évaluation Avant (Forme 2) N = N + 1 Console.WriteLine(N) ' Affichage des valeurs de 6 à 10 Loop Do ' "Tant que" avec évaluation Après N = N + 1 Console.WriteLine(N) ' Affichage des valeurs de 11 à 15 Loop While N < 15 Do Until N = 20 ' "Jusqu’à" avec évaluation Avant N = N + 1 Console.WriteLine(N) ' Affichage des valeurs de 16 à 20 Loop Do ' "Jusqu’à" avec évaluation Après N = N + 1 Console.WriteLine(N) ' Affichage des valeurs de 21 à 25 Loop Until N = 25

Il existe une structure de boucle logique qui permet au programmeur de placer l’évaluation de la condition n’importe où dans le code contrôlé. La sortie de la boucle se programme alors avec un ordre Exit Do.

Do ' Avec évaluation dans le code contrôlé N = N + 1 If N > 30 Then Exit Do ' Evaluation et sortie si nécessaire Console.WriteLine(N) ' Affichage des valeurs de 26 à 30 Loop

Condition

Action(s) répétée(s)

Condition

Action(s) répétée(s)

Page 57: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 31

V = VDeb ==> VFin, Pas

Action(s) répétée(s)

La boucle arithmétique La boucle arithmétique peut toujours être remplacée par une boucle logique dont la condition évalue la valeur d’une variable incrémentée à chaque itération (comme dans l’exemple précédent). Toutefois, l’instruction appropriée a le mérite d’intégrer la variable, dite variable de boucle, son incrémentation et son évaluation par rapport à une limite donnée. For Variable_de_boucle = Valeur_Debut To Valeur_Fin Step Pas Action(s)_ répétée(s) Next Variable_de_boucle Les trois boucles suivantes sont identiques :

For N = 1 To 25 Step 1 Console.WriteLine(N) ' Affichage des valeurs de 1 à 25 Next N For N = 1 To 25 ' La valeur par défaut du pas est 1 Console.WriteLine(N) ' Affichage des valeurs de 1 à 25 Next N For N = 1 To 25 Console.WriteLine(N) ' Affichage des valeurs de 1 à 25 Next ' Le rappel de la variable de boucle à cet endroit

' est facultatif, mais il améliore la lisibilité ' du code

Le pas peut prendre n’importe quel type de valeur (entière, négative, fractionnaire) pour autant que la variable de boucle soit du type approprié. S’il est fourni par une variable, celle-ci n’est évaluée qu’une fois et sa modification à l’intérieur de la boucle n’affecte pas le nombre d’itérations calculé au début.

For R As Single = 2 To -1 Step -.5 Console.WriteLine(R) ' Affichage les valeurs 2, 1.5, 1, 0.5, 0, -0.5, -1 Next

Pour rappel, un bloc de code ne bénéficie pas de l’effet de masque par rapport aux variables déclarées dans sa procédure ou fonction. La variable de boucle ne peut donc pas être déclarée localement si elle est déjà déclarée dans sa procédure ou fonction (hors d’un bloc de code). La déclaration de la variable de boucle lors de la définition de celle-ci en fait une variable locale au bloc de code de la boucle.

Private Sub MaSub() Dim x As Integer Dim k As Integer x = 0 While x < 5 ' Dim k As Integer ' Erreur : k est déjà déclarée dans MaSub Dim y As Integer ' C’est mieux comme ça et y est locale à x = x + 1 ' la boucle While y = y + 1 Console.WriteLine(x * y) End While ' For x As Integer = 1 To 5 ' Erreur : x est déjà déclarée dans MaSub For x = 1 To 5 ' C’est mieux comme ça Dim y As Integer ' y est locale à la boucle For y = y + 1 Console.WriteLine(x * y) Next x End Sub

Page 58: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 32

Une boucle ni logique, ni arithmétique La boucle "Pour chaque" ne requiert pas la définition d’une condition d’arrêt, ni la spécification d’un compteur. Elle permet l’accès à chacune des données contenues dans un tableau ou une collection. Module MonModule Private Structure MaStruct Dim Nom As String Dim Age As Integer End Structure Sub Main()

Dim TS(5) As MaStruct Dim MS As MaStruct Dim TI(5) As Integer ' … For Each N As Integer In TI ' Pour chaque N, qui est un entier contenu dans TI Console.WriteLine(N) ' Affiche la valeur de N Next For Each MS In TS ' Pour chaque MS, qui est une MaStruct contenue dans TS Console.WriteLine(MS.Nom & " " & MS.Age) Next

End Sub End Module Un autre exemple :

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

For Each TB As Object In Me.Controls ' Pour chaque TB, qui est un objet contenu If TB.GetType.Name = "TextBox" Then ' dans la collection des Controls de Form1 TB.Clear() ' lui-même (Me), s’il est de type TextBox, End If ' alors le vider de tout contenu Next ' …

L’appel de sous-programmes Les fonctions et les procédures sont les seuls sous-programmes utilisables en VB. Il s’agit de blocs de codes généralement spécialisés qui peuvent être exécutés plusieurs fois par un ou plusieurs codes appelants, ou en réponse à des évènements. Dans les pages précédentes, chaque Sub est un exemple de procédure. Pour le programmeur, ils présentent comme premier avantage qu’une fois bien mis au point, ils ne doivent plus être repensés ni ré-encodés. Mécanisme d’exécution d’un sous-programme.

Action(s)

Action(s)

Appel d’un sous-programme

Action(s)

Action(s)

Action(s)

Retour

Les fonctions ayant àretourner une valeur aucode appelant utilisel’instruction Return à ceteffet.

Code appelant

Procédure ou fonction

Page 59: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 33

L’exécution de programmes externes. L’appel, par l’application développée, d’un programme exécutable déjà existant est souvent bien utile. Qu’il s’agisse de lancer un programme dès que la nécessité se présente et sans intervention de l’utilisateur, ou bien d’exécuter des fichiers de commandes systèmes éventuellement créés par l’application. C’est l’espace de nom Microsoft.VisualBasic, créé pour permettre aux programmeurs VB 6.0 de retrouver bon nombre de leurs outils favoris, qui donne la commande Shell nécessaire à l’exécution de programmes externes. Cette librairie est référencée par défaut dans tout nouveau projet. Afin de se prémunir d’éventuelles incompatibilités futures, le programmeur doit n’utiliser les outils de Microsoft.VisualBasic que lorsqu’ils n’ont pas de remplaçant dans DotNet. Pour la plupart des applications, le programmeur peut même désactiver l’importation automatique de cette librairie. La désactivation de l’importation automatique de la librairie se règle dans les propriétés du projet, sous l’onglet Références. Il suffit de décocher la référence à Microsoft.VisualBasic.

Pour utiliser une librairie non encore présente dans un projet, il faut soit l’ajouter par le menu Projet/Ajouter une référence …, soit la spécifier en Imports là où elle est nécessaire. Cette deuxième solution est de loin préférable pour toute librairie d’usage occasionnel.

Imports Microsoft.VisualBasic Module MonModule Sub Main() ' … Shell("GoSyst.Bat", 1) ' Lance le fichier de commande GoSyst Shell("Calc.Exe", 1) ' Ouvre la calculette Shell("Attrib *.*", 1) ' Affiche les attributs des fichiers ' …

La commande Shell est une commande asynchrone. Cela signifie qu’elle n’attend pas la fin de l’exécution demandée pour rendre la main à l’application en cours. Toutefois, une valeur True pour troisième paramètre lui impose d'attendre la fin de la tâche lancée. Il faut y penser si le bon déroulement de l’application en cours dépend du résultat de l’exécution demandée par Shell. Le deuxième paramètre de la commande, dont la valeur dans cet exemple est 1 pour AppWinStyle.NormalFocus, exprime le mode d'affichage de la fenêtre (réduite dans la barre des tâches, normale, taille maximale). Sur certains systèmes, il faut désactiver les paramètres de sécurités (sous l’onglet sécurité des propriétés du projet) pour que l’exécution de Shell soit possible. Le mot de la fin Une dernière instruction de contrôle utile est celle qui provoque l’arrêt définitif du programme, c’est l’instruction "Fin". Le mot clé End ferme les fichiers ouverts avec Open et provoque l’arrêt inconditionnel de toute l’application. Module MonModule Sub Main() End ' Un programme qui ne fait rien ! End Sub End Module

Page 60: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 34

Les moyens de traitements Les opérateurs L’affectation = Cette opération, aussi appelée assignation, est celle par laquelle une valeur est attribuée à une variable.

Pi = 3.141592 La concaténation & et + La concaténation est la mise bout à bout de chaînes de caractères. Deux symboles & et + sont offerts. Il est

préférable d’employer & de sorte à n’induire aucune confusion avec l’addition arithmétique qui utilise le +.

Dim Nom As String = "Pierre" Console.WriteLine("Bon" & "jour " & Nom) ' Affiche Bonjour Pierre

Les opérateurs arithmétiques ^ Elévation à la puissance X = V ^ 2 si V = 3, X reçoit la valeur 9 (32 = 9) * Multiplication X = V * 2 si V = 4, X reçoit la valeur 8 (4 * 2 = 8) / Division réelle X = V / 2 si V = 5, X reçoit la valeur 2.5 (5 / 2 = 2.5) \ Division entière X = V \ 2 si V = 7, X reçoit la valeur 3 (7 \ 2 = 3) Mod Modulo (Reste) X = V Mod 2 si V = 7, X reçoit la valeur 1 (7 – (7 \ 2) * 2 = 1) + Addition X = V + 2 si V = 8, X reçoit la valeur 10 (8 + 2 = 10) - Soustraction X = V – 2 si V = 9, X reçoit la valeur 7 (9 - 2 = 7) Les formes contractées de l’affectation Il s’agit de la combinaison d’un des opérateurs précédents avec l’opérateur d’affectation ayant pour effet une économie d’écriture. On retrouve là tous les opérateurs arithmétiques, sauf Mod, et l’opérateur de concaténation. La signification de Variable Opérateur = Valeur est la même que celle de Variable = Variable Opérateur Valeur. ^= V ^= 2 est équivalent à V = V ^ 2 *= V *= 3 est équivalent à V = V * 3 /= V /= 4 est équivalent à V = V / 4 \= V \= 5 est équivalent à V = V \ 5 += V += 6 est équivalent à V = V + 6 -= V -= 7 est équivalent à V = V – 7 &= V &= "OK" est équivalent à V = V & "OK" Les opérateurs relationnels Les opérateurs relationnels, dits aussi opérateurs de comparaisons, sont indispensables à la définition des critères de choix des alternatives et à la définition des conditions d’arrêt des itérations logiques. Des expressions utilisant ces opérateurs fournissent des résultats booléens qui peuvent être affectés à des variables de ce type. < Inférieur à X = V < 2 si V = 3, X reçoit la valeur False <= Inférieur ou égal à X = V <= 2 si V = 3, X reçoit la valeur False > Supérieur à X = V > 2 si V = 3, X reçoit la valeur True >= Supérieur ou égal à X = V >= 2 si V = 3, X reçoit la valeur True = Egal à X = V = 2 si V = 3, X reçoit la valeur False <> Différent de X = V <> 2 si V = 3, X reçoit la valeur True Like Comme (usage des jokers ? et *) X = V Like "Pi*" si V = "Pierre", X reçoit la valeur True

Page 61: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 35

Les opérateurs logiques Lorsque les définitions des critères d’alternatives et des conditions d’arrêt d’itérations ne peuvent se suffire d’une seule expression relationnelle, les opérateurs logiques permettent d’en combiner plusieurs. Des expressions utilisant ces opérateurs fournissent des résultats booléens qui peuvent être affectés à des variables de ce type. Not Négation (contraire) And Et (les deux) Or Ou (un OU l’autre OU les deux) Xor Ou exclusif (un OU l’autre, mais pas les deux) AndAlso Et aussi OrElse Ou sinon Les résultats des expressions mettant en œuvre les opérateurs logiques sont prédéterminés par les tables de vérités. Conventionnellement, la valeur booléenne False est représentée dans ces tables par 0, et la valeur True est représentée par 1.

NOT OR AND XOR a R a b R a b R a b R

0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1 0 0 1 1 1 0 1 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0

Soit l’expression (X < 2) représentée par la lettre a et l’expression (Y >= X) représentée par la lettre b. Si X est effectivement plus petit que 2, l’expression X < 2 est vraie et elle est représentée par le chiffre 1 dans la colonne de la lettre a. Si X n’est pas plus petit que 2, l’expression est fausse et elle est représentée par le chiffre 0. Il en est de même pour l’expression représentée par b. Le résultat de l’usage de l’opérateur logique est représenté de la même manière dans la colonne de la lettre R. Le tableau montre ainsi, par exemple : Si (X < 2) est faux, a est faux et vaut 0, et Not a est vrai, et donc R vaut 1 Si (X < 2) est faux, et (Y >= X) est faux aussi, l’expression (X < 2) OR (Y >= X) est fausse, et donc R vaut 0 Si (X < 2) est vrai, et (Y >= X) est faux, l’expression (X < 2) OR (Y >= X) est vraie, et donc R vaut 1 Si (X < 2) est vrai, et (Y >= X) est faux, l’expression (X < 2) AND (Y >= X) est fausse, et donc R vaut 0 Si (X < 2) est vrai, et (Y >= X) est vrai, l’expression (X < 2) AND (Y >= X) est vraie, et donc R vaut 1 Plutôt que de tenter une mémorisation fastidieuse de ce tableau, mieux vaut sans doute retenir ceci : Not Tout ce qui est vrai devient faux, et vice versa And Pour que le résultat d’un And soit vrai, il faut que les deux expressions impliquées soient vraies Or Pour que le résultat d’un Or soit vrai, il suffit qu’une seule des expressions soit vraie Xor Pour que le résultat d’un Xor soit vrai, il faut qu’une des expressions soit vraie et que l’autre soit fausse Les opérateurs AndAlso et OrElse suivent les mêmes règles que, respectivement, And et Or. Cependant, leurs comportement est optimisé et donc quelque peu différent lors de l’évaluation des conditions qui les employent. (X < 2) Or (Y >= X) les deux expressions relationnelles sont évaluées d’abord, l’expression logique ensuite (X < 2) And (Y >= X) les deux expressions relationnelles sont évaluées d’abord, l’expression logique ensuite Or dans la première expression, si (X < 2) est vrai, il n’est plus nécessaire d’évaluer (Y >= X) car avec l’opérateur Or, il suffit d’un résultat vrai pour que toute l’expression le soit aussi. De même, dans la deuxième expression, si (X < 2) est faux, il n’est plus nécessaire d’évaluer (Y >= X) car avec l’opérateur And, il suffit d’un résultat faux pour que toute l’expression le soit aussi. (X < 2) OrElse (Y >= X) la deuxième expression relationnelle n’est évaluée que si la première est fausse (X < 2) AndAlso (Y >= X) la deuxième expression relationnelle n’est évaluée que si la première est vraie.

Page 62: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 36

Les opérateurs binaires Les opérateurs binaires permettent les opérations bit à bit sur les octets composant les nombres entiers. Ils ne sont applicables qu’aux numériques de type Byte, Short, Integer et Long. Not Complément à 1 (tous les bits sont inversés) And Garde les bits à valeur 1 dont le vis-à-vis à la valeur 1 Or Garde tous les bits à valeur 1 Xor Garde les bits à valeur 1 dont le vis-à-vis à la valeur 0 >> Décalage, d’un nombre donné de bits, de tous les bits vers la droite (les bits sortants sont perdus) << Décalage, d’un nombre donné de bits, de tous les bits vers la gauche (les bits sortants sont perdus) A B Not A A And B A Or B A Xor B A >> 2 A << 2Combinaison 00010111 00001010 11101000 00000010 00011111 00011101 00000101 01011100Valeur 23 10 -24 2 31 29 5 92 Les opérateurs binaires de décalage disposent également d’affectations contractées : >>= V >>= 3 est équivalent à V = V >> 3 <<= V <<= 4 est équivalent à V = V << 4 Ordre d’évaluation des expressions et hiérarchie des opérateurs Les parenthèses éventuelles encadrent dans les expressions des parties qui doivent être évaluées en premier. Si des expressions combinent plusieurs types d’opérations, l’évaluation commence toujours par la résolution des opérations arithmétiques. Les opérations relationnelles sont évaluées ensuite et les opérations logiques sont évaluées en dernier. A défaut de structuration des expressions par l’utilisation de parenthèses, les opérateurs arithmétiques ont la même hiérarchie qu’en mathématique. Par exemple, 3 + 2 * 23 vaut 49, la multiplication étant calculée avant l’addition. Les expressions combinant des opérateurs différents, mais de même type et de même niveau hiérarchique, sont évaluées de gauche à droite. Par exemple, 2 * 24 / 3 vaut 16 et 3 / 2 * 24 vaut 36. Les procédures et fonctions intrinsèques, et les méthodes Les procédures et les fonctions intrinsèques sont celles livrées par les librairies des langages. Encapsulées dans des objets, elles en deviennent les méthodes si leur concepteur a permis qu’elles soient visibles. Seuls quelques exemples illustrent ici ces moyens de traitements. D’autres sont étudiés dans ces pages là où leur usage est requis. Le lecteur peut explorer l’ensemble des outils des librairies de Visual Studio .Net en parcourant les pages de http://msdn.microsoft.com/fr-fr/library/default.aspx ainsi que l’aide intégrée dans son Visual Studio. La fonction CType fournit une valeur qui est le résultat de la conversion d’une valeur qui lui a été donnée dans un type de données qui lui a été indiqué.

Dim Nombre As String Dim Valeur As Integer Valeur = 12345 Nombre = CType(Valeur, String)

La procédure ReDim réalise l’allocation en mémoire de l’espace demandé pour le tableau qui lui est donné.

ReDim Tab5(2, 2) La méthode Sort() de la classe Array effectue le tri par ordre croissant du tableau qui lui est donné.

Array.Sort(Tab2)

Page 63: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 37

Déclarations des procédures et des fonctions Tout code exécutable doit être écrit à l’intérieur d’une procédure ou d’une fonction. La déclaration d’une procédure ou d’une fonction se compose des parties essentielles suivantes :

- la définition de la portée de la procédure ou fonction - la définition du type de sous-programme (Sub si procédure ou Function si fonction) - la définition de son nom (procédures et fonctions sont nommées selon les règles applicables aux variables) - la définition de ses arguments - la définition du type de valeur renvoyée (seulement pour la fonction)

Le type du sous-programme, son nom et la définition de ses arguments constituent ensemble la signature de la procédure ou fonction. A l'instar des variables, les procédures et les fonctions peuvent être déclarées Private, Public, Protected, Friend, Protected Friend, Shadows et Shared. Ces déclarations de portée ont la même signification ici que lors de la déclaration des variables. Mais attention, à défaut de déclaration complète, les procédures et fonctions sont Public. On ne peut définir une procédure (ou fonction) à l’intérieur d’une autre. Les procédures (ou fonctions) incluses dans des classes et accessibles dans les classes dérivées ou instanciées sont appelées méthodes.

Public Function MaFonction() As Integer Public Sub MaSub() Dim X As Integer Dim X As Integer ' …… calcul de la valeur de X ' …… code de la procédure Return X ' End Function End Sub

La fonction s’appelle par l’usage direct de son nom, c’est-à-dire par usage direct de la valeur renvoyée dans une affectation ou une expression. La procédure s’appelle par l’usage de son nom en dehors de toute expression.

MessageBox.Show(MaFonction()) MaVariable = MaFonction() * 3 MaSub()

Quelques vitamines, dites attributs, peuvent s’ajouter aux indicateurs de portée. Ce sont les mots Overloads, Overrides, Overridable, NotOverridable, MustOverride et, quoique de nature différente, Implements. Overloads L’attribut Overloads indique que la méthode en surcharge une ou plusieurs autres définies avec le même nom dans la même classe ou dans une classe de base. Quand la surcharge se fait au sein d'une même classe, les signatures doivent être différentes. Cela signifie notamment que les listes d’arguments doivent différer au niveau de leur nombre d'arguments, de leurs types de données ou des deux. C’est cette différenciation qui permet au compilateur de distinguer la version à utiliser. Et contrairement à la redéclaration avec Shadows qui peut être appliquée à n’importe quel objet accessible à partir d’une classe dérivée, la surcharge ne concerne que les méthodes. L’attribut Overloads peut être omis pour des surcharges définies dans la même classe. Toutefois, s’il est utilisé pour l’une, il doit l’être aussi pour les autres. Cet attribut ne peut être employé conjointement à Shadows. Overrides L’attribut Overrides indique que la méthode surdéfinit une procédure de même nom dans une classe de base. Contrairement à la surcharge par Overloads, la signature, la portée et le type de valeur de retour doivent ici être strictement identiques. La surdéfinition ne concerne que les méthodes qui doivent en outre, avoir été spécifiées Overridable dans la classe de base. Overridable L’attribut Overridable indique que la méthode peut être surdéfinie par une autre dans une classe dérivée. Par ailleurs, lorqu’une méthode en surdéfinit une autre, Overridable devient son attribut par défaut.

Page 64: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 38

' Shadows, Overloads, Overridable et Overrides

Public Class ClsBase Protected Function FB1(ByVal N1 As Byte, ByVal N2 As Byte) As Byte Return N1 + N2 End Function Protected Function FB2(ByVal N1 As Byte, ByVal N2 As Byte) As Byte Return N1 + N2 End Function ' La fonction FB3 est explicitement déclarée Overridable. Elle peut être surdéfinie Protected Overridable Function FB3(ByVal N1 As Byte, ByVal N2 As Byte) As Byte Return N1 + N2 End Function End Class Public Class ClsDeriv Inherits ClsBase ' Shadows permet la redéclaration de FB1, sans contrainte de respect de signature Shadows Function FB1(ByVal N1 As Byte, ByVal N2 As Byte) As Byte Return N1 + N2 + 1 End Function ' Overloads permet la surcharge de FB2 sans contrainte de signature dans la classe dérivée Overloads Function FB2(ByVal N1 As Integer, ByVal N2 As Integer) As Integer Return N1 + N2 End Function ' Overrides permet la redéfinition de FB3 déclarée Overridable, avec signature identique Protected Overrides Function FB3(ByVal N1 As Byte, ByVal N2 As Byte) As Byte Return N1 + N2 + 2 End Function End Class

NotOverridable L’attribut NotOverridable s’employe uniquement dans une classe dérivée et indique qu’une méthode surdéfinie ne peut plus l’être dans une classe sous dérivée. C’est l’attribut par défaut d'une méthode qui n’est pas la surdéfinition d’une autre appartenant à une classe de base. MustOverride L’attribut MustOverride indique que la méthode n’est pas implémentée dans la classe de base et qu’elle doit l’être dans une classe dérivée pour que cette classe puisse être créée. Il ne s'employe que dans une classe abstraite.

' NotOverridable et MustOverride

Public MustInherit Class BaseCls ' La classe doit être déclarée MustInherit pour ' permettre la déclaration MustOverride d’une

' méthode Protected Overridable Function FB4(ByVal N1 As Byte, ByVal N2 As Byte) As Byte Return N1 + N2 End Function Protected MustOverride Function FB5(ByVal N1 As Byte, ByVal N2 As Byte) As Byte ' … pas de code, ni de End Function ' End Function End Class

Page 65: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 39

Public Class DervCls Inherits BaseCls ' FB4 et FB5 surdéfinies ici ne peuvent plus être surdéfinies dans une classe ' qui serait dérivée Protected NotOverridable Overrides Function FB4(ByVal N1 As Byte,

ByVal N2 As Byte) As Byte Return N1 + N2 End Function Protected NotOverridable Overrides Function FB5(ByVal N1 As Byte,

ByVal N2 As Byte) As Byte Return N1 + N2 End Function End Class

Implements Nom_Interface.Nom_Méthode L’attribut Implements, qui ne s’écrit pas dans la définition de la portée, mais bien à la suite de la ligne de définition, indique que cette méthode implémente un homologue défini par une interface. L’usage des mots clés Interface et Implements sera étudié en temps opportun.

Public Interface Comparable Function Compare(ByVal Obj As Object) As Integer End Interface Public Class UneClasse Implements Comparable Public Function Compare(ByVal Obj As Object) As Integer Implements

Comparable.Compare ' … code de la fonction End Function End Class

Déclarations des arguments des procédures, fonctions et méthodes Un argument de procédure (ou fonction), et donc de méthode, définit une variable utilisable dans cette méthode pour y traiter les données qui lui sont passées en paramètres lors de l’appel. Cette variable est donc locale et bénéficie de l’effet de masque. Toutefois, bien qu’une variable définie en déclaration d’argument ne soit jamais accessible de l’extérieur de sa procédure (ou fonction), sa modification peut changer la valeur de la variable correspondante dans le code appelant. Les spécifications ByRef et ByVal et le type (Valeur ou Référence) de la variable déterminent la sécurité des données transmises. ByVal Un argument déclaré ByVal est strictement local s’il est d’un type Valeur. C’est une copie de la variable d'origine qui est transmise et sa modification n'affecte en rien la variable d'origine. S’il s’agit d’un type Référence, toute modification de la variable dans la procédure (ou fonction) modifie également la variable utilisée dans le code appelant. C’est la spécification par défaut. Le type String se comporte comme un type Valeur.

Private Sub ValByVal(ByVal a As Byte) a = a * 2 ' Multiplie l’argument par 2 End Sub ' La variable d’origine sera inchangée Private Sub ChaineByVal(ByVal Ch As String) Ch = Ch & "+++" ' Concatène "+++" à l’argument End Sub ' La variable d’origine sera inchangée Private Sub RefByVal(ByVal T() As Byte) T(2) = T(2) * 2 ' Multiplie un élement de l’argument par 2 End Sub ' La variable d’origine sera changée

Page 66: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 40

Private Sub Test() Dim s As String = "AZE" Dim x As Byte = 5 Dim T() As Byte = {10, 11, 12, 13, 14, 15} Console.WriteLine("Av.ValByVal X = " & x) ' Affiche : Av.ValByVal X = 5 ValByVal(x) Console.WriteLine("Ap.ValByVal X = " & x) ' Affiche : Ap.ValByVal X = 5 Console.WriteLine("Av.ChaineByVal s = " & s) ' Affiche : Av.ChaineByVal s = AZE ChaineByVal(s) Console.WriteLine("Ap.ChaineByVal s = " & s) ' Affiche : Ap.ChaineByVal s = AZE Console.WriteLine("Av.RefByVal T(2) = " & T(2)) ' Affiche : Av.RefByVal T(2) = 12 RefByVal(T) Console.WriteLine("Ap.RefByVal T(2) = " & T(2)) ' Affiche : Ap.RefByVal T(2) = 24 End Sub

ByRef Si l’argument est déclaré ByRef, alors la procédure (ou fonction) reçoit l'adresse mémoire de la variable et ce, quel que soit le type de cette variable. Dans ce cas, toute modification de la variable dans la procédure (ou fonction) entraîne la modification de la variable d'origine. Il faut être prudent quant aux manipulations effectuées sur cette variable à l'intérieur de la procédure ou de la fonction.

Private Sub ValByRef(ByRef a As Byte) a = a * 2 ' Multiplie l’argument par 2 End Sub ' La variable d’origine sera changée Private Sub RefByRef(ByRef T() As Byte) T(2) = T(2) * 2 ' Multiplie un élement de l’argument par 2 End Sub ' La variable d’origine sera changée Private Sub Test() Dim x As Byte = 5 Dim T() As Byte = {10, 11, 12, 13, 14, 15} Console.WriteLine("Av.ValByRef X = " & x) ' Affiche : Av.ValByRef X = 5 ValByRef(x) Console.WriteLine("Ap.ValByRef X = " & x) ' Affiche : Ap.ValByRef X = 10 Console.WriteLine("Av.RefByRef T(2) = " & T(2)) ' Affiche : Av.RefByRef T(2) = 12 RefByRef(T) Console.WriteLine("Ap.RefByRef T(2) = " & T(2)) ' Affiche : Ap.RefByRef T(2) = 24 End Sub

Optional Avec ByVal et ByRef, Optional et ParamArray constituent les quatre spécifications des arguments des procédures (ou fonctions). Le mot Optional indique que l'argument qu'il précède est facultatif. Cet argument peut donc être absent lors de l’appel de la procédure (ou fonction). Sa valeur par défaut doit être indiquée lors de la déclaration des arguments. Quand plusieurs arguments sont facultatifs, l'emploi de virgules lors de l'appel permet d'en omettre certains et pas d'autres. Les arguments sont évalués selon leur ordre de définition. Il est toutefois possible de désigner explicitement les arguments par leur nom lors de l'appel. Les arguments ainsi utilisés sont dits arguments nommés. Cette faculté, qui n'est pas réservée aux arguments facultatifs, permet de passer les variables dans un ordre différent de celui de la définition.

Private Sub Optionel(ByVal X As Byte, Optional ByVal Y As String = "Y absent") Console.WriteLine("Optionel " & X & " / " & Y) End Sub Private Sub Nommes(ByVal X As Byte, Optional ByVal Y As String = "Y",

Optional ByVal Z As String = "Z") Console.WriteLine("Nommes " & X & " / " & Y & " / " & Z) End Sub

Page 67: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 41

Private Sub Test() Optionel(1) ' Affichage de Optionel : Optionel 1 / Y absent Optionel(2, "Pierre") ' Affichage de Optionel : Optionel 2 / Pierre Nommes(3, Z:="Pierre") ' Affichage de Nommes : Nommes 3 / Y / Pierre Nommes(4, , "Pierre") ' Affichage de Nommes : Nommes 4 / Y / Pierre Nommes(Z:="Pierre", Y:="Bill", X:=5)' Affichage de Nommes : Nommes 5 / Bill / Pierre End Sub

ParamArray Cette spécification, qui doit être la dernière de la liste d’arguments, signifie que l’argument qui suit est facultatif et que s’il est présent, il est un tableau d'arguments du type précisé et de taille indéterminée. Un argument ParamArray est toujours passé à l'aide de ByVal.

Private Sub ParamArra(ByVal ParamArray PX() As Integer) Dim XX As Integer For Each XX In PX Console.Write(XX & " / ") Next XX Console.WriteLine() End Sub Private Sub Test() Dim T () As Integer = {101, 102, 103, 104} ParamArra() ' N’affiche rien dans ParamArra ParamArra(110, 120, 130) ' Affichage dans ParamArra : 110 / 120 / 130 / ParamArra(T(0), T(1), T(2)) ' Affichage dans ParamArra : 101 / 102 / 103 / ParamArra(T) ' Affichage de tous les éléments de T End Sub

Type de donnée des arguments Indépendamment des spécifications ByVal, ByRef, Optional et ParamArray, les arguments doivent être typés, c'est-à-dire que leur type de donnée doit être explicité. Les arguments peuvent Boolean, Byte, Char, Date, Decimal, Double, Integer, Long, Object, Short, Single ou String ou bien le nom d'une énumération, d'une structure, d'une classe ou d'une interface. Valeur de retour des fonctions Le type de la valeur retournée par une fonction doit être précisé dans la ligne de définition de la fonction. Les fonctions utilisent l’instruction Return pour simultanément envoyer le résultat de leur travail au code appelant et quitter la fonction.

Public Function MaFonction() As Byte ' … code de la fonction Return 10 ' … code non exécuté après la sortie inconditionnelle de la fonction …

Une ancienne méthode consiste à affecter le résultat au nom de la fonction. Cette opération ne provoque pas la sortie de la fonction. Le résultat n’est effectivement transféré qu’à la fin de la fonction ou lors d’une sortie forcée par Exit Function.

Public Function MaFonction() As Byte ' … code de la fonction MaFonction = 10 ' … code encore exécuté avant la sortie de la fonction …

Lors d’une sortie forcée par Exit Function, si le nom de la fonction n’a subi aucune affectation, la fonction retourne une valeur nulle selon le type de la valeur de retour : 0 pour les numériques, Nothing pour les objets, … .

Page 68: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBNF - 42

La récursivité La récursivité en informatique est la propriété qu’a une fonction ou une procédure de s’appeler elle-même. Cette technique de programmation est permise en VB.Net. L’usage de la récursivité est particulièrement indiqué pour les résolutions de problèmes nécessitant un nombre inconnu d’itérations. Toutefois, tout algorithme récursif peut être transformé en algorithme itératif. Tout appel récursif doit être programmé dans une alternative qui contient la condition d’arrêt de la récursivité, ou dans une boucle logique dont la condition d’arrêt est aussi la condition d’arrêt de la récursivité. Il y a lieu de distinguer la récursivité directe dans laquelle un sous-programme s’appelle lui-même et la récursivité croisée dans laquelle un sous-programme en appelle un autre qui à son tour appelle celui qui l’a appelé. Par exemple, le calcul de la somme des N premiers entiers positifs. Non récursif

Private Function SommeN(ByVal SN As Integer) As Integer Dim Som, i As integer Som = 0 For i = SN To 1 Step -1

Som = Som + i Next i Return Som

End Function

Récursif

Private Function SommeN(ByVal SN As Integer) As Integer If SN = 1 Then Return 1 Else Return (SN + SommeN(SN - 1)) End If

End Function

Page 69: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 1

Bases de la programmation visuelle et événementielle

en VB.Net

Page 70: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 2

Tables des matières des pages VBPV Programmation orientée objet, visuelle et événementielle ................................................................................................. 5 Une application Windows (Form et Button) .................................................................................................................. 6

Deux propriétés pour commencer : Name et Text ................................................................................................. 9 Propriétés généralement associées à la propriété Text : BackColor, ForeColor et Font .............................. 10

Quelques propriétés communes à la plupart des composants visuels .................................................................... 10 Quelques méthodes communes à la plupart des composants visuels ..................................................................... 11 Quelques événements communs à la plupart des composants visuels ................................................................... 11

Débogage et gestion des erreurs ....................................................................................................................................... 13 Le pas à pas ............................................................................................................................................................ 14 Le point d’arrêt ....................................................................................................................................................... 14 L’évaluation des variables en cours d’exécution .................................................................................................... 15 L’insertion d’envois de messages ........................................................................................................................... 15 La gestion des erreurs ............................................................................................................................................. 17 Générer des erreurs ................................................................................................................................................. 18

L’essentiel des composants visuels et de leurs membres ................................................................................................. 19 Le formulaire : Form ............................................................................................................................................. 19

Propriétés ..................................................................................................................................................... 19 Méthodes ...................................................................................................................................................... 21 Evénements .................................................................................................................................................. 22

Membres communs à la plupart des composants visibles ...................................................................................... 22 Propriétés ..................................................................................................................................................... 22 Méthode ....................................................................................................................................................... 23 Evénements .................................................................................................................................................. 23

L’étiquette : Label ............................................................................................................................................... 23 Propriétés ..................................................................................................................................................... 23

La boîte de texte : TextBox ................................................................................................................................. 24 Propriétés ..................................................................................................................................................... 24 Méthodes ...................................................................................................................................................... 25 Evénement .................................................................................................................................................... 25

La case à cocher : CheckBox ............................................................................................................................... 25 Propriétés ..................................................................................................................................................... 25 Evénements .................................................................................................................................................. 25

Le bouton radio : RadioButton ......................................................................................................................... 26 Les groupes d’objets : GroupBox et Panel ........................................................................................................ 27

Différences essentielles ................................................................................................................................ 27 L’étiquette hyperlien : LinkLabel ...................................................................................................................... 27 La boîte de liste : ListBox .................................................................................................................................. 28

Propriétés ..................................................................................................................................................... 28 Méthodes ...................................................................................................................................................... 29 Evénement .................................................................................................................................................... 29 Quelques exemples ...................................................................................................................................... 29

La liste de cases à cocher : CheckedListBox ................................................................................................... 30 La liste déroulante : ComboBox ............................................................................................................................ 30 La liste de visualisation : ListView .................................................................................................................... 30

Propriétés ..................................................................................................................................................... 30 Méthodes ...................................................................................................................................................... 32 Evénements .................................................................................................................................................. 32 Quelques exemples ...................................................................................................................................... 32

Les vues en arborescence : TreeView ................................................................................................................. 34 Propriétés ..................................................................................................................................................... 34 Méthodes ...................................................................................................................................................... 34 Evénément .................................................................................................................................................... 35 Lecture d’un TreeView ................................................................................................................................ 35

Le calendrier mensuel : MonthCalendar .......................................................................................................... 36 Le sélectionneur de date : DateTimePicker .................................................................................................... 36 La boîte à image : PictureBox .......................................................................................................................... 37 Le diviseur : Splitter ........................................................................................................................................ 38

Page 71: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 3

Une réalisation ............................................................................................................................................. 38 Son code ............................................................................................................................................. 38 Son résultat ........................................................................................................................................ 38

Le contrôle à onglets : TabControl ................................................................................................................... 39 Le menu : MainMenu et MenuStrip .............................................................................................................. 40 La minuterie : Timer ............................................................................................................................................ 41

Propriétés ..................................................................................................................................................... 41 Méthodes ...................................................................................................................................................... 41 Evénement .................................................................................................................................................... 41 Exemple ....................................................................................................................................................... 41

Les boîtes de dialogue ...................................................................................................................................................... 42 L’ouverture de fichiers : OpenFileDialog ...................................................................................................... 42

Propriétés ..................................................................................................................................................... 42 Méthode ....................................................................................................................................................... 43 Exemple ....................................................................................................................................................... 43

L’enregistrement de fichiers : SaveFileDialog .............................................................................................. 43 L’explorateur de dossiers : FolderBrowserDialog ....................................................................................... 43 Les polices de caractères : FontDialog ............................................................................................................. 44 Les couleurs : ColorDialog .............................................................................................................................. 44 L’impression des documents .................................................................................................................................. 45

Le moteur d’impression : PrintDocument .................................................................................................... 45 La mise en page : PageSetupDialog ............................................................................................................. 47 La sélection de l’imprimante : PrintDialog .................................................................................................. 47 La fenêtre de prévisualisation : PrintPreviewDialog .................................................................................... 47 Le composant de prévisualisation : PrintPreviewControl ............................................................................ 47 Exemple récapitulatif ................................................................................................................................... 47

Programmation multi formulaires ..................................................................................................................................... 49 L’ouverture de formulaires secondaires ................................................................................................................. 49 Le passage de données entre formulaires ............................................................................................................... 49 L’InputBox à l’ancienne ......................................................................................................................................... 55 Résumé des modes de communications abordés .................................................................................................... 56

L’exploitation des mémoires de masse ............................................................................................................................. 57 Les dossiers ............................................................................................................................................................ 57

Liste des unités ............................................................................................................................................. 57 Liste des dossiers d’une unité ...................................................................................................................... 57 Liste des fichiers d’un dossier ...................................................................................................................... 57 Liste des sous dossiers et fichiers d’un dossier ............................................................................................ 57 Créer un dossier ........................................................................................................................................... 58 Copier un dossier ......................................................................................................................................... 58 Déplacer un dossier ...................................................................................................................................... 58 Renommer un dossier ................................................................................................................................... 58 Supprimer un dossier ................................................................................................................................... 58

Les fichiers ............................................................................................................................................................. 59 Créer un fichier ............................................................................................................................................ 59 Copier un fichier .......................................................................................................................................... 59 Déplacer un fichier ....................................................................................................................................... 59 Renommer un fichier ................................................................................................................................... 59 Supprimer un fichier .................................................................................................................................... 59 Création, enregistrement et lecture d’un fichier texte .................................................................................. 60 Création, enregistrement et lecture d’un fichier de données à accès séquentiel .......................................... 61 Création, enregistrement et lecture d’un fichier de données à accès aléatoire ............................................. 62

Les modes d’ouvertures ..................................................................................................................... 62 Les types d’accès ............................................................................................................................... 62 Tableau récapitulatif des associations permises du mode d’ouverture et du type d’accès ................ 62 Quelques membres de la classe FileStream ................................................................................. 62 L’écriture et la lecture du fichier ....................................................................................................... 63 Exemple complet ............................................................................................................................... 64 La protection des données des fichiers lors des accès simultanés ..................................................... 70

Création, enregistrement et lecture d’un fichier binaire ............................................................................... 72 Inventaire des principaux outils concernant les dossiers et les fichiers ................................................................. 75

Page 72: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 4

Méthodes communes des classes Directory et File ...................................................................................... 75 Propriétés communes des classes DirectoryInfo et FileInfo ........................................................................ 75 Méthodes communes des classes DirectoryInfo et FileInfo ......................................................................... 75 Méthodes spécifiques de la classe Directory................................................................................................ 75 Propriétés spécifiques de la classe DirectoryInfo ........................................................................................ 75 Méthodes spécifiques de la classe DirectoryInfo ......................................................................................... 75 Méthodes spécifiques de la classe File ......................................................................................................... 76 Propriétés spécifiques de la classe FileInfo.................................................................................................. 76 Méthodes spécifiques de la classe FileInfo .................................................................................................. 76 La classe Stream ........................................................................................................................................... 77 Propriétés et méthodes de la classe StreamWriter ....................................................................................... 77 Méthodes de la classe StreamReader ........................................................................................................... 77 Méthodes de la classe BinaryWriter ............................................................................................................ 77 Méthodes de la classe BinaryReader ............................................................................................................ 78

Page 73: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 5

Programmation orientée objet, visuelle et événementielle Les périphériques d’entrées ont toujours été des générateurs d’événements en ce sens qu’ils informent le système de toute arrivée d’informations. Mais le concept de l’événementiel étend le champ de l’information au délà des données entrant dans le système. Les événements sont aussi, par exemple, le déplacement du curseur de la souris, le clic sur un objet, l’ouverture ou la fermeture d’une fenêtre, l’enfoncement ou le relâchement d’une touche du clavier. L’événement est la notion de base de cette programmation qui s’organise, non plus seulement en fonction de menus rigides en cascades, mais aussi en fonction d’événements dont certains sont invisibles pour l’utilisateur. Le programmeur dispose ainsi d’une plus vaste panoplie de détection des besoins de l’utilisateur. Tous les événements sont détectés par des objets. Presque tous les objets visibles sont détecteurs d’événéments. Certains objets sont générateurs d’événements en ce sens qu’ils détectent des événements qu’ils sont eux-mêmes entrain de produire (la fenêtre détecte l’événement de son ouverture). Le programmeur utilise ces événements pour déclencher des traitements à l’insu de l’utilisateur, sans lui donner l’occasion d’intervenir. La programmation visuelle permet la mise en place aisée d’objets détecteurs d’événements et la définition des codes des traitements appropriés aux événements détectés compte tenu des objectifs des applications. La notion de programmation visuelle tient essentiellement dans les fonctionnalités offertes par les compilateurs et environnements de développements. Dès lors qu’on aborde les aspects visuels et événementiels de la programmation, on est dans le monde de l’objet. De plus en .Net, "tout est objet". Il convient cependant de distinguer cette programmation basée sur l’association visible des objets et des événements, de la programmation orientée objet (POO). La programmation visuelle est la couche superficielle de la POO destinée au développement rapide d’applications pour environnements graphiques d’exploitation tels que Windows. La programmation orientée objet est la couche profonde où sont créés notamment, les outils et fonctions de la programmation visuelle. Alors que les structures mises en place par les programmeurs dans les anciens langages comme le Quick Basic, le Pascal, le C, le Cobol, …, contenaient uniquement des données, les structures de la POO quant à elles contiennent non seulement des données, mais également les fonctions nécessaires à leur traitement. Il faut ici considérer "données" au sens le plus large : ces structures sont notamment des descriptions d’objets visuels avec leurs variables (propriétés), leurs traitements (méthodes) et leurs événements. Ces objets visuels sont aussi appelés composants, ou contrôles. Les pages qui suivent concernent essentiellement les aspects visuels du développement sous VB.Net. Elles illustrent l’usage de la plupart des composants offerts et diverses fonctionnalités. Elles sont aussi l’occasion d’aborder quelques bonnes pratiques de la programmation. L’étude de la POO : elle sera détaillée ensuite.

Les boutons sont des détecteurs d’événements.

La fenêtre elle-même est détectrice d’événements tels que le clic, le double-clic, …

La fenêtre est aussi détectrice d’événements qu’elle génère tels que son ouverture, son activation, sa fermeture, …

Page 74: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 6

Une application Windows (Form et Button) Une première application Windows est idéale pour étudier l’usage du Form et du Button, ainsi que quelques points importants de la programmation visuelle sous l’environnement VB.Net. Au départ, VB.Net étant chargé, il s’agit de faire le choix de créer un Nouveau projet. Un premier formulaire s’ouvre alors et il ne faut pas accepter les informations proposées par défaut, mais plutôt le compléter avec des informations pertinentes. Outre de choisir le type de projet et son modèle, il faut désigner l’endroit de stockage des fichiers, le nom du dossier et le nom de la solution, c'est-à-dire le nom de l’application.

Après la validation de ces informations, VB.Net présente l’environnement de travail déjà observé. Les différents points des menus et outils divers sont abordés lorsqu’ils s’avèrent nécessaires. Pour l’instant, il importe de savoir comment obtenir la boîte à outils (menu Affichage/Boîte à outils ou bien par le bouton mis en évidence ci-dessous), et où nommer les objets utilisés dans l’application.

Boîte à outils Les composants visuels

Les fichiers de la solution dont les Références et le Form

Cette fenêtre présente les propriétés de l’objet sélectionné. Il faut l’utiliser pour nommer les objets visuels dès qu’ils sont déposés. C’est ici aussi que sont nommés significativement les Forms et les fichiers.

Cliquer ce bouton pour faire apparaître la boîte à outils.

Types de projets VB en ce qui nous concerne

Modèles Application Windows

Nom du dossier

Emplacement du dossier

Nom de l’application

Autoriser la création du dossier Sauf s’il existe déjà

Page 75: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 7

La sélection du fichier Form1.vb dans l’explorateur de solution provoque l’affichage du bouton qui permet l’accès à la feuille de code.

Comme déjà observé précédemment, d’entrée de jeu la feuille de code propose l’écriture d’une classe. Cela signifie que l’application composée du Form et de son code sera une instance de cette classe lors de l’exécution, c'est-à-dire un objet au sens de la POO. Cette classe est Public et porte le nom du formulaire. Elle hérite de Form, de la collection des Forms, de la librairie System.Windows. Cet héritage est réalisé dans le fichier Form1.Designer.vb écrit par le Concepteur Windows Form. Mais ces aspects ne sont pas pertinents pour l’instant. En revenant à l’écran précédent par l’onglet [Design] et, après sélection du Form, il convient de lui donner un nom significatif dans la fenêtre des propriétés, soit FBase pour cette application.

Le Form étant sélectionné par un clic dans l’onglet [Design], il est possible d’en changer le nom dans la fenêtre

des propriétés.

Le fichier Form1.vb étant sélectionné dans l’explorateur de solutions, il est possible d’en changer le nom dans la

fenêtre des propriétés.

Ne pas confondre nom de fichier et nom d’objet ! Tous deux ont leur importance et méritent de recevoir un nom représentatif de leur rôle dans le projet. Les noms attribués aux objets sontdirectement utiles dans le code et mieux ils sont représentatifs de l’objet, et plus facilement ils sont retrouvés par le programmeur parmi la multitude d’objets que peuvent contenir certaines applications.

Il est également permis de renommer le formulaire directement dans la

feuille de code.

Page 76: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 8

Sauf définition explicite, et à défaut d’une procédure Main() dans un module, c’est le premier formulaire (Form1) qui est le maître de l’application, c’est l’objet de démarrage. Dès lors qu’il est renommé FBase, par exemple, ou simplement que le développement du projet n’a pas débuté par le formulaire d’ouverture, il convient de désigner l’objet de démarrage dans les propriétés du projet (menu Projet/Propriétés du projet …). C’est aussi l’occasion de supprimer l’importation automatique de la librairie Microsoft.VisualBasic qui n’est idéalement utilisée qu’en cas de besoins par la commande Imports.

Pour continuer cette première application, il faut adapter la taille du formulaire, donner le texte de sa barre de titre (propriété Text), y placer un bouton, le nommer et lui attribuer le texte qui convient.

Après le choix d’un composant dans la boîte à outils (par un clic), sa mise en place sur le formulaire, l’attribution de son nom dans la fenêtre des propriétés et le réglage de sa propriété Text, la programmation visuelle est terminée. Il faut maintenant passer à la programmation événementielle. Il faut pour cela retourner à la feuille de code et utiliser ses listes déroulantes. Celle de gauche permet le choix de l’objet qui doit réagir à un événement, et celle de droite permet le choix de l’événement auquel doit répondre le code. La liste de droite propose les événements appropriés à l’objet sélectionné à gauche.

Page 77: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 9

Et voici la procédure événementielle en place. Le nom de l’événement associé au nom du bouton est visible sous deux formes : BFin_Click est le nom de la procédure et BFin.Click est le nom de l’événement. C’est le mot clé Handles qui réalise ici l’association entre la procédure et l’événement. Il ne manque plus que le code.

Il faut donc, pour parachever l’application, écrire le code de la procédure événementielle et compléter la feuille de code par les Imports éventuels et les déclarations d’options. Voici le programme complet : Option Explicit On ' Obligation de déclarer toutes les variables Option Strict On ' L'interdiction des conversions implicites des données oblige ' le programmeur à utiliser des fonctions de conversions, ce qui

' améliore la sécurité des données, mais peut alourdir le code Public Class FBase Private Sub BFin_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BFin.Click End ' Voici le code événementiel de ce premier programme End Sub End Class Et à l’exécution, un clic du bouton Fin arrête l’application :

Ce premier programme est composé de deux objets visuels (un Form et un Button) et d’une procédure événementielle. Quelques particularités de ces composants méritent d’être observées. Les propriétés, méthodes et événements d’un objet sont génériquement appelés membres de l’objet. Ils sont très nombreux et leur étude systématique ne présente guère d’intérêts. L’étude de tous les composants visuels proposés est également fastidieuse et superflue puisqu’ils sont presque tous d’un usage très semblable au niveau de la programmation. C’est pourquoi ces pages ne présentent qu’un échantillonnage de ces composants et pour chacun, quelques membres de chaque sorte, parmi les plus employés. De plus, lorsque des membres ont le même nom dans plusieurs objets, ils ont la même signification et les explications les concernant ne sont pas répétées. C’est ainsi qu’une des deux propriétés utilisées dans le programme précédent se retrouve dans tous les objets et l’autre dans tous les objets disposant d’une zone d’affichage de texte. Ces propriétés sont Name et Text.

Deux propriétés pour commencer : Name et Text Name Tous les objets doivent être nommés d’une façon représentative de leur rôle dans l’application. Il convient en outre de préfixer le nom d’une ou deux lettres rappelant le type de l’objet. C’est pourquoi le formulaire de l’application précédente a été nommé FBase (avec F comme Form) et le bouton a été nommé BFin (avec B comme Button). L’avantage de cette nomenclature est de permettre la différenciation claire des objets dans la liste déroulante de gauche lorsqu’on se propose d’écrire leur code événementiel. Il est ainsi facile de faire la différence entre une TextBox nommée TNomPers contenant le nom d’une personne et une ListBox nommée LBNomPers contenant les noms de toutes les personnes.

Page 78: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 10

Text Presque tous les objets disposant d’une zone d’affichage de texte ont une propriété Text. C’est elle qui contient le texte affiché. Pour un Button, ce texte est celui qui est affiché sur le bouton. Pour le Form, c’est le texte de la barre de titre. Pour un TextBox, c’est son contenu. Propriétés généralement associées à la propriété Text : BackColor, ForeColor et Font

BFin.BackColor = System.Drawing.Color.White ' Couleur du fond BFin.ForeColor = System.Drawing.Color.Black ' Couleur du texte BFin.Font = New System.Drawing.Font("Courier New", 10) ' Police Courier New ' de taille 10

Attention, ces mêmes propriétés réglées au niveau du Form n’affectent pas la barre de titre. A tester au cas par cas. Il a déjà été question des Imports dans ce cours. Voici un exemple : le code précédent peut s’écrire plus simplement si la déclaration Imports System.Drawing est placée à la suite des lignes d’options.

BFin.BackColor = Color.White ' Affectation d’une propriété BFin.ForeColor = Color.Black BFin.Font = New Font("Courier New", 10) ' Instanciation d’un objet ' Font

Il faut noter qu’il n’est pas possible dans ce cas d’utiliser l’économiseur de dactylographie With parce que System.Drawing n’est pas un objet ou une variable de type structure mais un espace de noms, une librairie.

Quelques propriétés communes à la plupart des composants visuels Location Cette propriété contient les coordonnées en pixels du coin supérieur gauche d’un objet par rapport au coin correspondant de l’objet qui le contient. Ces coordonnées sont accessibles en lecture via les deux sous propriétés X et Y et modifiables par le réglage des propriétés Left et Top exposées ci-après. La ligne de code suivante affiche les coordonnées du bouton BFin du programme précédent, exprimées en pixels et par rapport au coin supérieur gauche du formulaire FBase.

MessageBox.Show(CType(BFin.Location.X, String) & " " & CType(BFin.Location.Y, String))

Left et Top Ces propriétés contiennent respectivement la distance en pixels du bord gauche et la distance du bord supérieur d’un objet par rapport aux bords correspondants de l’objet qui le contient. En exprimant ces deux distances, la ligne de code suivante est tout à fait équivalente à la précédente.

MessageBox.Show(CType(BFin.Left, String) & " " & CType(BFin.Top, String)) Width et Height Ces propriétés contiennent respectivement la largeur et la hauteur de l’objet exprimées en pixels. La ligne de code suivante affiche la largeur et la hauteur du bouton BFin.

MessageBox.Show(CType(BFin.Width, String) & " " & CType(BFin.Height, String)) Enabled Cette propriété, qui peut être affectée d’une des valeurs True et False, ne cache pas l’objet mais en autorise ou interdit l’utilisation.

BFin.Enabled = True ' BFin utilisable BFin.Enabled = False ' BFin inutilisable

Page 79: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 11

Visible La propriété Visible permet de montrer ou cacher un objet à volonté selon la valeur qu’on lui affecte : True ou False. La première ligne de code ci-dessous montre le bouton BFin du formulaire FBase et la suivante l’efface. Par défaut, un composant visuel est toujours visible.

BFin.Visible = True ' BFin visible BFin.Visible = False ' BFin invisible

Quelques méthodes communes à la plupart des composants visuels Hide et Show Les méthodes Hide et Show permettent respectivement de cacher et montrer l’objet. Elles produisent ensemble le même effet que la propriété Visible selon la valeur qui lui est affectée.

BFin.Hide() ' BFin invisible BFin.Show() ' BFin visible Me.Hide() ' Le formulaire actif devient ' invisible. A ne pas faire !

Focus et Select Cette méthode donne le focus à l’objet, c'est-à-dire qu’elle le rend actif, lui donne la main. Cependant cette méthode ne peut pas toujours s’exécuter. Elle est dépendante du contexte d’exécution et elle peut s’exécuter quand la propriété en lecture seule CanFocus du contrôle a la valeur True. La méthode Select est une bonne alternative à la méthode Focus. C’est par cette méthode que le programmeur peut imposer l’encodage dans une TextBox plutôt que dans une autre, ou rendre actif un bouton par défaut.

BFin.Focus() ' C’est BFin qui a la main TUneTextBox.Select() ' C’est TUneTextBox qui a la main

ToString Cette méthode fournit des informations affichables au sujet de l’objet concerné.

MessageBox.Show(BFin.ToString) ' Affiche : System.Windows.Forms.Button, Text:Fin

MessageBox.Show(BFin.GetType.ToString) ' Affiche : System.Windows.Forms.Button MessageBox.Show(Me.ToString) ' Affiche : PremierProgramme.FBase,

Text:Premier programme MessageBox.Show(Me.BackColor.ToString) ' Affiche : Color [White]

Quelques événements communs à la plupart des composants visuels GotFocus et LostFocus Ces événements se produisent quand l’objet reçoit le focus ou le perd. Lorsqu’une méthode Focus est exécutée sur un objet, il reçoit le contrôle qui est ôté d’un autre objet. Ce dernier détecte l’événement LostFocus, et ensuite l’objet qui reçoit le focus détecte l’événement GotFocus.

BFin.Select() ' L’événement GotFocus est ensuite détecté ' par BFin

Click Cet événement se produit quand un objet est cliqué. Pour la plupart des objets, cela revient à lui donner le contrôle s’il ne l’a pas encore et dans ce cas, Click est détecté avant GotFocus.

Page 80: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 12

KeyPress Cet événement se produit quand une touche du clavier est enfoncée. L’événement fournit alors le caractère de la touche par la propriété KeyChar de son paramètre de type KeyPressEventArgs. Si l’objet possède une zone d’affichage du caractère pressé au clavier, c’est notamment le cas de la TextBox, le caractère est placé dans cette zone lors du relâchement de la touche.

Private Sub BFin_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles BFin.KeyPress

MessageBox.Show(e.KeyChar) ' Si x est la touche pressée, End Sub ' alors affichage : x

L’événement KeyPress ne se produit qu'à l'appui d'un caractère du clavier, mais non lors de l'appui d'une touche de contrôle ou d'une touche de fonction. Pour répondre à l'action d'une de ces touches, il faut utiliser l'événement KeyDown. La frappe d'un caractère provoque l'événement KeyPress qui est aussitôt suivi de l'événement KeyDown. Lors de l'appui d'une touche de contrôle ou de fonction, seul l'événement KeyDown est émis. La reconnaissance de la touche s'obtient par comparaison de la valeur de e.KeyCode avec une des valeurs d'une longue énumération de touches possibles. MouseMove Cet événement se produit lorsque le curseur de la souris est déplacé à la surface de l’objet. L’événement fournit les coordonnées du curseur par les propriétés X et Y de son paramètre de type MouseEventArgs.

Private Sub BFin_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles BFin.MouseMove

MessageBox.Show(CType(e.X, String) & " " & CType(e.Y, String)) End Sub ' Position du curseur

L’événement MouseMove se produit à chaque déplacement du curseur, aussi imperceptible soit-il. Son usage, qui accapare fortement le système, n’est que rarement requis. Les événements MouseEnter et MouseLeave suffisent dans la plupart des cas où il n’est pas nécessaire de connaître les coordonnées du curseur. MouseEnter Cet événement se produit lorsque le curseur de la souris atteint la surface de l’objet.

Private Sub BFin_MouseEnter(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BFin.MouseEnter ' … End Sub

MouseLeave Cet événement se produit lorsque le curseur de la souris quitte la surface de l’objet.

Private Sub BFin_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles BFin.MouseLeave

' … End Sub

Page 81: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 13

Débogage et gestion des erreurs Le débogueur Visual Studio .NET est un puissant outil grâce auquel il est possible d’observer le comportement du programme au moment de l'exécution et de déterminer l'emplacement des erreurs. Il comprend des fonctionnalités qui sont intégrées dans les langages de programmation et leurs bibliothèques. Avec le débogueur, l'exécution du programme peut être arrêtée (suspendue) pour permettre au programmeur d'examiner le code, de l’exécuter pas à pas, et d’en évaluer les variables. Il n’est pas question de faire ici un cours détaillé sur l’usage du débogueur, mais quelques fonctionnalités sont des plus utiles en cours de développement :

Le fonctionnement en pas à pas Le point d’arrêt L’évaluation des variables en cours d’exécution L’insertion d’envois de messages

L’environnement Visual Studio .Net effectue l’analyse du code en cours de dactylographie. Pendant cette analyse, deux aides essentielles sont activées. La première, c’est l’aide en ligne qui informe le programmeur sur la syntaxe et les paramètres à fournir pour l’instruction commencée. La seconde, c’est le soulignement de la plupart des fautes de syntaxe, à la manière du correcteur orthographique du traitement de textes.

A partir de la version 2005 de Visual Studio, l'environnement signale également des anomalies qui ne portent pas forcément préjudice au bon fonctionnement du programme. Ces warnings sont également soulignés d'un trait ondulé, mais celui-ci est de couleur verte.

Si le programmeur lance l’exécution avec son code erroné, il est averti que des erreurs sont détectées. Il a alors le choix d’arrêter l’exécution ou la poursuivre. Dans ce dernier cas, les lignes fautives sont ignorées. S’il préfère corriger son code, la fenêtre Liste des tâches l’informe sur le type et l’endroit de l’erreur. Un double clic sur une ligne ainsi présentée renvoie directement le programmeur là où une correction s’impose. Les messages d’erreurs disparaissent de la liste au fur et à mesure des corrections, sans qu’il soit nécessaire de recompiler.

L’instruction est complétée, mais le programmeur a oublié qu’une valeur littérale alphanumérique s’encode entre guillemets. Il y a faute de syntaxe. Un soulignement subsiste.

C’est une propriété de l’objet qui est choisie ici. Une aide contextuelle informe sur son type et sur son rôle. Mais l’instruction est toujours incomplète. Un caractère reste souligné.

Le nom de l’objet a été corrigé, mais l’instruction est toujours incomplète. Un caractère reste souligné. Dès la frappe du point, s’affiche la liste des vitamines plausibles pour ce cas.

Le soulignement bleu ondulé en cours de frappe avise le programmeur que son instruction est erronée ou incomplète. Dans cet exemple, le nom de l’objet est incorrect.

Page 82: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 14

Mais il n’y pas que des fautes de syntaxe dans un programme en cours de développement. Lorsque le programme est exécuté, le programmeur a l’occasion de constater que le déroulement ne s’effectue pas comme prévu, que les résultats ne sont pas ceux attendus. Il y a des erreurs logiques. C’est ici que les fonctionnalités du débogueur énumérées ci-dessus s’avérent très utiles.

Le pas à pas C’est l’exécution ligne par ligne du code, le programmeur activant l’exécution de la ligne présentée quand il le souhaite selon un des deux modes possibles. Le pas à pas détaillé se commande par la touche <F11> et le pas à pas principal par <F10>. En mode détaillé, l’exécution se poursuit ligne par ligne jusqu’à l’intérieur des fonctions et procédures. Lorsque l’exécution est sur le point d’entrer dans un sous-programme qu’il n’est pas opportun d’étudier ainsi, le mode principal en effectue l’exécution sans y entraîner le programmeur.

Au sein d’une même procédure, le programmeur peut provoquer la ré-exécution d’une ligne ou au contraire éviter qu’une ligne soit exécutée. Il lui suffit pour cela de glisser la flèche jaune jusqu’à la ligne de code souhaitée.

Le point d’arrêt Comme il n’est pratiquement jamais utile d’exécuter tout le code en pas à pas, le programmeur place un point d’arrêt dans son code à l’endroit à partir duquel il souhaite étudier l’exécution en détail. L’exécution du programme se déroule ordinairement jusqu’à la première ligne marquée d’un point d’arrêt et se met alors automatiquement en pas à pas. Le programmeur peut poursuivre par <F11> ou <F10>. Quand le programmeur le souhaite, l’appui de la touche <F5> provoque l’abandon du pas à pas jusqu’au prochain point d’arrêt ou jusqu’à la fin du programme. Un point d’arrêt est marqué par un clic dans la marge gauche de la feuille de code en regard de la ligne de code souhaitée pour le départ du pas à pas. Un point brun est placé dans la marge et la ligne de code est surlignée. Un point d’arrêt ne peut être fixé sur une déclaration, mais seulement sur une instruction valide.

La ligne de code sur le point d’être exécutée est désignée par une flèche jaune dans la marge gauche de la feuille de code et la ligne de code elle-même est surlignée. Pour que la ligne s’exécute, le programmeur doit presser <F11>. Dans l’exemple ci-contre, la ligne suivant celle indiquée par la flèche fait appel à la fonction SommeN(). Le moment venu d’exécuter cette ligne, le programmeur doit presser <F11> s’il souhaite exécuter lafonction en pas à pas, et <F10> dans le cas contraire.

Le pas à pas doit commencer à cette ligne.

Instruction à exécuter par <F11> ou <F10>

Page 83: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 15

L’évaluation des variables en cours d’exécution Pendant l’exécution en pas à pas, il suffit d’amener le curseur de la souris sur une variable pour que s’affiche spontanément sa valeur. En outre, une fenêtre présentant divers onglets dont Variables locales et Espion est présente pendant le débogage. On accède en permanence aux valeurs de toutes les variables de la procédure étudiée par l’onglet Variables locales. L’onglet Espion permet la garde de variables explicitement désignées de n’importe quelle procédure. La mise en garde d’une variable s’effectue par un copier de la variable à partir du code et son coller dans cet onglet.

L’insertion d’envois de messages L’insertion d’envois de messages n’est plus du ressort du débogueur, mais bien d’un jeu d’outils adéquats offerts par le langage. Comme de tout temps, le programmeur peut placer des instructions de sortie avec les messages qu’il souhaite aux endroits qu’il veut tester. Mais cette méthode présente au moins deux inconvénients. D’une part ces messages, qui ne peuvent généralement pas subsister dans la version compilée définitive, doivent être retirés manuellement. D’autre part, les contenus des messages disparaissent aussitôt l’exécution terminée et ne supportent donc pas une deuxième lecture. Les moyens offerts par VB.Net sont communs à tout Visual Studio .Net et ils sont livrés par les deux classes Trace et Debug. Ces deux classes sont identiques sauf que les méthodes de Trace subsistent dans les versions définitives tandis que celles de Debug ne sont pas intégrées dans les exécutables. Ces classes envoyent leurs messages vers des écouteurs qui les redirigent vers des sorties désignées. Il y trois types d’écouteurs prédéfinis :

1. TextWriterTraceListener redirige la sortie vers une instance de la classe TextWriter ou tout autre type de la classe Stream (Console ou fichier)

2. EventLogTraceListener redirige la sortie vers un journal d’événements 3. DefaultTraceListener renvoie les messages vers les méthodes OutPutDebugString et Debugger.Log.

Pendant la mise au point des codes, ces messages sont affichés dans la fenêtre Sortie de Visual Studio. C’est le seul écouteur par défaut des classes Trace et Debug car c’est le seul qui soit automatiquement inclus dans la collection Listeners.

Tous les écouteurs de la collection reçoivent les mêmes messages des méthodes de sorties. Simplement, chacun le redirige vers la sortie qui lui a été désignée, un flux pour TextWriterTraceListener et un journal d’événements pour EventLogTraceListener. Les méthodes de sorties pour Trace et Debug sont Write, WriteLine, Fail qui émettent inconditionnellement leurs sorties, et WriteIf, WriteLineIf, Assert pour lesquelles les sorties sont soumises à conditions.

Page 84: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 16

Quelques précisions s’imposent au sujet des classes Trace et Debug.

Les classes Trace et Debug partagent la même collection Listeners. Dès lors, l’ajout d’un écouteur à l’une ou l’autre de ces deux classes profitent à toutes les deux. Quand un seul écouteur doit être utilisé, tant en débogage qu’en exécution, il faut vider la collection avant d’y insérer l’écouteur souhaité.

En ce qui concerne les méthodes de sorties des écouteurs, il faut encore savoir que la différence entre Write et WriteLine est la même que celle qui existe entre les méthodes de même nom qui servent à la sortie des données en application Console. La présence du suffixe If désigne seulement le mode conditionnel. Il existe encore des méthodes Fail et Assert qui ne présentent pas d’intérêts ici. Le lecteur peut se reporter à l’aide en ligne de son Visual Studio, et au site MSDN de Microsoft déjà référencé, pour compléter son information à ce sujet. Voici l’essentiel ordinairement nécessaire au programmeur : Dim FichierTrace As New TextWriterTraceListener("MonFichierMsg.txt")

Trace.Listeners.Clear() ' Collection vidée Trace.Listeners.Add(FichierTrace) ' Ce fichier à l’indice 0

… Trace.Listeners(0).WriteLine(Date.Now & " MonMessage") ' Date, heure, message

… FichierTrace.Close() ' Vider le buffer

Debug.WriteLine("Message inconditionnel") ' Trace ou Debug Debug.WriteLineIf(Condition, "Message si condition réalisée")

En mode Debug, tous les messages sont envoyés, tandis qu’en mode Release, qui est le mode de compilation des versions définitives, seuls les messages Trace subsistent. Bien entendu, le lancement du fichier exécutable ne produit aucune sortie dans ce cas, aucune redirection n’ayant été définie.

Ici, DefaultTraceListener n’est plus le seul écouteur. Un TextWriterTraceListener a été ajouté et il dirige ses sorties vers la console. Un fichier ouvert aurait pu être désigné à la place de Console.Out. Il résulte de cette modification que tous les messages sont maintenant envoyés à l’écran et dans la fenêtre Sortie en mode Debug. La version exécutable quant à elle n’enverra que les messages Trace à l’écran.

Une ligne Trace.Listeners.Clear() avant l’ajout d’un écouteur vide la collection de ceux qui s’y trouve déjà, dont DefaultTraceListener.

Page 85: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 17

La gestion des erreurs Le débogage permet la mise au point d’une application mais ne la protège pas des erreurs qui surviennent en cours d’exploitation. Le traçage vers un fichier de l’exécution d’une version compilée peut informer le programmeur sur les circonstances des erreurs. Ces erreurs sont le plus souvent dues à une mauvaise utilisation de l’application. Le programmeur doit s’efforcer de prévoir toutes les maladresses dont peut être victime son application et l’en protéger. Outre de programmer des contrôles de validité des données à traiter, le programmeur peut user d’un outil du langage qui permet la prévention et la récupération des erreurs sans programmation excessive. Sans contrôle de validité des données, ni récupération de l’erreur :

Private Sub UneProcedure() Dim N1, N2, R As Integer …

R = N1 / N2 Console.WriteLine(CType(R, String))

… End Sub

Avec contrôle de validité des données, sans récupération de l’erreur :

Private Sub UneProcedure() Dim N1, N2, R As Integer … If N2 <> 0 then R = N1 / N2 Console.WriteLine(CType(R, String)) Else ' … traitement approprié de l’erreur End If … End Sub

Sans contrôle de validité des données, avec récupération de l’erreur :

Private Sub UneProcedure() Dim N1, N2, R As Integer … Try R = N1 / N2 Catch MonErr As Exception ' … traitement approprié de l’erreur Finally ' … traitement à faire en tous cas End Try … End Sub

Attention, la récupération d’une erreur sans sa gestion peut générer des résultats erronés. Dans l’exemple ci contre, aucun message ne vient perturber l’utilisateur et l’application ne s’arrête pas, mais la valeur affichée est erronée quand N2 vaut 0.

L’affichage réalisé par cette procédure est le résultat de la division de N1 par N2 si tout va bien. Si N2 vaut 0, alors unefenêtre du Just-In-Time Debugging vient proposer un débogage dont l’utilisateur ne peut rien faire. Le programme est ensuite arrêté. Si les variables étaient ici de type réel, la division ne produirait pas d’erreur, mais le résultat serait +Infini (si N1 est positif). Ceci n’est guère satisfaisant, mais sans erreur. C’est au programmeur d’assurer le contrôle de validité des données.

Le programmeur, qui contrôle ici la validité de la variable N2, peut gérer l’erreur comme il le souhaite. Soit ignorer l’appel de cette procédure par un Exit Sub, soit informer l’utilisateur par un message convivial, soit encore effectuer n’importe quel traitement qu’il juge utile.

Private Sub UneProcedure() Dim N1, N2, R As Integer … Try R = N1 / N2 Catch Console.WriteLine(CType(R, String)) Finally ' … traitement à faire en tous cas End Try … End Sub

Le programmeur, qui ne contrôle pas ici la validité de la variable N2, peut toutefois récupérer l’erreur et la gérer comme dans l’exemple précédent. C’est le bloc de contrôle Try … End Try qui permet cetterécupération.

Page 86: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 18

Les mots clés de cet outil de récupération des erreurs sont Try, Catch, Finally, When, Exit Try et End Try. L’emploi de Exit Try permet de ne pas exécuter les autres instructions du bloc Try. Le mot clé Catch permet la désignation éventuelle du type d’erreur à prendre en compte et le mot When permet d’en préciser la condition d’interception. Plusieurs instructions Catch peuvent se succéder et il convient dans ce cas de les classer par type d’erreur du plus précis vers le moins précis.

Private Sub UneProcedure() Dim N1, N2, R As Integer … Try

R = N1 / N2 If N1 = 1 Then Exit Try ' Il peut être opportun de ne pas exécuter

' sinon suite des traitements ' la suite Catch MonErr As DivideByZeroException ' Les Catch, à partir du plus précis … ' … traitement approprié Catch MonErr As ArithmeticException ' … traitement approprié Catch MonErr As Exception When N1 = 1 ' Attention : pas de récupération si N1 <> 1 ' … traitement approprié Catch ' … jusqu’au plus vague (Erreur indéterminée) ' … traitement approprié Finally ' Le bloc Finally est facultatif, mais s’il ' … traitement à faire en tous cas ' est présent, ses traitements sont exécutés End Try ' même en cas d’erreurs, ou d’Exit Try, ou de … ' Return (quand Try est dans une fonction) End Sub

Générer des erreurs Une instruction particulière, Throw, permet au programmeur de générer les exceptions appropriées aux erreurs qu’il définit. Voici son fonctionnement par un exemple extrait d’une application MonApplication. Une procédure à risque, Calcul, est appelée à partir d’un bloc Try … End Try de la procédure UneProcedure. Le programmeur, détecte la situation d’erreur dans Calcul par un test de validité de l’opérande Arg2 et génère lui-même une erreur avec un message adéquat à l’intention de la procédure appelante. Dans Calcul, c’est la ligne Throw New ApplicationException("MonErreur") qui retourne l’erreur de type ApplicationException avec le message MonErreur. Dans cet exemple, "MonErreur dans MonApplication" est la phrase affichée par le bloc Try de la procédure appelante quand sa variable Terme2 vaut 0.

Private Sub UneProcedure() Dim Resultat, Terme1, Terme2 As Integer ' … acquisition des valeurs pour Terme1 et Terme2 Try Calcul(Resultat, Terme1, Terme2) MessageBox.Show(Resultat) Catch UneErreur As ApplicationException MessageBox.Show(UneErreur.Message & " dans " & UneErreur.Source) End Try End Sub Private Sub Calcul(ByRef Rep As Integer, ByVal Arg1 As Integer,

ByVal Arg2 As Integer) If Arg2 = 0 Then Throw New ApplicationException("MonErreur") Else Rep = Arg1 / Arg2 End If End Sub

Page 87: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 19

L’essentiel des composants visuels et de leurs membres

Le formulaire : Form Propriétés Icon Cette propriété associe un fichier icône au formulaire. L’icône s’affiche dans la barre de titre à son extrémité gauche. Quand le formulaire est celui de démarrage, son icône est aussi celle de l’application. Visual Studio donne la possibilité de créer une icône par le menu Fichier/Nouveau /Fichier …/Fichier d’icône et de la modifier. Cursor Cette propriété qui peut aussi être réglée au niveau des contrôles, définit le style du curseur par défaut sur le formulaire. Les différentes valeurs sont disponibles dans la classe System.Windows.Forms.Cursors. BackgroundImage Définit l’éventuelle image de fond du formulaire. Si l’image est plus petite que le formulaire, elle est répétée en mosaïque, sinon elle est tronquée. WindowState Définit ou restitue l’état d’affichage du formulaire.

FormWindowState.Maximized Plein écran. Equivalent au clic du bouton Agrandir FormWindowState.Normal Taille normale. Equivalent au clic du bouton Restaurer FormWindowState.Minimized Réduit dans la barre des tâches. Equivalent au clic du bouton Réduire

ControlBox Indique si le bloc de boutons de contrôles situé dans le coin supérieur droit du formulaire est présent ou pas. D’autres propriétés sont dépendantes du ControlBox : MaximizeBox , MinimizeBox et HelpButton.

MaximizeBox Détermine si le bouton Agrandir du bloc peut être utilisé. Si cette propriété a comme valeur False, ce bouton est inactif.

MinimizeBox Détermine si le bouton Réduire du bloc peut être utilisé. Si cette propriété a comme valeur False, ce bouton est inactif.

HelpButton Affiche un bouton d’aide dans le ControlBox seulement si les boutons MaximizeBox et MinimizeBox sont tous les deux inactifs, auquel cas ils ne sont d’ailleurs plus affichés.

Pour répondre au clic sur le HelpButton, il faut programmer la réponse à l’événement HelpRequested du formulaire. Il est possible que le clic de ce bouton ne produise pas cet événement sur tous les systèmes, mais avec ou sans le HelpButton, l’appui de <F1> génère l’événement.

Private Sub FBase_HelpRequested(ByVal sender As Object, ByVal hlpevent As System.Windows.Forms.HelpEventArgs) Handles MyBase.HelpRequested

' Traitement pour fournir l'aide End Sub

AcceptButton Permet la désignation d’un bouton pour lequel est généré un événement Click lorsque le formulaire a le focus et que la touche <Enter> est pressée. C’est généralement un bouton OK qui est désigné comme AcceptButton.

Page 88: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 20

CancelButton Permet la désignation d’un bouton pour lequel est généré un événement Click lorsque le formulaire a le focus et que la touche <Escape> est pressée. C’est généralement un bouton Annule qui est désigné comme CancelButton. AutoScale La propriété AutoScale autorise ou pas le redimensionnement automatique des contrôles et du formulaire lors d’un changement de la police d’écran. AutoScroll La propriété AutoScroll autorise ou pas le placement automatiquement des barres de défilement lorsque la taille du formulaire ne permet pas l’affichage de tous les contrôles qu’il contient. FormBorderStyle Cette propriété détermine le style du formulaire.

None Fenêtre sans bord ni barre de titre, non dimensionnable et non déplaçable FixedSingle Fenêtre ordinaire non dimensionnable mais déplaçable Fixed3d Mêmes propriétés que FixedSingle, mais avec un aspect 3D FixedDialog Fenêtre non dimensionnable mais déplaçable Sizable Fenêtre ordinaire dimensionnable et déplaçable FixedToolWindow Fenêtre non dimensionnable mais déplaçable, sans icône ni ControlBox SizableToolWindow Fenêtre dimensionnable et déplaçable, sans icône ni ControlBox

IsMDIContainer Détermine si le formulaire est un conteneur MDI (Multiple Documents Interface), c’est-à-dire s’il est capable de contenir d’autres fenêtres. Quand une fenêtre MDI est créée, c’est par le code qu’il faut y placer les feuilles filles.

Dim F As Form = New NomFeuilleFille ' NomFeuilleFille est le nom du Form à charger F.MDIParent = Me F.Show()

StartPosition Permet de choisir la position de la fenêtre lors de son ouverture.

Manual Position définie par la propriété Location CenterScreen Centré par rapport à l’écran WindowsDefaultlocation Situé à l’emplacement par défaut de Windows avec la taille définie dans Size WindowsDefaultBounds Situé à l’emplacement par défaut de Windows avec la taille par défaut de

Windows CenterParent Centré par rapport à la fenêtre ayant déclenché l’ouverture.

TopMost Si cette option est activée (True) le formulaire est toujours à l’avant plan, même quand il n’est pas actif. Cette option est utile pour les fenêtres dont les contenus doivent toujours être visibles. Locked C’est une propriété au service du programmeur. En effet, quand elle a la valeur True elle empêche toute modification des propriétés visuelles du composant pendant le développement. Elle n’a aucune incidence sur l’application en cours d’exécution.

Page 89: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 21

LayoutMode, SnapLines et SnapToGrid C’est par le menu Outils/Options/Concepteur Windows Forms/Général qu’il convient de régler la propriété LayoutMode à SnapLines ou SnapToGrid (l’effet du réglage s’observe après le redémarrage de l’environnement). La propriété LayoutMode aide le programmeur à dimensionner et à positionner les composants visuels sur un formulaire. La valeur SnapLines provoque l’apparition de lignes de repérage par rapport aux composants déjà placés. La valeur SnapToGrid provoque l’apparition d’une grille de points auxquels s’accrochent les composants. Opacity Définit l’opacité générale d’un formulaire par une valeur allant de 0% à 100%. Pour obtenir l’illustration ci contre, l’opacité a été réglée à 75%. TransparencyKey Permet la transparence totale de tout objet du formulaire qui a la couleur désignée. Pour obtenir l’illustration ci contre, la couleur choisie a été le jaune, le bouton Test ayant cette couleur. ShowInTaskBar Cette propriété, qui a la valeur True par défaut, empêche la visibilité de l’application dans la barre des tâches lorsque sa valeur est False. Méthodes Activate La méthode Activate permet de remettre un formulaire au premier plan et de lui donner le focus. Close Cette méthode ferme le formulaire et libère la mémoire des ressources utilisées. Dans le premier programme de cette partie du cours, l’usage de l’instruction End ou de la méthode Me.Close()produit le même résultat. ShowDialog Affiche le formulaire en tant que feuille modale, c’est à dire que la fenêtre reste au premier plan tant qu’elle n’est pas fermée et empêche l’accès aux autres feuilles de l’application. L’objet MessageBox est toujours une fenêtre modale. BringToFront Cette méthode pousse le formulaire à l’avant plan. Contrairement à l’effet produit par la propriété TopMost, qui place définitivement le formulaire en avant, celui de BringToFront est temporaire.

Page 90: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 22

Evénements Activated et Deactivate L’événement Activated survient quand un formulaire reçoit le focus, qu’il devient actif. L’événement Deactivate se produit évidemment dans le cas contraire. Ces événements se produisent notamment quand le focus passe de la fenêtre d’une application à celle d’une autre application, ou d’une fenêtre à l’autre dans une même application. DoubleClick Comme son nom l’indique … Load Cet événement se produit avant le premier affichage du formulaire. Closing et Closed L’événement Closing se produit pendant la fermeture du formulaire et Closed quand il est effectivement fermé.

Membres communs à la plupart des composants visibles Propriétés Déjà vues : Enabled, Location, Locked, Text, Visible Anchor Les ancres permettent de fixer la position d’un contrôle de sorte qu’il la conserve ensuite lors des redimensionnements du formulaire. L’effet est assuré tant en développement qu’à l’exécution. Valeur par défaut : None. L’affectation simultanée des quatre valeurs Top, Bottom, Left et Right offre une mise à l’échelle globale des composants. Les propriétés Anchor et Dock s’excluent mutuellement, seule la dernière affectée est valide. Dock Similaire à Anchor, sauf qu’elle définit une seule ancre, la propriété Dock permet d’ancrer un contrôle à un bord du conteneur. Valeur par défaut : None. Les propriétés Anchor et Dock s’excluent mutuellement, seule la dernière affectée est valide.

Left L’objet occupe tout le bord gauche Right L’objet occupe tout le bord droit Top L’objet occupe tout le bord supérieur Bottom L’objet occupe tout le bord inférieur Fill L’objet occupe toute la surface du conteneur

Modifiers Cette propriété définit la portée de l’objet au niveau de la programmation. Par défaut, sa valeur est Friend.

Public Accessible à partir de tous les éléments de la solution. Protected Accessible à partir des membres de la classe et des sous classes Protected Friend Correspond à l’union des visibilités Friend et Protected Friend Accessible à partir de tous les éléments du projet. Private Accessible à partir des membres de la classe

Size Cette propriété contient la taille du contrôle. Elle est composée de deux sous propriétés, Width et Height.

Page 91: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 23

ClientSize Cette propriété contient la taille intérieure du contrôle, c'est-à-dire l'espace intérieur disponible. Elle est composée de deux sous propriétés, Width et Height, dont les valeurs sont toujours inférieures à celles de la propriété Size du même composant. La différence est particulièrement importante pour un Form où il faut déduire de Size, non seulement les épaisseurs des bords, mais aussi la hauteur de la barre de titre. TabIndex Ordre d’accès au contrôle par la touche <Tab>. Tous les objets posés sur un formulaire reçoivent une valeur pour cette propriété. La valeur par défaut correspond à l’ordre d’arrivée de l’objet sur la feuille : 0 pour le premier, 1 pour le deuxième, … jusqu’à N-1 pour le Nième. Tag Voici une sympathique propriété qui ne sert à rien ou, plus exactement, à n'importe quoi. C'est une variable associée (comme un signet) à l'objet et disponible pour le programmeur qui peut y stocker une valeur quelconque. Méthode Déjà vues : Focus, Select Evénements Déjà vus : Click, DoubleClick, GotFocus, KeyPress, LostFocus, MouseMove, MouseEnter, MouseLeave Enter Activé lorsque le contrôle reçoit le focus, bonne alternative à GotFocus Leave Activé lorsque le contrôle perd le focus, bonne alternative à LostFocus KeyDown Touche enfoncée KeyUp Touche relâchée MouseDown Bouton souris enfoncé MouseUp Bouton souris relâché MouseWheel Déplacement de la roulette Resize Déclenché lorsque le contrôle est redimensionné

L’étiquette : Label Propriétés BorderStyle Style de bordure. Valeur par défaut : None. Autres valeurs possibles : FixedSingle et Fixed3d

None Pas d’encadrement FixedSingle Encadré d’un simple trait Fixed3d Aspect 3D enfoncé

AutoSize La taille de l’étiquette est adaptée à la taille du texte. TextAlign Position du texte dans l’étiquette. Valeur par défaut : TopLeft. Autres valeurs possibles : TopCenter, TopRight, MiddleLeft, MiddleCenter, MiddleRight, BottomLeft, BottomCenter et BottomRight.

Page 92: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 24

La boîte de texte : TextBox Propriétés CharacterCasing Détermine la casse du texte. Valeur par défaut : CharacterCasing.Normal. Autres valeurs possibles : CharacterCasing.Upper (majuscule) et CharacterCasing.Lower (minuscule). Focused Cette propriété a la valeur True quand le contrôle détient le focus. Elle n’est accessible que par le code et en lecture seule. HideSelection Cette propriété définit si le contrôle conserve la marque d’une sélection lorsqu’il perd le focus. Sa valeur par défaut est True, ce qui signifie que le contrôle cache la marque de sélection. Lines et MultiLine La propriété Lines qui est un tableau de chaînes, donne accès à chacune des lignes d’une TextBox. Il est permis d’encoder plusieurs lignes de texte dans une TextBox quand sa propriété MultiLine a la valeur True.

Dim i As Integer For i = 0 To MaTextBox.Lines.Length - 1 MessageBox.Show(MaTextBox.Lines(i)) Next

MaxLength Nombre maximum de caractères autorisés pour le texte du contrôle. Par défaut : 32767. Modified Cette propriété reçoit la valeur True chaque fois que le contenu du champ est modifié. Elle est accessible par le code en lecture et écriture. PasswordChar Tout caractère encodé ou affiché est remplacé par le caractère affecté à cette propriété. Par défaut, cette propriété est vide. ReadOnly L’affectation de la valeur True à cette propriété interdit toute modification du contenu. SelectionStart et SelectionLength La propriété SelectionStart contient l’indice de départ d’une sélection et SelectionLength contient la longueur de sélection effectuée par un glissé du curseur sur le texte ou la longueur de la sélection à effectuer par le code.

MaTextBox.SelectionStart = 1 ' Commencer la sélection au deuxième caractère MaTextBox.SelectionLength = 3 ' Sélectionner 3 caractères MaTextBox.Select() ' Donne le focus et fait la sélection demandée

Page 93: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 25

TextLength Cette propriété contient le nombre de caractère contenu dans le contrôle. Méthodes Clear Vide la TextBox de son contenu. Copy, Cut et Paste Ce sont les méthodes Copier, Couper et Coller de Windows. Les méthodes Copy et Cut s’appliquent ici au texte sélectionné d’une TextBox. La méthode Paste restitue le contenu du presse-papier. Evénement TextChanged Déclenché lorsque le texte change. L’événement se produit à chaque appui de touche.

La case à cocher : CheckBox Un jeu de cases à cocher permet à l’utilisateur d’effectuer plusieurs choix parmi ceux proposés. Propriétés

Checked Cette propriété accessible en lecture et écriture a la valeur True quand la case est cochée. CheckState Cette propriété accessible en lecture et écriture peut avoir trois valeurs différentes selon que la case est cochée, cochée et grisée, et non cochée.

CheckState.Checked La case est cochée (Choix 1 de l’illustration) CheckState.Indeterminate La case est cochée et grisée (Choix 2 de l’illustration) CheckState.Unchecked La case est non cochée (Choix 4 de l’illustration)

ThreeState Par défaut, cette propriété a la valeur False. Il faut lui affecter la valeur True pour qu’une case à cocher puisse être cochée et grisée. CheckAlign Cette propriété permet de régler les positions respectives du texte et de la case au sein d’un contrôle CheckBox. Evénements CheckedChanged Cet événement se produit quand la propriété Checked change. CheckStateChanged Cet événement se produit quand la propriété CheckState change.

Page 94: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 26

Le bouton radio : RadioButton Contrairement à un jeu de cases à cocher, qui permet à l’utilisateur d’effectuer plusieurs choix parmi ceux proposés, les RadioButton quant à eux n’autorisent qu’un seul choix parmi les options proposées dans un même conteneur. Le système décoche lui-même l’option précédemment choisie lorsqu’une nouvelle est cochée. Les RadioButton n’ayant pas d’état grisé, ils ne possèdent pas les membres CheckState, ThreeState et CheckStateChanged. A part cela, les propriétés, méthodes et événements des RadioButton sont les mêmes que ceux des CheckBox. La réponse à un événement CheckedChanged peut être programmée de deux manières. Soit une procédure événementielle est écrite par RadioButton, soit une seule est écrite pour tous les RadioButton du même conteneur.

Private Sub RadioButton1_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles RadioButton1.CheckedChanged

MessageBox.Show("1") End Sub Private Sub RadioButton2_CheckedChanged(ByVal sender As Object, ByVal e As

System.EventArgs) Handles RadioButton2.CheckedChanged MessageBox.Show("2") End Sub Private Sub RadioButton3_CheckedChanged(ByVal sender As Object, ByVal e As

System.EventArgs) Handles RadioButton3.CheckedChanged MessageBox.Show("3") End Sub Private Sub RadioButton4_CheckedChanged(ByVal sender As Object, ByVal e As

System.EventArgs) Handles RadioButton4.CheckedChanged MessageBox.Show("4") End Sub

Les quatre procédures précédentes peuvent être remplacées par la suivante, qui illustre au passage la possibilité d’écrire une seule procédure pour répondre à plusieurs événements :

Private Sub RadioButton_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadioButton1.CheckedChanged,

RadioButton2.CheckedChanged, RadioButton3.CheckedChanged, RadioButton4.CheckedChanged

Select Case sender.Name.ToString Case "RadioButton1" MessageBox.Show("1") Case "RadioButton2" MessageBox.Show("2") Case "RadioButton3" MessageBox.Show("3") Case "RadioButton4" MessageBox.Show("4")

End Select End Sub

L’une comme l’autre de ces deux solutions génère deux affichages de MessageBox. Lorsqu’un RadioButton est coché, un autre est décoché automatiquement. Il s’en suit qu’une première MessageBox affichant le numéro du RadioButton décoché est suivie d’une seconde MessageBox affichant le numéro du RadioButton coché. Les RadioButton, de même que les CheckBox, peuvent prendre l'apparence d'un bouton. Il suffit d'affecter la valeur Button à leur propriété Appearance.

UnRadioButton.Appearance = Appearance.Button UnCheckBox.Appearance = Appearance.Button

Page 95: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 27

Les groupes d’objets : GroupBox et Panel Divers contrôles peuvent être regroupés au sein d’un même conteneur qui leur est spécifique. Les composants GroupBox et Panel sont des conteneurs au même titre que le formulaire. Ils ont pour avantages de permettre au programmeur de regrouper des contrôles de manière logique (pour gérer plusieurs jeux de RadioButton, par exemple) afin de les isoler du reste du formulaire et de faciliter leur placement pendant le développement (le déplacement d’un groupe déplace tout son contenu). Par ailleurs ces composants disposent de la plupart des membres déjà étudiés. Différences essentielles

GroupBox Panel

Pas de BorderStyle : encadré simple uniquement BorderStyle : None, FixedSingle, Fixed3D

Propriété Text : permet de placer un libellé sur le cadre Pas de propriété Text

Pas de propriété AutoScroll Valeur par défaut de la propriété AutoScroll : False

En cours de développement

En cours de développement

A l’exécution

A l’exécution (BorderStyle = None)

Pour ce qui concerne les jeux de RadioButton, ils sont tels que décrits aux pages précédentes. Simplement, il est possible d’en cocher un au niveau du formulaire et un dans chacun des regroupements.

L’étiquette hyperlien : LinkLabel Directement dérivée de Label, le LinkLabel en possède tous les comportements, propriétés et méthodes. Son intérêt réside dans quelques propriétés supplémentaires, dont notamment LinkArea et LinkBehaviour, qui sont spécialisées dans l'affichage d'hyperliens. L'activation d'une URL (Uniform Resource Locator) peut se faire à partir de n'importe quelle procédure, avec ou sans LinkLabel, par la commande System.Diagnostics.Process.Start("Chaîne_url"), l'URL pouvant être extraite de la propriété Text d'un LinkLabel sur base des valeurs attribuées à son LinkArea. La propriété LinkArea permet de définir le début et la fin des caractères qui constituent l'URL dans le texte de l'étiquette. La propriété LinkBehaviour permet de définir le soulignement de ce texte.

LinkLabel1.Text = "+++http://www.google.be+++" ' Le texte affiché LinkLabel1.LinkArea = New LinkArea(3, 20) ' Délimitation de l'URL LinkLabel1.LinkBehavior = LinkBehavior.NeverUnderline ' Ne pas souligner LinkLabel1.LinkVisited = True ' Indiquer si le lien a

' été visité

Page 96: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 28

La boîte de liste : ListBox Le contrôle ListBox permet l’affichage d’une liste de données présentées sous forme de chaînes de caractères, dans laquelle l’utilisateur peut effectuer une ou plusieurs sélections. Elle est aussi un excellent outil de présentation de l’information.

Propriétés MultiColumn Quand cette propriété a la valeur True, l’affichage des données s’étale sur plusieurs colonnes, sans jamais dépasser l’espace alloué verticalement. De ce fait, seule la barre de défilement horizontal reste utile dans ce mode. Il n’est pas possible de choisir de remplir une colonne plutôt qu’une autre, chaque colonne n’étant occupée que par la suite des données de la colonne précédente (à la manière des colonnes d’un journal). Integralheight La valeur par défaut de cette propriété est True. La ListBox adapte sa hauteur, même en cours de développement, à un multiple de la hauteur des caractères définie par la propriété Font. Elle reste ainsi proche de la hauteur souhaitée par le programmeur, mais aucune ligne de données ne subit un affichage partiel. Items Cette propriété est la collection contenant toutes les lignes de données de la liste. Sorted Les informations sont affichées dans leur ordre alphabétique croissant lorsque la valeur de cette propriété est True. SelectionMode Cette propriété contient le mode de sélection autorisé sur la ListBox. Sa valeur par défaut est One.

One Une seule sélection permise à la fois None Aucune sélection n’est possible sur la liste MultiSimple Plusieurs sélections désignées distinctement (Clics avec <Ctrl> enfoncée) MultiExtended Plusieurs sélections distinctes et par plages (Clics avec <Ctrl> et/ou <Shift> enfoncée(s))

SelectedIndex Indice de l’élément sélectionné entre 0 et Items.Count() - 1 SelectedIndices Cette propriété est la collection contenant les indices des lignes sélectionnées.

Page 97: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 29

Méthodes FindString Retourne l’indice de l’élément commençant par le texte recherché. SetSelected Effectue la sélection ou la désélection de la ligne désignée par son indice, selon que le second paramètre de la méthode est True ou False. GetSelected Retourne la valeur True si la ligne désignée par son indice est sélectionnée. ClearSelected Désélectionne toutes les sélections. Evénement SelectedIndexChange Cet événement est déclenché lorsque la propriété SelectedIndex change, c'est-à-dire à chaque sélection ou désélection. Quelques exemples

Dim i As Integer MaListe.Items.Clear ' Vide MaListe de tout contenu For i = 0 To 50 MaListe.Items.Add("Texte " & i) ' Ajout de 51 lignes dans MaListe Next MessageBox.Show(MaListe.Items.Count()) ' Affiche le nombre de lignes de

MaListe For i = 0 To MaListe.SelectedIndices.Count - 1 MessageBox.Show(MaListe.SelectedIndices(i)) ' Affiche les indices des lignes

sélectionnées Next i = MaListe.FindString("Texte 5") ' Stocke dans i l’indice de la

ligne Texte 5 MessageBox.Show(MaListe.Items.Item(i)) ' Affiche la ligne d’indice i MaListe.SetSelected(i, True) ' Sélectionne la ligne d’indice i If MaListe.GetSelected(MaListe.Items.Count()-1) … ' Vérifie si la dernière ligne est

sélectionnée

Page 98: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 30

La liste de cases à cocher : CheckedListBox Ce contrôle est une association des contrôles CheckBox et ListBox. Il hérite des membres de ses deux parents. Il n’accepte toutefois pas les sélections multiples.

La liste déroulante : ComboBox Le contrôle ComboBox, ou boîtes combinées, est l’association d’une ListBox et d’une TextBox. Il permet à l’utilisateur de sélectionner une valeur dans une liste ou de saisir une nouvelle valeur. Cependant, ce contrôle n’accepte pas les sélections multiples et ne déroule sa liste de données qu’à la demande. Sa propriété DropDownStyle est à épingler. Ses valeurs possibles sont :

DropDown Zone modifiable et déroulante (c'est la valeur par défaut) DropDownList Zone déroulante, mais non modifiable Simple Zone modifiable, mais non déroulante (balayage des valeurs par les flèches du clavier)

La liste de visualisation : ListView Le contrôle ListView permet l'affichage d’une liste d'éléments avec, pour chacun, son texte et, éventuellement, une icône identifiant son type ou des informations filles le concernant et présentées en colonnes. La classe ListViewItem représente un élément (avec ses informations filles éventuelles) du contrôle ListView. Les éléments de la liste peuvent être affichés de quatre manières différentes. Ils peuvent apparaître sous forme de grandes icônes (LargeImageList), de petites icônes (SmallImageList) ou de petites icônes dans une liste verticale (StateImageList). Les éléments peuvent aussi contenir des sous éléments comportant des informations relatives à l'élément parent. Le quatrième mode d'affichage, la vue Details, permet d'afficher l'élément et ses sous-éléments dans une grille munie d'en-têtes de colonne qui identifient les données affichées. L’utilisateur peut réaliser des sélections par des clics sur la colonne d’indice 0 ou n’importe où sur la ligne selon la valeur affectée à la propriété FullRowSelect. Quelque soit le mode de sélection, il est possible d’obtenir toutes les informations de la ligne, mais à l’instar des volets de l’explorateur de Windows, seule celle de la colonne d’indice 0 est directement accessible. Dans l’illustration proposée, les éléments sont contenus dans la colonne d’indice 0 et intitulée Col .1. Les autres colonnes contiennent des informations liées à l’élément de la même ligne. Propriétés FullRowSelect Quand cette propriété à la valeur True, un clic sur n’importe quelle cellule de la ligne sélectionne toute la ligne. Si la valeur est False, une sélection ne peut s’opérer qu’à partir de la colonne d’indice 0 (comme dans l’explorateur). FocusedItem Cette propriété contient des informations de la ligne sélectionnée.

FocusedItem.Index Indice de la ligne sélectionnée FocusedItem.Text Contenu de la ligne sélectionnée en colonne d’indice 0 FocusedItem.SubItems Collection des informations de la ligne FocusedItem.SubItems.Count Nombre de colonnes de la ligne FocusedItem.SubItems(n).Text Contenu de la ligne sélectionnée en colonne d’indice n

Page 99: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 31

CheckBoxes Quand cette propriété a la valeur True, les informations de la colonne d’indice 0 sont assorties de cases à cocher. CheckedIndices et SelectedIndices Ces propriétés contiennent respectivement la collection des indices des lignes cochées (si CheckBoxes vaut True) et celle des indices des lignes sélectionnées. Comme toutes les collections, elles disposent des sous propriétés Count et Item. Une ligne cochée n’est pas une ligne sélectionnée et une ligne sélectionnée n’est pas une ligne cochée. CheckedItems et SelectedItems Ces propriétés contiennent respectivement la collection des lignes cochées (si CheckBoxes vaut True) et celle des lignes sélectionnées. Comme toutes les collections, elles disposent des sous propriétés Count et Item. View C’est par l’affectation de cette propriété que se définit le mode d’affichage souhaité pour la ListView : Details, LargeImageList, SmallImageList et StateImageList. Columns En mode Details, cette propriété désigne la collection des colonnes de la liste. La largeur de chaque colonne se définit en paramètre de la méthode Add qui la crée. Il est toutefois possible de modifier la largeur d’une colonne par sa propriété Width. Il est ainsi possible d’affecter la largeur 0 à la colonne d’indice 0. Cette faculté peut être utile par exemple, pour cacher l’identifiant de l’information présentée. HeaderStyle La détermination du type d’entête de colonne s’effectue par l’affectation de cette propriété par une des valeurs suivantes :

ColumnHeaderStyle.Clickable Le clic de l’entête de colonne provoque l’événement ColumnClick ColumnHeaderStyle.Nonclickable Le clic de l’entête de colonne ne provoque pas d’événement ColumnHeaderStyle.None Pas d’entête de colonne

AllowColumnReorder Quand cette propriété a la valeur True, l’utilisateur peut déplacer les colonnes de sorte à les placer dans un ordre différent. Cette manœuvre n’affecte que l’apparence de la ListView. Les indices de colonnes ne sont pas modifiés. LabelEdit Selon sa valeur True ou False, cette propriété autorise ou interdit la modification d’un élément par l’utilisateur. Seul le contenu de la colonne d’indice 0 peut être modifié. SubItems La collection des informations d’une ligne n’est pas une propriété de ListView, mais bien celle d’un objet de type ListViewItem, comme FocusedItem par exemple. C’est par l’ajout d’éléments à cette propriété que sont créés les champs d’informations liés au parent, c'est-à-dire l’élément de la colonne d’indice 0. Sorting La valeur de cette propriété détermine la façon dont les informations peuvent être triés sur la colonne d’indice 0.

SortOrder.Ascending Tri dans l’ordre croissant SortOrder.Descending Tri dans l’ordre décroissant SortOrder.None Pas de tri

Page 100: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 32

Méthodes Il n’y a guère de méthodes spécifiques à la ListView qui n’aient déjà été vues. Par contre, quelques méthodes accessibles par des membres sont indispensables. Columns.Add L’ajout de colonnes est réalisé par cette méthode. Sa syntaxe est la suivante :

MaListView.Columns.Add("Libellé", Largeur, Alignement) Le paramètre Alignement peut prendre une des valeurs HorizontalAlignment.Center, HorizontalAlignment.Left et HorizontalAlignment.Right. Items.Add Cette méthode réalise l’ajout d’éléments, c'est-à-dire d’information en colonne d’indice 0. Elle crée les lignes. Sa syntaxe est :

MaListView.Items.Add("Information") Outre de créer la ligne et d’y placer l’information donnée, la méthode retourne un objet de type ListViewItem. C’est par la récupération de cet objet et de sa collection SubItems qu’il est ensuite possible d’ajouter les informations filles, en principe autant qu’il y a de colonnes dans la ListView. Dim UneLigne As ListViewItem ' Déclaration d’une variable de type ListViewItem UneLigne = MaListView.Items.Add("Info 1") ' Création de la ligne avec la donnée de colonne 0 UneLigne.SubItems.Add("Sous info 1 de 1") ' Crée un sous élément avec la donnée de colonne 1 UneLigne.SubItems.Add("Sous info 2 de 1") ' Crée un sous élément avec la donnée de colonne 2 Evénements ColumnClick Cet événement est déclenché par le clic de l’entête d’une colonne. SelectedIndexChanged L’événement se produit lors de chaque changement de sélection. Il est toujours suivi de l’événement Click de la ListView. Quelques exemples

MaListV.HeaderStyle = ColumnHeaderStyle.Clickable ' Type d’entête de colonne MaListV.View = View.Details ' Mode d’affichage MaListV.Clear() ' Vide la liste MaListV.Sorting = SortOrder.Ascending ' Mode de tri ' Création de 4 colonnes de largeur 100 avec intitulé : Col.0 … Col.3 For j As Integer = 0 To 3 MaListV.Columns.Add("Col." & j, 100, HorizontalAlignment.Left) Next j ' Création de 6 lignes, chacune munie de 2 sous éléments Dim UneLigne As ListViewItem For i As Integer = 0 To 5 UneLigne = MaListV.Items.Add("Info " & i) UneLigne.SubItems.Add("Sous info 1 de " & i) UneLigne.SubItems.Add("Sous info 2 de " & i) Next i

Page 101: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 33

' Affectation de la largeur 0 à la colonne d’indice 0 MaListV.Columns(0).Width = 0 ' Affiche l’indice de la ligne qui a le focus MessageBox.Show(MaListV.FocusedItem.Index) ' Affiche le texte de la colonne 0 de la ligne qui a le focus MessageBox.Show(MaListV.FocusedItem.Text) ' Affiche le nombre de cellules de la ligne qui a le focus MessageBox.Show(MaListV.FocusedItem.SubItems.Count) ' Affiche tous les textes situés en colonne d’indice 0 Dim x As ListViewItem For Each x In MaListV.Items MessageBox.Show(x.Text) Next ' Affiche le texte de chaque cellule de la ligne qui a le focus For i As Integer = 0 To MaListV.FocusedItem.SubItems.Count - 1 MessageBox.Show(MaListV.FocusedItem.SubItems(i).Text) Next i ' Affiche aussi tous les textes de la ligne qui a le focus Dim x As ListViewItem.ListViewSubItem For Each x In MaListV.FocusedItem.SubItems MessageBox.Show(x.Text) Next ' Marque la ligne qui a le focus comme étant sélectionnée MaListV.FocusedItem.Selected = True ' Affiche tous les indices de lignes cochées For Each i As Integer In MaListV.CheckedIndices MessageBox.Show(i) Next i ' Affiche tous les textes de la colonne d’indice 0 des lignes cochées For i As Integer = 0 To MaListV.CheckedIndices.Count - 1 MessageBox.Show(MaListV.CheckedItems.Item(i).Text) Next i ' Affiche tous les indices de lignes sélectionnées For Each i As Integer In MaListV.SelectedIndices MessageBox.Show(i) Next i ' Affiche tous les textes de la colonne d’indice 0 des lignes sélectionnées For i As Integer = 0 To MaListV.SelectedIndices.Count - 1 MessageBox.Show(MaListV.SelectedItems.Item(i).Text) Next i

Page 102: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 34

' Affiche l’indice de la colonne cliquée (sur l’entête) Private Sub MaListV_ColumnClick(ByVal sender As Object, ByVal e As

System.Windows.Forms.ColumnClickEventArgs) Handles MaListV.ColumnClick MessageBox.Show(e.Column) End Sub

Les vues en arborescence : TreeView Le TreeView, qui s’apparente un peu à la ListView, présente toutefois une énorme différence. Alors que dans la ListView, chaque élément peut être assorti de plusieurs informations filles sans distinction hiérarchique entre elles, dans le TreeView, chaque élément appelé node peut être assorti de Nodes fils qui lui sont subordonnés et qui sont aussi des Nodes à part entière. Le premier nœud d’une arborescence est aussi appelé racine. Propriétés Nodes Cette propriété contient la collection des Nodes. SelectedNode La propriété SelectedNode contient les informations du nœud sélectionné. ShowPlusMinus Selon sa valeur True ou False, cette propriété détermine si les boutons +/- sont affichés ou pas. Quand ils ne sont pas affichés, les développements ou réductions d’arborescences restent possibles par double-clic. ShowLines Quand cette propriété a la valeur True, soit sa valeur par défaut, l’affichage des lignes joignant les différents nœuds est effectué. ShowRootLines Quand cette propriété a la valeur True, soit sa valeur par défaut, et que ShowLines autorise l’affichage des lignes joignant les différents nœuds, la ligne joignant les nœuds racines est également affichée. Si ShowLines a la valeur True et que ShowRootLines a la valeur False, seules les lignes joignant les nœuds secondaires sont affichées. Méthodes Comme pour la ListView, les méthodes les plus utiles du TreeView se trouvent au niveau de certains de ses membres. Il faut toutefois épingler ExpandAll et CollapseAll qui sont des méthodes de TreeView, et dont les cousines sont des méthodes de TreeView.Nodes. ExpandAll Cette méthode provoque le développement de toute l’arborescence, comme si chacun des boutons + avait été cliqué. CollapseAll Cette méthode provoque la réduction de toute l’arborescence, comme si chacun des boutons - avait été cliqué.

Page 103: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 35

SelectedNode.Expand, SelectedNode.ExpandAll, SelectedNode.Collapse et SelectedNode.Toggle Ces méthodes, qui s’appliquent à un nœud sélectionné, développent ou réduisent l’arborescence à partir de la sélection.

SelectedNode.Expand Développement d’un sous niveau SelectedNode.ExpandAll Développement de tous les sous niveaux SelectedNode.Collapse Réduction de tous les sous niveaux SelectedNode.Toggle Passage à l’état suivant (développé ou réduit)

Nodes.Add Cette méthode réalise l’ajout de nœuds et retourne un objet de type TreeNode. L’exécution de Nodes.Add sur l’objet retourné crée un sous niveau de l’arborescence. Il n’est pas nécessaire de récupérer l’objet retourné par la méthode lorsqu’on crée le nœud de dernier niveau. Le code suivant crée la TreeView présentée en illustration ci-dessous.

Dim N1, N2 As TreeNode For i As Integer = 1 To 2 ' Deux racines N1 = MaTreeView.Nodes.Add("Racine " & i) For j As Integer = 1 To 2 ' Deux premiers sous niveaux N2 = N1.Nodes.Add("Noeud " & i & "." & j) For k As Integer = 1 To 3 ' Trois seconds sous niveaux N2.Nodes.Add("Sous-noeud " & i & "." & j & "." & k) Next k Next j Next i

Evénément Le seul événement retenu ici est NodeMouseClick : il donne accès toutes les propriétés d’un nœud cliqué.

Private Sub MaTreeView_NodeMouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeNodeMouseClickEventArgs)

Handles MaTreeView.NodeMouseClick MessageBox.Show(e.Node.Text) ' Donne le texte du nœud cliqué End Sub

Lecture d’un TreeView La procédure récursive suivante réalise la lecture de tous les nœuds enfants, à partir d’un nœud d’entrée donné, et copie leur donnée dans une ListBox nommée LBNodes.

Private Sub AfficheNodes(ByVal Entree As TreeNode) LBNodes.Items.Add(Entree.Text) ' Copie des données dans une ListBox If Entree.GetNodeCount(True) > 0 Then For Each N As TreeNode In Entree.Nodes AfficheNodes(N) Next End If End Sub

Pour faire une lecture complète du TreeView, il faut appeler la procédure pour chaque racine. Le remplissage de la ListBox illustré ci-contre, avec toutes les données du TreeView, est le résultat de l’exécution des deux lignes suivantes :

AfficheNodes(MaTreeView.Nodes(0)) AfficheNodes(MaTreeView.Nodes(1))

Page 104: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 36

Le calendrier mensuel : MonthCalendar Le contrôle MonthCalendar est un calendrier présentant par défaut, un mois à la fois et permettant le passage d’un mois à l’autre par deux boutons de navigation. Sa propriété CalendarDimensions permet la définition d’un nombre de mois présentés en ligne, en colonne ou en rectangle selon les valeurs affectées aux sous propriétés Height et Width. Ces propriétés doivent être réglées pendant le développement dans la fenêtre des propriétés. Par exemple, affecter respectivement 2 et 3 à ces propriétés fait que le calendrier présente ensuite 6 mois à la fois, en 2 lignes de 3. La propriété MaxSelectionCount définit le nombre de jours pouvant être sélectionnés en une fois. Quand une sélection est réalisée, les propriétés SelectionStart, SelectionEnd et SelectionRange founissent la ou les date(s) sélectionnée(s) en une plage contiguë.

' Affiche la date sélectionnée ou la première date d’une plage sélectionnée MessageBox.Show(MonCalendrier.SelectionStart) ' Affiche la date sélectionnée ou la dernière date d’une plage sélectionnée MessageBox.Show(MonCalendrier.SelectionEnd) ' Affiche la première et la dernière date d’une plage sélectionnée MessageBox.Show(MonCalendrier.SelectionRange.ToString)

Le sélectionneur de date : DateTimePicker Ce contrôle, qui est le regroupement des contrôles ComboBox et MonthCalendar, a l’avantage de montrer le calendrier à la demande. Il n’est affiché qu’en réponse au clic sur le bouton de la ComboBox et disparaît après sélection. Seule la ComboBox reste visible. La date sélectionnée est automatiquement affichée dans la zone de texte du ComBoBox. Sa valeur est accessible à partir du code par la propriété Text du contrôle.

Page 105: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 37

La boîte à image : PictureBox

Le composant PictureBox est l’outil de présentation d’images. Il a comme propriétés particulières celle qui définit le mode d’affichage de l’image et celle qui désigne l’image à afficher. Cette dernière va de paire avec une méthode de chargement du fichier de l’image. SizeMode C’est la propriété qui définit le mode d’affichage par une des quatre valeurs possibles : Normal, CenterImage, StretchImage et AutoSize. L’illustration montre l’effet de chacune de ces valeurs.

Normal L’image est dessinée à partir du coin supérieur gauche de l’objet. Si elle est trop grande, seule une partie est affichée

CenterImage L’image est dessinée à partir de son centre positionné au centre de l’objet. Si elle est trop grande, seule une partie est affichée

StretchImage L’image est réduite ou agrandie pour occuper la surface de l’objet. AutoSize L’image est dessinée à partir du coin supérieur gauche de l’objet qui est agrandi pour

contenir toute l’image, mais sans toutefois dépasser les bords du formulaire qui le contient. Si l’image est malgré tout trop grande, seule une partie est affichée

Image C’est le membre qui fournit à la fois la propriété Image et la méthode Image.FromFile qui permet son affectation.

MonImage.Image = System.Drawing.Image.FromFile("F:\MesImages\ArbresEnNeigés.jpg")

Page 106: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 38

Le diviseur : Splitter Le contrôle Splitter permet de créer des barres de séparation redimensionnables pour distribuer l’espace du formulaire entre différents contrôles. Le Splitter est particulièrement utilisé dans les interfaces de type explorateur Windows où la séparation verticale entre les deux volets d’affichage est un Splitter. Outre l’exagération présentée par l’illustration ci contre où le formulaire est divisé en cinq parties, voici un exemple plus plausible. Le but est de présenter le chemin et le nom de chaque fichier d’image dans une ListBox, et de présenter l’image sélectionnée dans la PictureBox voisine. Une réalisation Placer la ListBox sur le formulaire et fixer sa propriété Dock à Left. Placer le Splitter et ajuster sa largeur comme souhaité. Sa propriété Dock a la valeur Left par défaut. Placer une PictureBox sur la partie restante du formulaire et régler sa propriété Dock à Fill de sorte qu’elle occupe tout l’espace. Son code

Private Sub FBase_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

MaListe.Items.Clear() MaListe.Items.Add("X:\VBNet\ArbresEnneiges.jpg") MaListe.Items.Add("X:\VBNet\Chateau.jpg") MonImage.SizeMode = PictureBoxSizeMode.StretchImage End Sub Private Sub MaListe_SelectedIndexChanged(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles MaListe.SelectedIndexChanged MonImage.Image = System.Drawing.Image.FromFile(MaListe.SelectedItem) End Sub

Son résultat

Page 107: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 39

Le contrôle à onglets : TabControl

Un TabControl contient des pages d'onglets qui sont représentées par des objets TabPage ajoutés à la collection TabPages.

L’objet TabControl se présente d’emblée muni de deux onglets TabPage1 et TabPage2. Le moyen le plus simple d’ajouter des pages, ou d’en retirer, est d’utiliser l’éditeur de collections de TabPage accessible par le bouton situé dans la fenêtre des propriétés.

L’éditeur de collections de TabPage présente une page de propriétés pour chaque page ajoutée. C’est l’occasion d’affecter la propriété Text de chacune et de les nommer de façon pertinente. Une fois les pages créées, elles sont disponibles pour le programmeur qui peut y déposer les composants visuels qu’il souhaite. Les onglets sont indicés à partir de 0 et sont accessibles par la propriété Item de la collection TabPages ou par leur propriété Name. La propriété SelectedTab désigne l’onglet d’avant plan.

MesTab.TabPages.Item(0).Text = "MonTexte" Private Sub MesTab_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles MesTab.Click MessageBox.Show(MesTab.SelectedTab.Text) ' Affiche le libellé de l‘onglet End Sub ' cliqué

Les objets TabPage n’ont pas de propriété Visible. Il n’est donc pas possible de les rendre visible ou invisible à la demande aussi simplement que cela se fait avec d’autres composants. Comme ils appartiennent à une collection, ils peuvent être retirés ou insérés dynamiquement. Toutefois, pour qu’une insertion d’un TabPage restitue celui qui a été retiré auparavant, il faut que le nom de ce dernier soit conservé ou que sa référence soit mémorisée.

Dim Page As TabPage ' Pour mémoriser la référence du ' … ' TabPage à manipuler Page = MesTab.TabPages.Item(i) ' Mémorisation du TabPage d’indice i MesTab.TabPages.RemoveAt(i) ' Suppression du TabPage d’indice i ' … MesTab.TabPages.Insert(i, Page) ' Remise en place du TabPage supprimé ' dont la référence a été mémorisée

AutresTab.TabPages.Remove(NomDuTabPage) ' La suppression d’un TabPage nommé ' … AutresTab.TabPages.Insert(i, NomDuTabPage) ' Remise en place du TabPage supprimé ' dont le nom a été conservé

Page 108: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 40

Le menu : MainMenu et MenuStrip Le MainMenu, ou MenuStrip (depuis VB.Net 2005), se situe à mi-chemin entre les composants visibles et ceux qui ne le sont pas. En effet, tous les contrôles abordés jusqu’à présent se dessinent concrètement sur le formulaire. Le MainMenu est un concepteur dont seuls les effets sur la fenêtre sont visibles. D’autres composants qui sont expliqués plus loin, ne sont visibles qu’en temps utile, ou même jamais. Comme illustré ci contre, l’interface de développement est quelque peu adaptée lorsque de tels composants sont utilisés. Sur cette copie d’écran, les composants peu ou pas visibles apparaissent en dessous du formulaire. Il s’agit du MainMenu dont l’effet est visible sur le formulaire, du Timer qui n’est jamais visible, et de l’OpenFileDialog qui n’est visible qu’à la demande. Dans la mesure où l’objet de cette page est de placer un menu sur un formulaire, MainMenu et MenuStrip se valent. Toutefois le MenuStrip offre des possibilités supplémentaires qui ne sont pas étudiées ici. Alors que le MainMenu est un conteneur de MenuItem, le MenuStrip peut aussi contenir des ToolStripMenuItem (équivalent au MenuItem), ToolStripComboBox, ToolStripSeparator et ToolStripTextBox. Le MenuStrip remplace le MainMenu dans l’évolution des composants. Depuis VB.Net 2005, le MainMenu peut être ajouté à la liste des composants visuels par un clic du bouton droit de la souris sur la boîte à outils, puis Choisir les éléments …, puis Composants .Net Framework.

Quand le composant Menu a été déposé sur le formulaire, il est automatiquement déplacé dans la zone des composants invisibles et les zones de dactylographie des différents points des menus sont présentées sur le formulaire.

Les différents menus se dessinent assez simplement au fur et à mesure que le programmeur encode ses libellés. Un clic du bouton droit de la souris présente un menu contextuel dont certaines propositions peuvent être utiles à la mise au point du menu et à l’insertion des composants d’un MenuStrip. Pour que le menu soit affiché lors de l’exécution, il faut affecter la propriété Menu du formulaire avec le nom du menu. Il est ainsi possible de choisir un menu parmi d’autres selon les nécessités du moment. Me.Menu = MonMenuPrincipal Chaque menu et sous menu a sa propriété Name à laquelle il convient de donner un nom aussi représentatif que possible, contrairement à ce qui est illustré ci-dessous. En effet, c’est en réponse à l’événement Click qu’est programmé le code événementiel et il n’est pas commode de s’y retrouver lorsque, comme ci-dessous, les menus portent les noms MenuItem1, MenuItem2, …, MenuItem…

Différentes propriétés, dont notamment Enabled, Visible, Shortcut, Checkedpermettent d’influencer l’apparence et le comportement des sous menus Des barres de séparations peuvent être placées par le menu contextuel ou bien en encodant un tiret en lieu et place du texte. Il faut encore savoir que précéder une lettre du texte du caractère &, fait de cette lettre un raccourci clavier par l’usage de la touche <Alt>. Cela est vrai pour d’autres contrôles, tels que le bouton par exemple.

Page 109: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 41

La minuterie : Timer Le composant Timer est toujours invisible. C’est idéalement dans le code qu’il est réglé et c’est dans le code qu’est exploité son effet, c'est-à-dire le déclenchement d’un événement à un intervalle prédéfini. Propriétés Enabled Cette propriété permet le fonctionnement du Timer quand elle a la valeur True et en provoque l’arrêt immédiat quand elle reçoit la valeur False. Interval C’est par cette propriété que se définit l’intervalle de déclenchement de l’événement. Sa valeur exprime un nombre de millièmes de seconde compris entre 1 et 2147483647, soit jusqu’à plus de 595 heures. L’affectation de la valeur 0 à cette propriété provoque l’arrêt du Timer aussitôt son intervalle en cours terminé. L’arrêt du Timer par ce procédé n’est pas instantané. Méthodes Start La méthode Start démarre le Timer. Son usage équivaut à donner la valeur True à la propriété Enabled. Stop La méthode Stop arrête le Timer. Son usage équivaut à donner la valeur False à la propriété Enabled. Evénement Tick Cet événement est le seul produit par le Timer. Il survient à chaque intervalle de temps écoulé, selon la valeur de la propriété Interval. Exemple

UnTimer.Interval = 1000 ' Intervalle d’une seconde UnTimer.Start() ' Code de démarrage UnTimer.Stop() ' Code d’arrêt Private Sub UnTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs)

Handles UnTimer.Tick Static T As Integer T += 1 MaTextBox.Text = T ' Affichage de la valeur de T chaque fois End Sub ' que Tick se produit

Page 110: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 42

Les boîtes de dialogue Les boîtes de dialogue font partie de ces composants visibles à la demande. Elles sont rendues visibles par programmation, à l’instar des MessageBox, quand leur usage est requis, le plus souvent par l’invocation la méthode ShowDialog. Cette méthode renvoie une valeur de type DialogResult qui indique le mode de fermeture de la DialogBox (<OK>, <Escape>, …).

L’ouverture de fichiers : OpenFileDialog La boîte dialogue OpenFileDialog est celle que présente Windows lorsque l’utilisateur ouvre le menu Fichier et choisi l’option Ouvrir … dans une application telle que Word ou Excel, ou encore dans l’environnement de développement VB.Net. Elle sert à l’acquisition du nom d’un fichier avec son chemin complet en vue de son ouverture. Propriétés InitialDirectory Contient le dossier par défaut pour la première ouverture de la boîte de dialogue. Pour les ouvertures suivantes, le dossier présenté est le dernier auquel l’OpenFileDialog a accédé. Title Contient le texte de la barre de titre de la boîte de dialogue. Filter Cette propriété définit le contenu de la ComboBox Type de fichiers. Le filtre est une chaîne de caractères regroupant tous les textes concaténés. Ces textes sont séparés les uns des autres par le caractère pipe (|), et chacun est constitué de la partie affichable séparée du filtre proprement dit par un pipe également.

Filter = "Tous|*.*|Texte|*.TXT|Excel|*.XLS|Word|*.DOC" DefaultExt Contient le texte de l’extension à placer lorsque l’utilisateur néglige de l’encoder. AddExtension Quand cette propriété a la valeur True, l’extension définie dans DefaultExt est ajoutée dans tous les cas. Il faut user de cette faculté avec prudence sous peine de retrouver les fichiers munis de deux extensions (MonDoc.Txt.TXT).

Page 111: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 43

MultiSelect Cette propriété détermine par sa valeur True ou False, s’il est permis de pratiquer la sélection de plusieurs fichiers dans la boîte de dialogue. CheckFileExists et CheckPathExists Lorsque ces propriétés ont la valeur True, qui est leur valeur par défaut, une vérification de l’existence du fichier et de son chemin est réalisée. FileName et FileNames Cette propriété retourne le nom et le chemin du fichier sélectionné. Quand la propriété MultiSelect a la valeur True et qu’une sélection multiple est pratiquée, FileNames contient la collection des noms et chemins des fichiers sélectionnés. Méthode ShowDialog C’est la méthode ShowDialog qui provoque l’affichage de la fenêtre. Exemple

Private Sub BFichier_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BFichier.Click

Dim Resultat As DialogResult ' Valeur renvoyée par la méthode Showdialog MonOpenFileDial.Title = "Test de l'OpenFileDialog" MonOpenFileDial.Filter = "Tous|*.*|Texte|*.TXT|Excel|*.XLS|Word|*.DOC" MonOpenFileDial.InitialDirectory = "X:\VbNet"

Resultat = MonOpenFileDial.ShowDialog() If Resultat = Windows.Forms.DialogResult.OK Then ' OK est le gage d’une saisie MessageBox.Show(MonOpenFileDial.FileName) ' correcte, sans annulation End If

End Sub

L’enregistrement de fichiers : SaveFileDialog La boîte dialogue SaveFileDialog est celle que présente Windows lorsque l’utilisateur ouvre le menu Fichier et choisit l’option Enregistrer sous … dans une application. Elle sert à l’acquisition du nom d’un fichier avec son chemin complet en vue de son enregistrement. Elle très semblable à l’OpenFileDialog. La principale différence est qu’elle ne dispose pas de la propriété MultiSelect et donc, ne dispose pas non plus de la collection des fichiers sélectionnés FileNames.

L’explorateur de dossiers : FolderBrowserDialog Le FolderBrowserDialog permet la sélection d’un dossier et sa création si nécessaire. Il est plus simple à mettre en œuvre que les FileDialog lorsqu’il n’y a pas de nom de fichier à rechercher, mais seulement un chemin. Le bouton Nouveau dossier est affiché ou pas selon la valeur de la propriété ShowNewFolderButton qui peut être True ou False.

Private Sub BFichier_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles BFichier.Click MonFolderBrowserDial.SelectedPath = "X:\VBNet" MonFolderBrowserDial.ShowDialog() MessageBox.Show(MonFolderBrowserDial.SelectedPath) End Sub

Page 112: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 44

Les polices de caractères : FontDialog La FontDialog est bien entendu l’interface de sélection et de configuration d’une police de caractère. Quelques propriétés permettent de régler son contenu avant de la proposer à l’utilisateur. ShowEffects et ShowColor La propriété ShowEffects, dont la valeur par défaut est True, autorise l’affichage de l’encadré Effets. Quand ce jeu de cases à cocher est visible, il est possible de proposer aussi un choix de couleur par la ComboBox Couleur. Pour cela, il faut affecter la valeur True à la propriété ShowColor qui est False par défaut. MinSize et MaxSize Ces propriétés définissent les limites des tailles de caractères proposées dans la ListBox Taille. La valeur minimale permise est 8 et la valeur maximale est 72. Ces limites n’empêchent toutefois pas l’utilisateur d’encoder une valeur choisie hors de la plage proposée. Usage des propriétés Font, Color et Font.Size.

MaPolice.ShowDialog() ' Affichage de la boîte de dialogue MessageBox.Show(MaPolice.Font.Size) ' Affichage de la taille choisie MaTextBox.Font = MaPolice.Font ' Affectation de la police à une TextBox MaTextBox.ForeColor = MaPolice.Color ' Affectation de couleur du texte de ' la TextBox

Les couleurs : ColorDialog La propriété FullOpen, qui a la valeur False par défaut, limite le choix aux couleurs de base. Quand elle a la valeur True, l’utilisateur peut obtenir la palette de personnalisation. La propriété SolidColorOnly limite les possibilités de choix aux couleurs supportées par la carte graphique quand sa valeur est True. Sa valeur par défaut est False. La propriété la plus importante est Color. Elle permet de proposer une couleur par défaut et surtout, elle retourne la couleur sélectionnée par l’utilisateur.

MaCouleur.Color = System.Drawing.Color.Green ' Affectation d’une couleur par ' défaut (Vert) MaCouleur.AllowFullOpen = True ' Autorise la personnalisation ' d’une couleur MaCouleur.ShowDialog() MaTextBox.ForeColor = MaCouleur.Color ' Affectation de couleur du texte ' de la TextBox

Page 113: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 45

L’impression des documents Les composants permettant le lancement d’une impression sont PrintDocument et PrintPreviewDialog. Dans la mesure où il est souhaitable de permettre à l’utilisateur de régler les marges de son document et de sélectionner une imprimante, les composants PageSetupDialog et PrintDialog sont également nécessaires. Le composant PrintPreviewControl permet seulement la visualisation du document désigné par PrintDocument et c’est au programmeur de prévoir la commande d’impression. Les composants PrintDocument et PrintPreviewDialog provoquent l’événement PrintPage en réponse duquel il faut programmer le détail de l’impression. C’est notamment dans cette procédure événementielle qu’est réalisée la lecture du fichier contenant le document, à moins qu’il s’agisse d’impression directe. Pour pouvoir utiliser les outils de lecture de fichier, l’Imports System.IO est indispensable et le fichier, qui doit être utilisé par plusieurs procédures, doit être déclaré au niveau de la classe. Dans cette situation, les premières lignes de codes de la classe sont : Imports System.IO Public Class FBase Dim MonDoc As StreamReader Le moteur d’impression : PrintDocument Le composant PrintDocument est toujours invisible à l’exécution. L’impression directe d’un document, sans possibilité pour l’utilisateur de sélectionner l’imprimante, ni de modifier les marges, se commande par le code suivant : PrintDocument1.Print() L’exécution de la méthode Print provoque l’événement PrintPage et l’impression peut être réalisée comme ceci :

Private Sub PrintDocument1_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage

Dim Ligne As String Dim Position As System.Drawing.PointF ' PointF et RectangleF sont les position et Dim Rectangle As RectangleF ' dimensions de la zone d’impression Dim Police As Font ' exprimées en nombres réels (Floating point) Dim HauteurLigne As Single Ligne = "Voici la ligne à imprimer" ' Une première ligne à imprimer Police = New Font("Courier New", 10, FontStyle.Bold) Position = New PointF(e.MarginBounds.Left, e.MarginBounds.Top) HauteurLigne = Police.GetHeight(e.Graphics) Rectangle.Location = Position Rectangle.Width = e.MarginBounds.Width ' Récupérer la largeur entre marges Rectangle.Height = HauteurLigne ' et utiliser la hauteur de ligne e.Graphics.DrawString(Ligne, Police, Brushes.Black, Rectangle) Ligne = "et en voici une autre" ' une autre … Police = New Font("Times New Roman", 16, FontStyle.Bold) Position = New PointF(e.MarginBounds.Left, e.MarginBounds.Top + HauteurLigne) HauteurLigne = Police.GetHeight(e.Graphics) Rectangle.Location = Position Rectangle.Width = e.MarginBounds.Width Rectangle.Height = HauteurLigne e.Graphics.DrawString(Ligne, Police, Brushes.Black, Rectangle) ' … tant qu’il y a des lignes … ' encore une … e.HasMorePages = False End Sub

Page 114: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 46

L’impression d’un fichier texte, sans possibilité pour l’utilisateur de sélectionner l’imprimante, ni de modifier les marges, se commande par le code suivant : PrintDocument1.DocumentName = "X:\TXT\MonTexte.Txt" MonDoc = New StreamReader(PrintDocument1.DocumentName) PrintDocument1.Print() MonDoc.Close() L’exécution de la méthode Print provoque l’événement PrintPage et l’impression peut être réalisée, mais cette fois les lignes à imprimer sont fournies par la lecture du fichier. La procédure PrintDocument1_PrintPage devient par exemple :

Private Sub PrintDocument1_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage

Dim Ligne As String Dim Position As System.Drawing.PointF Dim Rectangle As RectangleF Dim Police As Font Dim HauteurLigne As Single Dim NbrLignesParPage As Integer Dim NumLigne As Integer = 0 ' Comptage des lignes imprimées

' Dans cet exemple, toutes les lignes ' utilisent la même police

Police = New Font("Courier New", 10, FontStyle.Bold) HauteurLigne = Police.GetHeight(e.Graphics)

' Nombre de lignes par page est la ' hauteur de la zone imprimable ' divisée par la hauteur d'une ligne

NbrLignesParPage = e.MarginBounds.Height / HauteurLigne Do Until MonDoc.Peek = -1 ' Lire le contenu du fichier ligne par Ligne = MonDoc.ReadLine() ' ligne et calculer la position de la ' ligne à imprimer Position = New PointF(e.MarginBounds.Left, e.MarginBounds.Top +

(NumLigne * HauteurLigne))

' Définir les coordonnées de la zone ' dans laquelle la ligne sera imprimée ' avec la position calculée et sa

Rectangle.Location = Position ' taille Rectangle.Width = e.MarginBounds.Width ' Récupérer la largeur entre marges Rectangle.Height = HauteurLigne ' et utiliser la hauteur de ligne e.Graphics.DrawString(Ligne, Police, Brushes.Black, Rectangle) NumLigne += 1 ' Incrémenter le compteur de lignes If NumLigne > NbrLignesParPage Then Exit Do ' et vérifier si on est en fin de page Loop If MonDoc.Peek <> -1 Then ' Relancer l'impression de la page e.HasMorePages = True ' suivante Else MonDoc.Close() MonDoc = New StreamReader(TDocument.Text) ' En prévisualisation, cette procédure End If ' est exécutée une première fois pour End Sub ' l’affichage et il faut rouvrir le

' fichier pour l’impression éventuelle Pour donner à l’utilisateur la possibilité de sélectionner l’imprimante et de modifier les marges, il faut associer les composants PrintDialog et PageSetupDialog à PrintDocument et permettre à l’utilisateur de les utiliser pour définir ses options. Il faut noter que PageSetupDialog permet aussi la désignation de l’imprimante. Ces associations se programment par ces lignes : PageSetupDialog1.Document = PrintDocument1 PrintDialog1.Document = PrintDocument1

Page 115: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 47

La mise en page : PageSetupDialog Une fois un PrintDocument et une PageSetupDialog associés, l’activation de la boîte de dialogue se programme par : PageSetupDialog1.ShowDialog() Les propriétés AllowMargins, AllowOrientation, AllowPaper et AllowPrinter dont la valeur par défaut est True peuvent recevoir la valeur False pour interdire l’accès aux fonctionnalités correspondantes. La sélection de l’imprimante : PrintDialog Une fois un PrintDocument et une PrintDialog associés, l’activation de la boîte de dialogue se programme par : PrintDialog1.ShowDialog() Les propriétés AllowSomePages, AllowSelection et AllowPrintToFile peuvent aussi recevoir une des valeurs True ou False selon le degré de liberté accordé à l’utilisateur. La fenêtre de prévisualisation : PrintPreviewDialog La boîte de dialogue de prévisualisation affiche le document tel qu’il sera imprimé et permet à l’utilisateur d’en commander l’impression. Le composant PrintPreviewDialog doit être associé à un PrintDocument avant d’être activé. Il est bien entendu que le PrintDocument doit être réglé au préalable. Le code suivant réalise cette association et active la boîte de dialogue. PrintPreviewDialog1.Document = PrintDocument1 MonDoc = New StreamReader(PrintDocument1.DocumentName) PrintPreviewDialog1.ShowDialog() MonDoc.Close() Le composant de prévisualisation : PrintPreviewControl Le composant de prévisualisation affiche le document tel qu’il sera imprimé, mais ne permet pas à l’utilisateur d’en commander l’impression. Le composant PrintPreviewControl doit être associé à un PrintDocument avant d’être activé. Il est bien entendu que le PrintDocument doit être réglé au préalable. Le code suivant réalise cette association et active l’affichage du document dans le composant. MonDoc = New StreamReader(PrintDocument1.DocumentName) PrintPreviewControl1.Document = PrintDocument1 PrintPreviewControl1.InvalidatePreview() Exemple récapitulatif Tous les composants étudiés ci avant sont regroupés dans une même application permettant l’expérimentation de chacun dans une situation de traitement d’un fichier texte en autorisant la sélection de l’imprimante et la modification des marges par l’utilisateur. Les codes communs de réglage des composants ont été réunis dans une procédure InitialiseComposants qui est appelée par chaque procédure événementielle de réponse au clic d’un bouton. La boîte de texte TDocument contient le chemin et le nom du fichier à traiter.

Page 116: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 48

Imports System.IO Public Class FBase Dim MonDoc As StreamReader Private Sub InitialiseComposants(ByVal Test As Byte) Select Case Test Case 1 PageSetupDialog1.Document = PrintDocument1 ' Association pour PageSetupDialog Case 2 PrintDialog1.Document = PrintDocument1 ' Association pour PrintDialog Case 3 PrintPreviewDialog1.Document = PrintDocument1 ' Association pour PrintPreviewDialog Case 4 PrintPreviewControl1.Document = PrintDocument1 ' Association pour PrintPreviewControl Case 0 ' Rien à faire ici. Le paramètre 0 est prévu pour entrer dans la procédure, même s’il n’y ' a aucune association à régler (comme dans BPrintDocument_Click). Les 2 lignes écrites ' après End Select doivent toujours être exécutées. End Select PrintDocument1.DocumentName = TDocument.Text MonDoc = New StreamReader(PrintDocument1.DocumentName) End Sub Private Sub BPageSetup_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BPageSetup.Click InitialiseComposants(1) PageSetupDialog1.ShowDialog() End Sub Private Sub BPrintDial_Click(ByVal sender As System.Object, ByVal e As

system.EventArgs) Handles BPrintDial.Click InitialiseComposants(2) PrintDialog1.ShowDialog() End Sub Private Sub BPrintPreviewDial_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles BPrintPreviewDial.Click InitialiseComposants(3) PrintPreviewDialog1.ShowDialog() MonDoc.Close() End Sub Private Sub BPrintPreviewControl_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles BPrintPreviewControl.Click InitialiseComposants(4) PrintPreviewControl1.InvalidatePreview() ' Force la mise à jour du contrôle End Sub Private Sub BPrintDocument_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles BPrintDocument.Click InitialiseComposants(0) PrintDocument1.Print() MonDoc.Close() End Sub Private Sub PrintDocument1_PrintPage(ByVal sender As Object, ByVal e As

System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage ' Replacer ici la procédure PrintDocument1_PrintPage déjà écrite pour l’impression d’un fichier ' texte End Sub End Class

Page 117: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 49

Programmation multi formulaires L’ouverture de formulaires secondaires Une application Windows dispose d’un formulaire de démarrage, qui est de ce fait son formulaire principal, et toute fenêtre doit être instanciée et affichée à la demande. Soit une application qui contient deux formulaires, FBase et FTest. Le formulaire de démarrage est FBase. Il s’affiche automatiquement dès l’application chargée. Il est muni d’un bouton Fenêtre secondaire destiné à ouvrir FTest. Ce dernier est muni d’un bouton Fin pour le retour à FBase. Voici le code d’ouverture de FTest dans FBase :

Private Sub BOuvreFTest_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BOuvreFTest.Click

Dim FSecondaire As New FTest FSecondaire.Show() End Sub

Le code de fermeture de FTest est le suivant :

Private Sub BFin_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BFin.Click

Me.Close() End Sub

Dans cette situation multi formulaires, l’usage de la méthode Me.Close n’équivaut pas à utiliser l’instruction End. Cette dernière provoque l’arrêt de toute l’application et ne doit être utilisée qu’à bon escient. Dans cet exemple, il n’est pas question de tout arrêter, mais seulement de fermer FTest et de continuer les traitements avec FBase.

Le passage de données entre formulaires La communication d’informations entre deux fenêtres est chose courante. Les techniques qui permettent cette communication sont multiples. Afin d’en illustrer quelques unes, voici une hypothétique application de gestion d’un signalétique complexe de personnes comportant notamment les quatre formulaires suivants : FBase, dont le titre est Gestion des personnes, est une fenêtre de sélection d’une personne. C’est par elle aussi que se commande l’encodage d’une nouvelle personne. FAffiche, dont le titre est Affichage, est la fenêtre de consultation de l’ensemble des informations concernant la personne sélectionnée dans FBase. Dans cet exemple, la seule information affichable est le nom de la personne. FVerifie, dont le titre est Vérification – correction, est la fenêtre de modification des informations concernant la personne sélectionnée dans FBase. Dans cet exemple, la seule information modifiable est le nom de la personne. FEncode, dont le titre est Encodage, est la fenêtre d’encodage des informations concernant une nouvelle personne. Dans cet exemple, la seule information à encoder est le nom de la personne.

Cette écriture de BOuvreFTest permet l’instanciation simultanée de plusieurs fenêtres FTest, chacune ayant ses éventuelles données réinitialisées. Les 2 lignes de codes de cette procédure peuvent être remplacées par FTest.Show(). Dans ce cas, FTest est unique et conserve ses éventuelles données d’un appel à l’autre.

Page 118: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 50

L’affichage du signalétique complet pour consultation se commande par le bouton Affichage de FBase. L’affichage du signalétique complet pour édition se commande par le bouton Vérification. Dans un cas comme dans l’autre, il faut qu’une personne soit sélectionnée dans la ListBox MesPersonnes. Voici le code de FBase qui provoque l’ouverture des fenêtres concernées en leur passant l’information nécessaire, à savoir le nom de la personne dans cet exemple. Private Selection As Integer ' Seule variable de la classe FBase, pour

mémoriser l'indice d'une sélection Une procédure privée a été créée pour l’affichage d’un message en cas de tentative d’affichage ou d’édition alors qu’il n’y a pas de sélection. Cette procédure peut donc être appelée par BAffiche_Click et par BVerification_Click. Le libellé du bouton concerné lui est passé en paramètre de sorte à rendre le message plus précis. Private Sub MessageErreur(ByVal TexteDuBouton As String) MessageBox.Show("Une personne doit être sélectionnée pour utiliser " & TexteDuBouton) End Sub Pour l’affichage, la méthode choisie pour la communication de l’information à FAffiche consiste à profiter de la portée par défaut de tout objet déposé sur un formulaire, qui est Friend, pour affecter directement la TextBox d’affichage TUnePers. Private Sub BAffiche_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles BAffiche.Click If MesPersonnes.SelectedItems.Count = 1 Then ' Seulement si une sélection existe Dim Fs As New FAffiche Fs.TUnePers.Text = MesPersonnes.SelectedItem ' Affecter la sélection à la TextBox Fs.Owner = Me ' Passer le nom du Form appelant Fs.ShowDialog() Else MessageErreur(BAffiche.Text) ' Passer le libellé du bouton au message End If End Sub Pour l’édition, la méthode choisie pour la communication de l’information à FVerifie consiste à utiliser une propriété Entree créée à cette fin dans cette classe. Cette façon de faire est conforme au contrôle d’accès préconisé en POO : on accède aux propriétés d’une classe par les méthodes qu’elle expose pour cela. Private Sub BVerification_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles BVerification.Click If MesPersonnes.SelectedItems.Count = 1 Then Dim Fs As New FVerifie Selection = MesPersonnes.SelectedIndex ' Mémoriser l'indice tant qu’on a le focus Fs.Entree = MesPersonnes.SelectedItem ' Affecter à une propriété du form Fs.Owner = Me ' Passer le nom du Form appelant Fs.ShowDialog() Else MessageErreur(BVerification.Text) ' Passer le libellé du bouton au message End If End Sub Dans cette application, le nom du formulaire appelant doit être passé aux formulaires instanciés. C’est la propriété Owner, au nom symbolique, qui est utilisée à cet effet mais la propriété Tag aurait tout aussi bien fait l’affaire. Quand un formulaire est la propriété d'un autre, il est réduit et fermé en même temps que son formulaire propriétaire. Par exemple, si Form2 appartient à Form1, la fermeture ou la réduction de Form1 entraîne celle de Form2. En outre, un formulaire détenu par un autre formulaire n'est jamais affiché derrière son propriétaire. Si ces caractéristiques de la relation de propriété ne conviennent pas, il est toujours possible de créer une propriété dans le formulaire secondaire pour lui transmettre le nom de l’appelant. Il suffit d’affecter ensuite cette propriété de la même façon que n’importe quelle autre valeur.

Page 119: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 51

Voici le code complet de FAffiche : Public Class FAffiche Dim Fp As FBase ' Référence à un Form de type FBase Private Sub FAffiche_Activated(ByVal sender As Object, ByVal e As System.EventArgs)

Handles MyBase.Activated Fp = Me.Owner ' Désignation du formulaire appelant à Fp BOK.Select() End Sub Private Sub BOK_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BOK.Click Fp.LDernierAffiche.Text = Me.TUnePers.Text ' Accès direct à un objet de FBase Me.Close() End Sub End Class L’application conserve dans l’étiquette LDernierAffiche le dernier affichage réalisé par FAffiche. Pour l’exemple, c’est de la classe FAffiche qu’est affecté ce Label. La méthode choisie pour la communication de l’information à FBase est la même que celle utilisée par FBase pour transmettre le texte à FAffiche, c'est-à-dire l’affectation directe d’un objet du formulaire destinataire. Voici maintenant le code complet de FVerifie : Public Class FVerifie Private Fp As FBase ' Référence à un Form de type FBase Private Modifiee As Boolean ' A passer à MiseAJour de Fp Private Sub FVerifie_Load(ByVal sender As Object, ByVal e As System.EventArgs)

Handles MyBase.Load Fp = Me.Owner ' Désignation du formulaire appelant à Fp Modifiee = False End Sub Private Sub TUnePers_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs)

Handles TUnePers.TextChanged Modifiee = True ' L’événement TextChanged indique End Sub ' s’il y a modification Private Sub BCorrecte_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles BCorrecte.Click If Me.TUnePers.Text <> String.Empty Then ' Ne pas envoyer une chaîne vide Fp.MiseAJour(Modifiee, TUnePers.Text) ' Accès à une méthode Friend de Fp End If Me.Close() End Sub Private Sub BIncorrecte_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles BIncorrecte.Click Fp.Supprime() ' Accès à une autre méthode Friend de Fp Me.Close() End Sub Private Sub BAnnule_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles BAnnule.Click Me.Close() End Sub

Page 120: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 52

Public WriteOnly Property Entree() As String ' Méthode d’accès à une propriété ' locale Set(ByVal Valeur As String) TUnePers.Text = Valeur ' Stockage de la valeur affectée End Set ' à cette propriété dans FBase End Property End Class Plusieurs tâches sont disponibles dans FVerifie. Il reçoit les informations signalétiques par sa méthode Entree et les affiche dans les zones appropriées. L’utilisateur peut alors déclarer les informations correctes. Si elles ont été modifiées, elles sont remplacées dans FBase. C’est pourquoi la valeur de la variable Modifiee est passée à la méthode MiseAJour de FBase. Les informations peuvent aussi être simplement déclarées incorrectes et dans ce cas elles sont supprimées dans FBase par sa méthode Supprime. L’utilisateur peut aussi n’avoir à rien dire des informations qui lui sont soumises et annuler la vérification. Voici le code des méthodes MiseAJour et Supprime de FBase : Friend Sub MiseAJour(ByVal ValeurChangee As Boolean, ByVal Personne As String) If ValeurChangee Then Supprime() ' Suppression de la ligne erronée Me.MesPersonnes.Items.Insert(Selection, Personne) ' et remplacement par la bonne End if End Sub Friend Sub Supprime() Me.MesPersonnes.Items.RemoveAt(Selection) ' Suppression de la ligne erronée End Sub Il reste à examiner les liens existant entre FBase et FEncode. Voici le code concerné dans FBase. La méthode Encodage est déclarée Friend pour pouvoir être utilisée dans FEncode. Private Sub BEncodage_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles BEncodage.Click Dim Fs As New FEncode Fs.Owner = Me ' Passer le nom du Form appelant Fs.ShowDialog() If Not Fs.Abandonne Then Encodage(Fs.NomEncode()) ' Appeler la méthode locale Encodage End If ' pour le stockage de la propriété End Sub ' renvoyée par la méthode NomEncode ' de Fs. Friend Sub Encodage(ByVal Personne As String) Me.MesPersonnes.Items.Add(Personne) End Sub L’affichage modal de FEncode est indispensable ici car le formulaire secondaire doit rester actif jusqu'à la fin du traitement et ne pas permettre à FBase de continuer ses traitements. S’il n’est pas modal, FBase reçoit la propriété NomEncode dès l’ouverture de FEncode alors que rien n'a encore été encodé. Cela entraîne l’ajout d’une ligne vide. Indépendamment de la propriété retournée par FEncode, ce dernier utilise la méthode Encodage de FBase pour enregistrer des encodages successifs, sans retour à FBase. Voici le code complet de FEncode : Public Class FEncode Private Annule As Boolean ' Pour ne pas enregistrer sur une annulation Private Modifiee As Boolean ' Pour ne pas ajouter une valeur déjà Private Fp As FBase ' enregistrée

Page 121: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 53

Private Sub FEncode_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

Fp = Me.Owner TUnePers.Clear() Modifiee = False End Sub Private Sub FEncode_Activated(ByVal sender As Object, ByVal e As System.EventArgs)

Handles MyBase.Activated TUnePers.Select() End Sub Private Sub TUnePers_TextChanged(ByVal sender As Object, ByVal e As

System.EventArgs) Handles TUnePers.TextChanged Modifiee = True ' L’événement TextChanged indique End Sub ' s’il y a modification Private Sub BOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles BOK.Click Annule = Not Modifiee ' A la sortie, si la valeur est déjà Me.Close() ' enregistrée, Annule reçoit True et cette End Sub ' méthode devient équivalente à BAnnule_Click Private Sub BAnnule_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles BAnnule.Click Annule = True Me.Close() End Sub Private Sub BAjout_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles BAjout.Click If Me.TUnePers.Text <> String.Empty Then Fp.Encodage(Me.TUnePers.Text) ' Accès à la méthode Encodage de Fp TUnePers.Clear() Modifiee = False ' Réinitialise Modifiee après enregistrement End If TUnePers.Select() End Sub Public ReadOnly Property NomEncode() As String Get Return TUnePers.Text ' Réponse à la demande d’accès à NomEncode End Get ' par Fp End Property Public ReadOnly Property Abandonne() As Boolean Get Return Annule End Get End Property End Class La réponse à l’événement Click du bouton BOK rend le focus à la fenêtre appelante FBase. Cette dernière continue alors son traitement et prend possession de la valeur demandée par Fs.NomEncode dans BEncodage_Click. Entre le moment où FEncode reçoit le focus et le moment où FBase le récupère, la procédure BAjout_Click de FEncode a ajouté toutes les valeurs d’une même session d’encodage dans la ListBox MesPersonnes de FBase. Bien que FBase n’ait pas le focus, sa ListBox est effectivement mise à jour au fur et à mesure des encodages.

Page 122: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 54

Si le formulaire d’encodage ne doit pas permettre l’encodage de plusieurs personnes en une même session, le code peut être écrit autrement. Dans FBase, la procédure d’appel de FEncode est la suivante : Private Sub BEncodage_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles BEncodage.Click Dim Fs As New FEncode Fs.ShowDialog() If Not Fs.Abandonne Then Encodage(Fs.NomEncode()) ' Réponse fournie par la propriété NomEncode de Fs End If End Sub Aucune méthode et aucun objet de FBase n’est directement utilisé dans FEncode qui n’a ainsi pas besoin de connaître l’appelant. Public Class FEncode Private Annule As Boolean Private Sub FEncode_Activated(ByVal sender As Object, ByVal e As System.EventArgs)

Handles MyBase.Activated TUnePers.Clear() TUnePers.Select() End Sub Private Sub BOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles BOK.Click If TUnePers.Text = String.Empty Then Annule = True Else Annule = False End If Me.Close() End Sub Public ReadOnly Property NomEncode() As String Get Return TUnePers.Text End Get End Property Public ReadOnly Property Abandonne() As Boolean Get Return Annule End Get End Property Private Sub BAnnule_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles BAnnule.Click Annule = True Me.Close() End Sub End Class Le cas de l’exemple ci-dessus, où le formulaire d’encodage doit fournir une seule chaîne de caractères par session d’encodage, rappelle un outil de saisie qui existait dans les versions antérieures de VB : l’InputBox. Cet outil est encore disponible dans la librairie MicroSoft.VisualBasic.Interaction mais ne s’utilise plus de la même manière que son ancêtre. La réalisation d’une InputBox ressemblant autant que possible à l’ancienne permet d’aborder une autre forme de communication entre formulaires.

Page 123: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 55

L’InputBox à l’ancienne Voici une InputBox apparemment identique à celle proposée autrefois en VB 6. Elle est utilisée ici pour répondre à une demande d’encodage effectuée par un clic du bouton Encodage du formulaire FBase de l’application précédente. Elle remplace donc le formulaire FEncode de cette application. Dans FBase, le code de réponse à ce clic est le suivant : Private Sub BEncodage_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles BEncodage.Click Dim x As String x = New InputBox("Nom de la personne ?", "Encodage", "", ).Resultat() If x <> String.Empty Then Encodage(x) End If End Sub L’appel de l’InputBox se fait par une instanciation et le résultat est obtenu par la lecture de sa propriété Resultat. Mises à part ces deux différences, les programmeurs VB 6 reconnaîtront leur InputBox. Plusieurs informations sont passées à l’InputBox lors de cet appel. La première chaîne de caractères constitue la question que l’InputBox doit afficher. La deuxième est son titre. La troisième, une chaîne vide dans cet exemple, est la valeur à proposer par défaut à l’utilisateur. La quatrième, qui n’est pas exploitée ici, constituerait la réponse à renvoyer en cas d’annulation par l’utilisateur. Pour passer ces quatre chaînes à l’InputBox, la méthode choisie ici consiste à surcharger le constructeur de la nouvelle classe de sorte qu’il accepte les paramètres souhaités et les utilise comme il convient. Toutes les classes possèdent un constructeur qui est New et qui est exécuté chaque fois qu’un objet est instancié. Pour le surcharger, il suffit de le récrire comme souhaité. C’est ce qui est fait ci-dessous dans Public Sub New. Toutefois, le constructeur initial a des tâches à réaliser et notamment, dans le cas d’une fenêtre, il doit exécuter le code généré par le Concepteur Windows Form qui réalise le dessin à l’écran des composants visuels. C’est pour cette raison que la nouvelle procédure de surcharge commence par l’appel du constructeur initial : Me.InitializeComponent(). Voici le code complet de la classe InputBox. Public Class InputBox Dim ValeurSiAnnulation As String Public Sub New(ByVal Msg As String, Optional ByVal Titre As String = " ",

Optional ByVal ValeurParDefaut As String = Nothing, Optional ByVal ValeurSiAnnule As String = Nothing)

Me.InitializeComponent() ' Une fois la fenêtre dessinée, il faut régler ses Me.Text = Titre ' composants en fonction des valeurs reçues par LMsg.Text = Msg ' paramètres : le Titre, la question Msg pour le ValeurSiAnnulation = ValeurSiAnnule ' Label LMsg, la valeur par défaut pour la TextBox TReponse.Text = ValeurParDefaut ' TReponse et la valeur à retourner en cas TReponse.TabIndex = 0 ' d’annulation à stocker dans la variable Me.ControlBox = True ' ValeurSiAnnulation réservée à cet effet. Me.MaximizeBox = False ' Quelques réglages supplémentaires pour donner Me.MinimizeBox = False ' l’apparence souhaitée à l’InputBox … Me.CancelButton = BAnnule Me.AcceptButton = BOK Me.TopMost = True Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedDialog Me.StartPosition = FormStartPosition.CenterParent Me.ShowDialog() ' … et il n’y a plus qu’à l’afficher. End Sub

Page 124: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 56

Public ReadOnly Property Resultat() As String Get Return TReponse.Text ' La propriété Resultat retourne la valeur de la End Get ' TextBox TReponse End Property Private Sub BOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles BOK.Click Me.Close() ' Rendre la main End Sub Private Sub BAnnule_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles BAnnule.Click TReponse.Text = ValeurSiAnnulation ' Placer la valeur adéquate en cas d’annulation et Me.Close() ' rendre la main End Sub End Class

Résumé des modes de communications abordés Le formulaire FBase affecte directement l’objet TUnePersonne de FAffiche en profitant de la portée Friend de cet objet. De même, FAffiche affecte directement le Label LDernierAffiche de FBase : Dim Fs As New FAffiche ' Dans FBase Fs.TUnePers.Text = MesPersonnes.SelectedItem Fp = Me.Owner ' Dans FAffiche Fp.LDernierAffiche.Text = Me.TUnePers.Text Le formulaire FVerifie réalise la mise à jour de données dans FBase en utilisant directement la procédure MiseAJour déclarée Friend dans ce dernier : Friend Sub MiseAJour(ByVal ValeurChangee As Boolean, ByVal Personne As String) ' FBase Fp.MiseAJour(Modifiee, TUnePers.Text) ' Dans FVerifie Le formulaire FBase passe son nom aux formulaires appelés par l’affectation de leur propriété Owner : Fs.Owner = Me ' Dans FBase Fp = Me.Owner ' Dans FAffiche notamment Le formulaire FBase passe une valeur à FVerifie par une propiété Entree créée à cet effet dans ce dernier. De même, FBase accède aux propriétés NomEncode et Abandonne de FEncode par les méthodes Property créées pour cela : Dim Fs As New FVerifie ' Dans FBase Fs.Entree = MesPersonnes.SelectedItem If Not Fs.Abandonne Then ' Dans FBase Encodage(Fs.NomEncode()) … Public WriteOnly Property Entree() As String … ' Dans FEncode Public ReadOnly Property NomEncode() As String … Public ReadOnly Property Abandonne() As Boolean … Le constructeur de InputBox est surchargé pour permettre l’envoi d’informations lors de l’instanciation : x = New InputBox("Nom de la personne ?", "Encodage", "", ).Resultat() ' Dans FBase

Page 125: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 57

L’exploitation des mémoires de masse L’exploitation des mémoires de masse concerne la création et la gestion des dossiers et des fichiers qu’ils contiennent. Bien qu’ayant de nombreux points communs, dossiers et fichiers méritent des approches distinctes. Les dossiers stockent les fichiers qu’on y place, sans considération de leur contenu, et les fichiers, dont l’organisation du contenu peut relever d’algorithmes complexes, conservent les données des applications. De plus, les outils des traitements des uns et des autres fournis par VB.Net appartiennent à des classes différentes, bien que toutes dérivées de System.IO. La déclaration Imports System.IO est donc requise. Ce sont notamment les classes Directory et File, DirectoryInfo et FileInfo, Path, Stream, StreamWriter et StreamReader, TextWriter et TextReader, BinaryWriter et BinaryReader, ainsi que la classe FileStream qui permet notamment la gestion de fichiers à accès relatif par l’usage d’une méthode Seek. Le contenu de cette partie du cours est limité à l’examen des principaux membres de quelques unes de ces classes. Pour commencer, voici quelques exemples qui se passent généralement de commentaires et qui illustrent les opérations de base sur les dossiers et les fichiers.

Les dossiers Liste des unités

Dim MesLecteurs() As String = Directory.GetLogicalDrives Dim UnLecteur As String For Each UnLecteur In MesLecteurs LBMaListe.Items.Add(UnLecteur) Next

Liste des dossiers d’une unité

Dim MesDossiers() As String = Directory.GetDirectories("X:\") Dim UnDossier As String For Each UnDossier In MesDossiers LBMaListe.Items.Add(UnDossier) Next

Liste des fichiers d’un dossier

Dim MesFichiers() As String = Directory.GetFiles("X:\MonDossier") Dim UnFichier As String For Each UnFichier In MesFichiers LBMaListe.Items.Add(UnFichier) Next

Liste des sous dossiers et fichiers d’un dossier

Private Sub ExploreSousDossiers(ByVal Dossier As String) ' Charger LBMaListe avec les fichiers du Dossier

(placer ce code dans un Try permettrait d’ignorer les fichiers protégés par le système) Dim MesFichiers() As String = Directory.GetFiles(Dossier) Dim UnFichier As String For Each UnFichier In MesFichiers LBMaListe.Items.Add(UnFichier) Next ' Charger LBMaListe avec les sous dossiers du Dossier

(placer ce code dans un Try permettrait d’ignorer les dossiers protégés par le système) Dim SousDossiers() As String = Directory.GetDirectories(Dossier) Dim UnDossier As String For Each UnDossier In SousDossiers LBMaListe.Items.Add(UnDossier) ExploreSousDossiers(UnDossier) ' Rappel récursif de la procédure Next End Sub

GetFiles et GetDirectories peuvent recevoir un second paramètre désignant le nom ou une partie du nom de l’objet recherché, ainsi qu’un troisième paramètre indiquant si la recherche doit se faire récursivement.

Page 126: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 58

Créer un dossier Dim UnDossierNeuf As String = "X:\Tmp\MonDossierNeuf" Directory.CreateDirectory(UnDossierNeuf) ou ' Mieux vaut utiliser Directory que DirectoryInfo

' quand c’est possible (sécurité multi Threads)

Dim UnDossierNeuf As String = "X:\Tmp\MonDossierNeuf" Dim DossierACreer As New DirectoryInfo(UnDossierNeuf) DossierACreer.Create()

Copier un dossier Le langage n’offre aucune méthode de copie de dossier. Il faut donc la programmer. Voici une procédure qui réalise la copie récursive des fichiers et sous dossiers d’un répertoire donné. Les blocs Try d’interception d’erreurs sont destinés ici à éviter l’arrêt de la procédure lorsqu’elle est confrontée à un dossier ou à un fichier protégé par le système. C’est l’utilisation du Try qui est suggérée dans la procédure ExploreSousDossiers.

Private Sub CopieDossiers(ByVal DossierSource As String, ByVal DossierCible As String)

' La classe Path donne notamment la méthode GetFileName qui permet d’extraire le nom du ' fichier ou du sous dossier d’un chemin complet Directory.CreateDirectory(DossierCible) Try Dim MesFichiers() As String = Directory.GetFiles(DossierSource) Dim UnFichier As String For Each UnFichier In MesFichiers File.Copy(UnFichier, DossierCible & "\" & Path.GetFileName(UnFichier)) Next Catch End Try Try Dim SousDossiers() As String = Directory.GetDirectories(DossierSource) Dim UnDossier As String For Each UnDossier In SousDossiers CopieDossiers(UnDossier, DossierCible & "\" &

Path.GetFileName(UnDossier)) Next Catch End Try End Sub

Déplacer un dossier Le code suivant permet le déplacement d’un dossier vers un autre sur la même unité de mémoire de masse.

Directory.Move("X:\Tmp\MonDossier", "X:\Trsf\DossierDeplace") Pour déplacer un dossier vers une autre unité de mémoire de masse, il faut d’abord le copier et supprimer ensuite le dossier source. Renommer un dossier

Directory.Move("X:\Tmp\MonDossier", "X:\Tmp\DossierRenomme") Supprimer un dossier

Dim UnVieuxDossier As String = "X:\Tmp\MonVieuxDossier" Directory.Delete(UnVieuxDossier, True) ' True pour une suppression récursive

Page 127: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 59

Les fichiers Créer un fichier

Dim UnFichierNeuf As String = "X:\Tmp\MonFichierNeuf.TXT" File.Create(UnFichierNeuf)

ou ' Mieux vaut utiliser File que FileInfo

' quand c’est possible (sécurité multi Threads)

Dim UnFichierNeuf As String = "X:\Tmp\MonFichierNeuf.TXT" Dim FichierACreer As New FileInfo(UnFichierNeuf) FichierACreer.Create()

Copier un fichier

File.Copy("X:\Tmp\MonFichier", "X:\Tmp\MonFichierCopie") Déplacer un fichier

File.Move("X:\Tmp\MonFichier", "X:\Trsf\FichierDeplace") Contrairement à ce qu’il en est avec les dossiers, il est permis de déplacer un fichier vers une autre unité de mémoire de masse. Renommer un fichier

File.Move("X:\Tmp\MonFichier", "X:\Tmp\FichierRenomme") Supprimer un fichier

Dim UnVieuxFichier As String = "X:\Tmp\MonVieuxFichier" File.Delete(UnVieuxFichier)

Page 128: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 60

Création, enregistrement et lecture d’un fichier texte La spécificité d’un fichier texte est d’être enregistré ligne par ligne. Ce sont les séquences de caractères des fins de lignes qui délimitent chaque enregistrement qui peuvent être de longueurs différentes. Ces fichiers sont lus séquentiellement. Ce sont les classes StreamWriter et StreamReader qui fournissent les outils nécessaires.

' Ouverture en création : si le fichier existe, il est remplacé Dim FichierSortie As New StreamWriter("X:\Tmp\MonFichierNeuf.TXT") ' Enregistrement For i As Integer = 1 to 5 FichierSortie.WriteLine("Ligne " & i) Next i ' FichierSortie.Flush() ' Close effectue Flush FichierSortie.Close() ' Ouverture en ajout : si le fichier n’existe pas, il est créé Dim FichierSortie As New StreamWriter("X:\Tmp\MonFichierNeuf.TXT", True) ' Enregistrement des lignes supplémentaires For i As Integer = 6 to 10 FichierSortie.WriteLine("Ligne " & i) Next i FichierSortie.Close() ' Ouverture en lecture Dim FichierEntree As New StreamReader("X:\Tmp\MonFichierNeuf.TXT") ' Lecture : première solution ' Dim Ligne As String ' Ligne = FichierEntree.ReadLine() ' While Ligne <> String.Empty ' LBMaListe.Items.Add(Ligne) ' Ligne = FichierEntree.ReadLine() ' End While ' Lecture : meilleure solution Do Until FichierEntree.Peek = -1 ' Peek donne le prochain octet LBMaListe.Items.Add(FichierEntree.ReadLine()) ' sans influencer la lecture Loop ' suivante. Quand cet octet ' vaut -1, il signifie EOF FichierEntree.Close()

Page 129: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 61

Création, enregistrement et lecture d’un fichier de données à accès séquentiel Le fichier à accès séquentiel permet l’enregistrement de structures complexes. Les champs de chaque enregistrement, ainsi que les enregistrements eux-mêmes, peuvent être de longueurs différentes. Il n’est pas nécessaire en VB.Net d’utiliser des délimiteurs de champs, ni des séparateurs d’enregistrements. L’usage des instructions de lecture appropriées permet l’accès aux différentes données selon leur type. Ces fichiers sont lus séquentiellement. Ce sont les classes BinaryWriter et BinaryReader qui fournissent les outils nécessaires. Imports System.IO Public Class FSequentiel Structure Personne Dim Nom As String Dim Age As Integer End Structure Private Sub BEnregistrer_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles BEnregistrer.Click Dim UnePersonne As Personne Static NouvelleSession As Boolean = True Dim FichierSortie As String = "X:\Tmp\MonFichierData.dat" Static FSortie As BinaryWriter If NouvelleSession Then FSortie = New BinaryWriter(File.Open(FichierSortie, _ FileMode.Append, _ FileAccess.Write)) NouvelleSession = False End If With UnePersonne .Nom = TNom.Text .Age = CType(TAge.Text, Integer) If .Nom <> String.Empty And .Age > 0 Then FSortie.Write(.Nom) FSortie.Write(.Age) Else FSortie.Close() NouvelleSession = True End If End With End Sub ' Attention, la procédure BLire_Click suivante ne peut être activée qu’après la fermeture ' du fichier par la procédure BEnregistrer_Click ci-dessus. Private Sub BLire_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles BLire.Click Dim UnePersonne As Personne Dim FichierEntree As String = "X:\Tmp\MonFichierData.dat" Dim FEntree As BinaryReader FEntree = New BinaryReader(File.Open(FichierEntree, _ FileMode.Open, _ FileAccess.Read)) LBMaListe.Items.Clear() While FEntree.PeekChar() <> -1 With UnePersonne .Nom = FEntree.ReadString() .Age = FEntree.ReadInt32() LBMaListe.Items.Add(.Nom & " " & .Age) End With End While FEntree.Close() End Sub End Class

Page 130: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 62

Création, enregistrement et lecture d’un fichier de données à accès aléatoire Le fichier à accès random (à la demande), aussi nommé fichier à accès direct, ou relatif, permet l’enregistrement de structures complexes. Les champs de chaque enregistrement, et donc les enregistrements eux-mêmes, doivent être tous de la même longueur. Un tel fichier peut être lu séquentiellement, mais aussi par accès direct à un enregistrement souhaité, notamment pour permettre le remplacement d’un enregistrement modifié. C’est la classe FileStream qui fournit les outils nécessaires. Le fichier est une instance de la classe FileStream et son ouverture est réalisée par le constructeur selon les mode et type d’accès qui lui sont passés par paramètres.

Dim Fichier As FileStream Dim NomFichier As String = "X:\MesDonnees\MonFichier.dat" Fichier = New FileStream(NomFichier, FileMode, FileAccess)

Les modes d’ouvertures Le mode d’ouverture d’un fichier est donné par une des valeurs possibles du paramètre FileMode du constructeur.

Append Ouverture d’un fichier en ajout et positionnement pour écriture à la fin. Si le fichier n’existe pas, il est créé. Le mode d’accès FileAccess.Write est le seul permis sous ce mode d’ouverture.

Create Ouverture d’un fichier en création et positionnement pour écriture au début. Si le fichier existe, il est remplacé. Le mode d’accès FileAccess.Read est interdit sous ce mode d’ouverture.

CreateNew Ouverture d’un fichier en création et positionnement pour écriture au début. Si le fichier existe, une exception est générée. Le mode d’accès FileAccess.Read est interdit sous ce mode d’ouverture.

Open Ouverture d’un fichier existant. Si le fichier n’existe pas, une exception est générée. OpenOrCreate Ouverture d’un fichier existant ou création s’il n’existe pas. Truncate Ouverture et vidange d’un fichier existant. Si le fichier n’existe pas, une exception est

générée. Le mode d’accès FileAccess.Read est interdit sous ce mode d’ouverture. Les types d’accès Le type d’accès à un fichier est donné par une des valeurs possibles du paramètre FileAccess du constructeur.

Read Accès en lecture seule au fichier. ReadWrite Accès en lecture et en écriture au fichier. Write Accès en écriture seule au fichier.

Tableau récapitulatif des associations permises du mode d’ouverture et du type d’accès

Si le fichier existe Si le fichier n‘existe pas Read ReadWrite Write Read ReadWrite Write

Append Err. Err. OK Err. Err. OK Create Err. OK OK Err. OK OK CreateNew Err. Err. Err. Err. OK OK Open OK OK OK Err. Err. Err. OpenOrCreate OK OK OK OK OK OK Truncate Err. OK OK Err. Err. Err.

Quelques membres de la classe FileStream Length Cette propriété contient la taille du fichier en octets. Position Cette propriété contient la position du prochain octet accessible dans le fichier. A l’ouverture du fichier,

Position vaut 0. Quand la fin de fichier est atteinte, Position a la même valeur que Length.

Page 131: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 63

Close La méthode Close force l’enregistrement des données non encore transférées vers la mémoire de masse, ferme le fichier, et libères les ressources utilisées pour son exploitation.

Flush La méthode Flush force l’enregistrement des données non encore transférées vers la mémoire de masse

et vide le buffer. Seek Cette méthode force le déplacement du pointeur du fichier d’un nombre d’octets spécifié par rapport à

une position également spécifiée et choisie parmi SeekOrigin.Begin, SeekOrigin.Current et SeekOrigin.End.

WriteByte Cette méthode écrit un seul Byte dans le fichier : Fichier.WriteByte(UneVarDeTypeByte). ReadByte Cette méthode lit un seul Byte du fichier : Fichier.ReadByte(UneVarDeTypeByte). L’écriture et la lecture du fichier Outre les méthodes marginales WriteByte et ReadByte, ce sont les méthodes Write et Read qui permettent l’exploitation d’un fichier à accès direct. Elles ont en commun la manipulation d’un nombre donné de Byte appartenant ou affectés à un vecteur à partir d’un indice également donné. Cet indice peut avoir une des valeurs de 0 à Count - 1. Le nombre d’octets à lire ou écrire ne peut être supérieur à Count – IndiceDepart.

Fichier.Write(VecteurDeBytes, IndiceDansLeVecteur, NombreDeBytesAEcrire) Fichier.Read(VecteurDeBytes, IndiceDansLeVecteur, NombreDeBytesALire)

La méthode Read retourne le nombre d’octets effectivement lus. Ce nombre peut être inférieur au NombreDeBytesALire demandé lorsque que la fin du fichier est atteinte.

Nombre = Fichier.Read(VecteurDeBytes, IndiceDansLeVecteur, NombreDeBytesALire) L’usage d’un vecteur de Byte impose que les données à enregistrer soient converties dans ce type et que les données lues subissent la conversion inverse. La classe System.Text.Encoding offre le nécesaire à ces conversions sous la forme du jeu de propriétés énumérées ci-dessous et des méthodes GetBytes et GetString.

ASCII Codage pour le jeu de caractères ASCII (7 bits). Default Codage de la page de codes ANSI actuelle du système. UTF7 Codage du format UTF-7. UTF8 Codage du format UTF-8. Unicode Codage du format UTF-16 avec primauté des octets de poids faible (Little endian). BigEndianUnicode Codage du format UTF-16 avec primauté des octets de poids fort (Big endian). UTF32 Codage du format UTF-32 avec primauté des octets de poids faible (Little endian).

Le jeu de caractères ASCII sur 7 bits ne concerne que les caractères 0 à 127 et le jeu par défaut peut handicaper l’usage d’un fichier sur un autre système dont le format par défaut serait différent. L’UTF représente un des formats de codage du jeu de caractères universel (UTF pour UCS Transformation Format et UCS pour Universal Characters Set). Il faut que le format choisi soit le même à l’enregistrement et à la lecture.

MonVecteur = System.Text.Encoding.UTF8.GetBytes(UneChaine) UneChaine = System.Text.Encoding.UTF8.GetString(MonVecteur)

Les enregistrements doivent être d’une même longueur. Cela implique que les différents champs soient traités avant leur concaténation et leur conversion en tableau de Byte. Toutes les valeurs numériques peuvent aisément être converties en chaînes par CType ou ToString et toutes les chaînes doivent être ajustées à une longueur convenable. Les méthodes PadRight et PadLeft de la classe String servent à compléter une chaîne en reproduisant un caractère donné jusqu’à l’obtention de la longueur souhaitée. De plus la chaîne traitée est alignée à droite ou à gauche selon la méthode choisie. La méthode SubString de la classe String permet de tronquer les chaînes trop longues. Les caractères excédentaires peuvent être enlevés à droite ou à gauche selon le paramétrage de la méthode.

S = UneChaine.PadRight(6,"+"c) ' Si UneChaine est 1234, ' le résulat S vaut 1234++

Page 132: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 64

S = UneChaine.PadLeft(6,"~"c) ' Si UneChaine est 1234, ' le résulat S vaut ~~1234 S = UneChaine.Substring(0, 4) ' Si UneChaine est 123456, ' le résulat S vaut 1234 S = UneChaine.Substring(UneChaine.Length - 4, 4) ' Si UneChaine est 123456, ' le résulat S vaut 3456

Lors de la lecture des enregistrements, les méthodes TrimStart et TrimEnd de la classe String permettent la suppression aisée des caractères ajoutés. La méthode Trim sans paramètre ôte les espaces à gauche et à droite.

S = UneChaine.TrimStart("~") ' Si UneChaine est ~~1234, ' le résulat S vaut 1234 S = UneChaine.TrimEnd("+") ' Si UneChaine est 1234++, ' le résulat S vaut 1234 S = UneChaine.TrimStart() ' Enlève les espaces à gauche S = UneChaine.TrimEnd() ' Enlève les espaces à droite

Exemple complet L’application permet une gestion simple de personnes à l’écran par des boîtes de textes TNom et TSalaire destiné à l’encodage, ainsi qu’un ListView LVPers destiné à la consultation et aux sélections. L’information Numéro présente dans TNumero et dans la première colonne de LVPers ne peut être modifiée. Elle représente le numéro de l’enregistrement de la personne dans le fichier principal. Ce numéro vaut 0 tant que les données n’ont pas été effectivement enregistrées. Les boîtes de textes TFichierPrincipal et TFichierCopie doivent recevoir les chemins et noms complets des fichiers. Le premier fichier est celui de toutes les personnes et le second est destiné à recevoir la copie d’enregistrements sélectionnés dans LVPers. Les boutons Ajouter à la liste (BAjouterListe) et Supprimer de la liste (BSupprimerListe) permettent le pilotage de LVPers. Les boutons Tout lire … (BChargerTout) et Tout sauver … (BEnregistrerTout) assurent respectivement la lecture et l’enregistrement de toutes les personnes du fichier principal. Le bouton Ajouter dans le fichier … (BAjouterFichier) provoque l’écriture d’un seul enregistrement, celui dont les données sont présentes dans les TextBox. Le ListView est également mis à jour. Le bouton Modifier dans le fichier … (BModifierFichier) provoque la ré-écriture de l’enregistrement dont le numéro et les données sont présentes dans les TextBox. Le ListView est également mis à jour. Le bouton Copier la sélection (BCopierSelection) provoque l’enregistrement dans le deuxième fichier des enregistrements dont les données sont sélectionnées dans LVPers. Afin d’illustrer l’ouverture simultanée de deux fichiers et l’accès direct, chaque enregistrement envoyé vers le fichier de copie est d’abord lu dans le fichier principal sur base de son numéro présent dans LVPers. Imports System.IO Public Class FichierRandom Private Structure Personne ' La structure des données d’une personne Dim Nom As String ' associée aux réglages des longueurs des champs Dim Salaire As Integer ' ci-dessous, constitue la définition d’un End Structure ' enregistrement. Elle est écrite ici à titre ' d’exemple et n’est utilisée dans le code que Private LongNom As Integer = 30 ' lors de la lecture d’un fichhier complet Private LongSalaire As Byte = 6 Private LongEnregistrement As Integer ' Valeur calculée au démarrage du programme Private VBytes() As Byte ' L’enregistrement sous forme d’un vecteur de Byte Private UnEnregistrement As String ' et sous forme d’une chaîne Private NomPers As String ' Les variables de manipulations des champs d’un Private SalPers As String ' enregistrement

Page 133: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 65

' Programmation de quelques outils ' La fonction ByteVersString reconstitue une chaîne après la lecture d’un enregistrement Private Function BytesVersString(ByRef DesBytes() As Byte) As String Return System.Text.Encoding.UTF8.GetString(DesBytes) End Function ' La fonction StringVersByte crée un vecteur de Byte à partir d’une chaîne de données Private Function StringVersBytes(ByVal UneChaine As String) As Byte() Return System.Text.Encoding.UTF8.GetBytes(UneChaine) End Function ' La fonction ALongueur permet la mise à longueur et l’alignement des données des champs Private Function ALongueur(ByVal UneChaine As String, ByVal UneLongueur As Integer,

Optional ByVal Alignement As Byte = 0, Optional ByVal Caractere As Char = " "c) As String

Select Case Alignement Case 0 ' Alignement à gauche (coupure ou ajout à droite) If UneChaine.Length > UneLongueur Then Return UneChaine.Substring(0, UneLongueur) Else Return UneChaine.PadRight(UneLongueur, Caractere) End If Case 1 ' Alignement à droite (coupure ou ajout à gauche) If UneChaine.Length > UneLongueur Then Return UneChaine.Substring(UneChaine.Length - UneLongueur, UneLongueur) Else Return UneChaine.PadLeft(UneLongueur, Caractere) End If End Select End Function ' La fonction AccesAuFichier regroupe toutes les commandes d’ouverture et de fermeture des ' fichiers. Elle gère plusieurs fichiers par mise à jour d’un vecteur des références allouées ' à la demande. La fermeture d’un fichier libère sa référence qui peut être utilisée ensuite ' pour un autre fichier. Seules les associations Mode_Ouverture – Type_Accès utiles à cet exemple ' ont été programmées (Cas 1 à 4). Le bloc Select peut être complété selon les besoins. Private Function AccesAuFichier(ByVal Job As Byte, ByRef Canal As Byte,

Optional ByRef NomFichier As String = Nothing) As FileStream Static Dim Fichier() As FileStream = Nothing Static Dim CanalLibre As Integer = 0 Dim NumCanal As Integer If Job < 9 Then ' Le Job 9 est réservé à la fermeture d’un fichier For NumCanal = 0 To CanalLibre – 1 ' Recherche d’un Fichier() non attribué If Fichier(NumCanal) Is Nothing Then Exit For End If Next If NumCanal < CanalLibre Then Canal = NumCanal ' Renvoyer l’indice par Canal passé ByRef Else Canal = CanalLibre ' S’il n’y a pas de référence libre dans Fichier(), ReDim Preserve Fichier(Canal) ' il faut réallouer le vecteur en conséquence et Fichier(Canal) = Nothing ' initialiser la nouvelle référence. CanalLibre += 1 ' Préparation pour la prochaine réallocation. End If End If Select Case Job Case 9 ' Fermeture du fichier If Fichier(Canal) IsNot Nothing Then Fichier(Canal).Close() Fichier(Canal) = Nothing ' La référence est réinitialisée après fermeture End If

Page 134: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 66

Case 1 ' Création pour écriture avec écrasement d'un existant éventuel Fichier(Canal) = New FileStream(NomFichier, FileMode.Create, FileAccess.Write) Case 2 ' Création ou ouverture pour écriture en ajout d'un existant éventuel Fichier(Canal) = New FileStream(NomFichier, FileMode.Append, FileAccess.Write) Case 3 ' Ouverture pour lecture d'un existant Fichier(Canal) = New FileStream(NomFichier, FileMode.Open, FileAccess.Read) Case 4 ' Ouverture pour lecture et écriture d'un existant Fichier(Canal) = New FileStream(NomFichier, FileMode.Open,FileAccess.ReadWrite) End Select Return Fichier(Canal) ' Renvoi du FileStream ou End Function ' Nothing si fichier fermé. ' Les procédures événementielles préalables ' Initialisations au démarrage du programme Private Sub FichierRandom_load(ByVal sender As Object, ByVal e As System.EventArgs)

Handles Me.Load LongEnregistrement = LongNom + LongSalaire ' Déterminer la longueur d’un ReDim VBytes(LongEnregistrement) ' enregistrement et allouer le LVPers.View = View.Details ' vecteur en conséquence. LVPers.FullRowSelect = True ' Réglages de la ListView LVPers.Columns.Add("Numéro", LVPers.Width \ 6, HorizontalAlignment.Left) LVPers.Columns.Add("Nom", LVPers.Width \ 2, HorizontalAlignment.Left) LVPers.Columns.Add("Salaire", LVPers.Width \ 3, HorizontalAlignment.Left) TNumero.ReadOnly = True ' Interdire la modification End Sub ' du numéro d’enregistrement. ' Placer les données d’une ligne sélectionnée de LVPers dans les TextBox Private Sub LVPers_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles LVPers.Click TNumero.Text = LVPers.FocusedItem.Text TNom.Text = LVPers.FocusedItem.SubItems(1).Text TSalaire.Text = LVPers.FocusedItem.SubItems(2).Text End Sub ' Les procédures de traitements des données et des fichiers Private Sub BAjouterListe_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BAjouterListe.Click Dim LV As ListViewItem If TNom.Text = "" Or TSalaire.Text = "" Then Exit Sub ' Données non valides LV = LVPers.Items.Add("0") ' Numéro 0 car non enregistré LV.SubItems.Add(TNom.Text) LV.SubItems.Add(TSalaire.Text) End Sub Private Sub BSupprimerListe_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles BSupprimerListe.Click Dim NrLigne As Integer If TNom.Text = "" Or TSalaire.Text = "" Then Exit Sub ' Données non valides For NrLigne = 0 To LVPers.Items.Count - 1 If LVPers.Items(NrLigne).Text = TNumero.Text Then ' Recherche sur numéro et nom If LVPers.Items(NrLigne).SubItems(1).Text = TNom.Text Then LVPers.Items.RemoveAt(NrLigne) TNumero.Clear() TNom.Clear() TSalaire.Clear() Exit For End If End If Next End Sub

Page 135: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 67

Private Sub BAjouterFichier_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BAjouterFichier.Click

Dim FAjoute As FileStream Dim NumeroCanal As Byte Dim LV As ListViewItem If TNom.Text = "" Or TSalaire.Text = "" Then Exit Sub ' Données non valides If TFichierPrincipal.Text = String.Empty Then ' Nom de fichier requis MessageBox.Show("Désignez un fichier principal.") TFichierPrincipal.Select() Exit Sub End If If TNumero.Text >= "1" Then ' Cette personne existe déjà dans le fichier BModifierFichier_Click(Me, Nothing) Exit Sub End If ' Ouvrir le fichier en création ou ajout et conserver le NumeroCanal pour sa fermeture FAjoute = AccesAuFichier(2, NumeroCanal, TFichierPrincipal.Text) ' Mettre les champs à longueur, créer la chaînes des données, la convertir en vecteur de Byte NomPers = ALongueur(TNom.Text, LongNom, 0) ' Aligner à gauche SalPers = ALongueur(TSalaire.Text, LongSalaire, 1) ' Aligner à droite UnEnregistrement = NomPers & SalPers VBytes = StringVersBytes(UnEnregistrement) ' Enregistrer le vecteur de Byte FAjoute.Write(VBytes, 0, LongEnregistrement) ' Mettre à jour LVPers. Le numéro de l’enregistrement est calculé à partir de Position LV = LVPers.Items.Add(FAjoute.Position / LongEnregistrement) LV.SubItems.Add(TNom.Text) LV.SubItems.Add(TSalaire.Text) ' Fermer le fichier AccesAuFichier(9, NumeroCanal) End Sub Private Sub BModifierFichier_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles BModifierFichier.Click Dim FModifie As FileStream Dim NumeroCanal As Byte Dim NrLigne As Integer If TNom.Text = "" Or TSalaire.Text = "" Then Exit Sub ' Données non valides If TFichierPrincipal.Text = String.Empty Then MessageBox.Show("Désignez un fichier principal.") TFichierPrincipal.Select() Exit Sub End If If TNumero.Text < "1" Then ' Cette personne n'existe pas dans le fichier BAjouterFichier_Click(Me, Nothing) Exit Sub End If ' Ouvrir le fichier en lecture-écriture et conserver le NumeroCanal pour sa fermeture FModifie = AccesAuFichier(4, NumeroCanal, TFichierPrincipal.Text) ' Mettre les champs à longueur, créer la chaînes des données, la convertir en vecteur de Byte NomPers = ALongueur(TNom.Text, LongNom, 0) ' Aligner à gauche SalPers = ALongueur(TSalaire.Text, LongSalaire, 1) ' Aligner à droite UnEnregistrement = NomPers & SalPers VBytes = StringVersBytes(UnEnregistrement)

Page 136: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 68

' Positionner le pointeur sur l’enregistrement dont le numéro est donné par TNumero FModifie.Seek((CType(TNumero.Text, Integer) - 1) * LongEnregistrement,

SeekOrigin.Begin) ' Enregistrer le vecteur de Byte FModifie.Write(VBytes, 0, LongEnregistrement) ' Mettre à jour LVPers là où le numéro d’enregistrement est le même que dans TNumero For NrLigne = 0 To LVPers.Items.Count – 1 If LVPers.Items(NrLigne).Text = TNumero.Text Then LVPers.Items(NrLigne).SubItems(1).Text = TNom.Text LVPers.Items(NrLigne).SubItems(2).Text = TSalaire.Text Exit For End If Next ' Fermer le fichier AccesAuFichier(9, NumeroCanal) End Sub Private Sub BChargerTout_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BChargerTout.Click Dim FEntree As FileStream Dim NumeroCanal As Byte Dim UnePersonne As Personne Dim LV As ListViewItem If TFichierPrincipal.Text = String.Empty Then ' Nom de fichier requis MessageBox.Show("Désignez un fichier principal.") TFichierPrincipal.Select() Exit Sub End If LVPers.Items.Clear() ' Ouvrir le fichier en lecture seule et conserver le NumeroCanal pour sa fermeture FEntree = AccesAuFichier(3, NumeroCanal, TFichierPrincipal.Text) ' Lire les enregistrements de 1 à FEntree.Length / LongEnregistrement For NrEnreg As Integer = 1 To FEntree.Length / LongEnregistrement FEntree.Read(VBytes, 0, LongEnregistrement) ' Extraire les champs de chaque vecteur de Byte et supprimer les caractères de mise à ' longueur utilisés lors de l’enregistrement. Comme ce sont des espaces qui ont été utilisés, ' la méthode Trim sans paramètre suffit pour les ôter à gauche et à droite.

UnEnregistrement = BytesVersString(VBytes) With UnePersonne .Nom = UnEnregistrement.Substring(0, LongNom).Trim .Salaire = CType(UnEnregistrement.Substring(LongNom, LongSalaire).Trim,

Integer) ' Afficher les données lues dans LVPers LV = LVPers.Items.Add(NrEnreg) LV.SubItems.Add(.Nom) LV.SubItems.Add(.Salaire.ToString) End With Next NrEnreg ' Fermer le fichier AccesAuFichier(9, NumeroCanal) End Sub

Page 137: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 69

Private Sub BEnregistrerTout_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BEnregistrerTout.Click

Dim FSortie As FileStream Dim NumeroCanal As Byte If TFichierPrincipal.Text = String.Empty Then ' Nom de fichier requis MessageBox.Show("Désignez un fichier principal.") TFichierPrincipal.Select() Exit Sub End If ' Ouvrir le fichier en écrasement de l’existant et conserver le NumeroCanal pour sa fermeture FSortie = AccesAuFichier(1, NumeroCanal, TFichierPrincipal.Text) ' Lire les données dans LVPers (sans le numéro d’enregistrement) For NrLigne As Integer = 0 To LVPers.Items.Count - 1 ' Mettre les champs à longueur, créer la chaînes des données, la convertir en vecteur de Byte NomPers = ALongueur(LVPers.Items(NrLigne).SubItems(1).Text, LongNom, 0) SalPers = ALongueur(LVPers.Items(NrLigne).SubItems(2).Text, LongSalaire, 1) UnEnregistrement = NomPers & SalPers VBytes = StringVersBytes(UnEnregistrement) ' Enregistrer le vecteur de Byte FSortie.Write(VBytes, 0, LongEnregistrement) Next ' Fermer le fichier AccesAuFichier(9, NumeroCanal) End Sub Private Sub BCopierSelection_Click(ByVal sender As Object,

ByVal e As System.EventArgs) Handles BCopierSelection.Click Dim FSource As FileStream Dim FCible As FileStream Dim NumeroCanalSource As Byte Dim NumeroCanalCible As Byte ' Nom de fichier requis et il If TFichierCopie.Text = TFichierPrincipal.Text Or ' doit être différent du source

TFichierCopie.Text = String.Empty Then MessageBox.Show("Désignez un fichier différent du principal.") TFichierCopie.Select() Exit Sub End If ' Ouvrir le fichier source en lecture seule et conserver NumeroCanalSource pour sa fermeture ' et ouvrir le fichier cible en écrasement d’un existant éventuel et conserver ' NumeroCanalCible pour sa fermeture FSource = AccesAuFichier(3, NumeroCanalSource, TFichierPrincipal.Text) FCible = AccesAuFichier(1, NumeroCanalCible, TFichierCopie.Text) ' Rechercher les lignes sélectionnées dans LVPers For NrLigne As Integer = 0 To LVPers.Items.Count - 1 If LVPers.Items(NrLigne).Selected Then If LVPers.Items(NrLigne).Text > "0" Then ' Un numéro d’enregistrement supérieur à 0 dans LVPers, signifie que l’enregistrement existe ' dans le fichier source. Il est alors lu après positionnement direct dans le fichier source ' et le vecteur conservé tel quel. FSource.Seek((CType(LVPers.Items(NrLigne).Text, Integer) - 1)

* LongEnregistrement, SeekOrigin.Begin) FSource.Read(VBytes, 0, LongEnregistrement) Else

Page 138: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 70

' Un numéro d’enregistrement égal à 0 dans LVPers, signifie que les données de la ligne ' n’ont pas encore été enregistrées dans le fichier principal et il n’est donc pas possible ' de les relire. Dans ce cas, il faut mettre les champs à longueur, créer la chaînes des ' données, la convertir en vecteur de Byte NomPers = ALongueur(LVPers.Items(NrLigne).SubItems(1).Text, LongNom, 0) SalPers = ALongueur(LVPers.Items(NrLigne).SubItems(2).Text, LongSalaire, 1) UnEnregistrement = NomPers & SalPers VBytes = StringVersBytes(UnEnregistrement) End If ' Enregistrer le vecteur de Byte, lu ou créé, dans le fichier cible FCible.Write(VBytes, 0, LongEnregistrement) End If Next ' Fermer les fichiers AccesAuFichier(9, NumeroCanalCible) AccesAuFichier(9, NumeroCanalSource) End Sub End Class La protection des données des fichiers lors des accès simultanés La protection des données des fichiers lors des accès simultanés se réalise par l’emploi de la méthode Lock de la classe FileStream. La méthode Lock permet le verrouillage d’un ou plusieurs enregistrements (Record Locking), voire même de tout un fichier (File Locking). Le verrouillage limité aux seuls enregistrements concernés à un moment donné, présente l’avantage de préserver l’accès aux autres enregistrements par les utilisateurs qui peuvent ainsi continuer à travailler sans perte de temps. La libération d’enregistrements verrouillés se commande par la méthode UnLock, avec les mêmes paramètres que ceux employés lors du verrouillage :

Dim Fichier As FileStream Dim Position As Long Dim NombreOctet As Long ' … … … Fichier.Lock(Position, NombreOctet) ' … … … personne d’autre que moi ne peut modifier ces octets … Fichier.UnLock(Position, NombreOctet)

La protection des données des fichiers par l’usage de ces méthodes consiste à s’assurer qu’une donnée en cours de mise à jour, n’est pas modifiée dans le même temps par un autre utilisateur ou par une autre instance de l’application. Les données en mémoire sont d’abord copiées dans des variables temporaires avant d’être modifiées et les valeurs contenues dans le fichier sont relues avant l’enregistrement des modifications. L’enregistrement lu est verrouillé avant la relecture et n’est libéré qu’après l’enregistrement ou l’abandon des modifications. Voici le procédé : Copie des valeurs sur le point d’être modifiées dans des variables temporaires (Tmp1). Modification des valeurs des variables de travail. Verrouillage de l’enregistrement dans le fichier. Relecture de l’enregistrement à modifier dans des variables temporaires (Tmp2). Comparaison des variables temporaires (Tmp1 et Tmp2). Si les variables temporaires sont égales alors

mettre le fichier à jour avec les variables de travail modifiées sinon

traiter la situation : les données ont été modifiées entre temps Déverrouiller l’enregistrement après la mise à jour réussie ou après abandon des modifications

La procédure BModifierFichier_Click de l’application précédente est récrite ci-après à titre d’illustration de ce procédé. Son expérimentation se réalise en instanciant deux fois la même application, chaque instance traitant le même enregistrement du même fichier.

Page 139: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 71

Private Sub BModifierFichier_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BModifierFichier.Click

Dim FModifie As FileStream Dim NumeroCanal As Byte Dim NrLigne As Integer Dim TmpNom1, TmpSal1 As String ' Déclaration d’un jeu de variables temporaires Dim TmpNom2, TmpSal2 As String ' Déclaration de l’autre jeu Dim OKModif As Boolean

' … … … ' Voir les quelques contrôles de validité dans l’exemple précédent ' … … …

' Ouvrir le fichier en lecture-écriture et conserver le NumeroCanal pour sa fermeture FModifie = AccesAuFichier(4, NumeroCanal, TFichierPrincipal.Text) ' Stockage des infos telles qu'elles ont été lues précédemment dans le fichier For NrLigne = 0 To LVPers.Items.Count - 1 If LVPers.Items(NrLigne).Text = TNumero.Text Then TmpNom1 = LVPers.Items(NrLigne).SubItems(1).Text TmpSal1 = LVPers.Items(NrLigne).SubItems(2).Text Exit For End If Next ' Relecture des infos actuelles du fichier après verrouillage de l'enregistrement FModifie.Lock((CType(TNumero.Text, Integer) - 1) * LongEnregistrement,

LongEnregistrement) FModifie.Seek((CType(TNumero.Text, Integer) - 1) * LongEnregistrement,

SeekOrigin.Begin) FModifie.Read(TByte, 0, LongEnregistrement) TmpNom2 = BytesVersString(TByte).Substring(0, LongNom).Trim TmpSal2 = CType(BytesVersString(TByte).Substring(LongNom, LongSalaire).Trim,

Integer) ' Vérifier si des modifications ont été faites entre temps If TmpNom1 <> TmpNom2 Or TmpSal1 <> TmpSal2 Then If MessageBox.Show("Enregistrement modifié entre temps. Continuer ? ",

"Attention", MessageBoxButtons.YesNo) = Windows.Forms.DialogResult.No Then TNom.Text = TmpNom2 ' Mettre à jour nos données avec celles du fichier TSalaire.Text = TmpSal2 OKModif = False Else OKModif = True ' ou remplacer les données du fichier avec les nôtres End If Else OKModif = True ' Le fichier n’a pas été modifié entre temps End If If OKModif Then ' Mettre les champs à longueur, créer la chaîne des données, la convertir en vecteur de Byte

' … … … ' Voir les quatres lignes de codes dans l’exemple précédent ' … … …

' Positionner le pointeur sur l’enregistrement est donné par TNumero FModifie.Seek((CType(TNumero.Text, Integer) - 1) * LongEnregistrement,

SeekOrigin.Begin) ' Enregistrer le vecteur de Byte FModifie.Write(VBytes, 0, LongEnregistrement) End If ' Déverrouiller l'enregistrement FModifie.Unlock((CType(TNumero.Text, Integer) - 1) * LongEnregistrement,

LongEnregistrement) ' Mettre à jour LVPers là où le numéro d’enregistrement est le même que dans TNumero

' … … … ' Voir le reste de la procédure dans l’exemple précédent ' … … …

End Sub

Page 140: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 72

Création, enregistrement et lecture d’un fichier binaire Comme illustré précédemment, la gestion séquentielle de données de type complexe en mémoire de masse utilise des fichiers binaires. Ils sont également utilisés pour assurer la persistance des objets. L’exemple précédent est suffisamment illustratif pour ce qui concerne l’enregistrement de structures. Mais voici un exemple tout différent qui rappellera peut être à certains, d’anciens utilitaires de visualisations et de modifications d’exécutables. Le but du programme suivant est d’afficher, en hexadécimal et en ASCII, le contenu d’un programme exécutable et de permettre le remplacement d’une chaîne par une autre de même longueur. Il est donné à titre d’exemple et sans aucune prétention de performance. Le programme cible de l’expérimentation est un programme en langage C qui est compilé et qui affiche Bonjour en session Dos. Le fichier exécutable est Bonjour.Exe et il a une taille de ±19 Ko. Voici son code source en C :

#include <stdio.h> main() { printf("\nBonjour"); getc(stdin); return 0; }

L’interface présente deux ListBox (LBHexa et LBAscii) pour l’affichage du contenu du fichier, deux TextBox (TCherche et TRemplace) pour l’acquisition de la chaîne cherchée et de celle de remplacement, et un jeu de boutons qui permet le pilotage du programme :

BCharger provoque la lecture du fichier et son affichage dans les ListBox BRemplacer remplace la chaîne dans les ListBox BEnregistrer récrit le fichier avec le contenu de LBAscii converti au format approprié.

Page 141: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 73

Voici le code complet du programme. Imports System.IO Imports System.Text.RegularExpressions ' Outils de manipulation des chaînes Public Class FBase Private Sub BCharger_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BCharger.Click Dim ValeurLue(2000) As Byte ' Un buffer de lecture de 2000 octets Dim UneLigneHex As String = String.Empty ' La ligne à placer dans LBHexa Dim UneLigneAsc As String = String.Empty ' La ligne à placer dans LBAscii Dim i As Integer Dim FichierEntree As String = "X:\Tmp\Bonjour.Exe" Dim FEntree As BinaryReader FEntree = New BinaryReader(File.Open(FichierEntree, FileMode.Open, FileAccess.Read)) LBHexa.Items.Clear() LBAscii.Items.Clear() ' Effectuer la lecture jusqu’à EOF par lots While FEntree.PeekChar() <> -1 ' de maximum 2000 octets et constituer les ValeurLue = FEntree.ReadBytes(2000) ' lignes des ListBox en les limitant à 16 For i = 1 To ValeurLue.Length ' octets chacune pour faciliter la lecture UneLigneHex = UneLigneHex & String.Format("{0:X2}", ValeurLue(i - 1)) UneLigneAsc = UneLigneAsc & Char.ConvertFromUtf32(ValeurLue(i - 1)) If i Mod 16 = 0 Then LBHexa.Items.Add(UneLigneHex) UneLigneHex = String.Empty ' Vider la ligne sauvée LBAscii.Items.Add(UneLigneAsc) UneLigneAsc = String.Empty End If Next End While FEntree.Close() End Sub Private Sub BRemplacer_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BRemplacer.Click Dim RechAsc, RempAsc As String Dim RechHex As String = String.Empty Dim RempHex As String = String.Empty Dim Ligne As String Dim N As Integer Dim SRech As Regex RechAsc = TCherche.Text ' Acquisition des chaînes en ASCII RempAsc = TRemplace.Text For N = 0 To RechAsc.Length – 1 ' Création des chaînes en hexadécimal RechHex = RechHex & String.Format("{0:X2}", RechAsc.Substring(N, 1)) RempHex = RempHex & String.Format("{0:X2}", RechAsc.Substring(N, 1)) Next SRech = New Regex(RechAsc) ' La recherche s’effectue par la chaîne ASCII For N = 0 To LBAscii.Items.Count – 1 ' pour chaque ligne de la liste LBAscii, et Ligne = LBAscii.Items.Item(N) ' comme les 2 listes présentent les mêmes If SRech.IsMatch(Ligne) Then ' caractères aux mêmes endroits, exception Ligne = Ligne.Replace(RechAsc, RempAsc) ' faite du format, si une ligne de LBAscii LBAscii.Items.RemoveAt(N) ' contient la chaîne cherchée, la ligne LBAscii.Items.Insert(N, Ligne) ' correspondante de LBHexa la contient aussi. LBAscii.SelectedIndex = N ' La chaîne trouvée dans une ligne d’une Ligne = LBHexa.Items.Item(N) ' liste est donc remplacée dans deux lignes. Ligne = Ligne.Replace(RechHex, RempHex) ' Chacune remplace ensuite sa ligne initiale LBHexa.Items.RemoveAt(N) ' dans sa liste d’origine. LBHexa.Items.Insert(N, Ligne) LBHexa.SelectedIndex = N Exit For ' On termine si on a trouvé. Le programme ne End If ' remplace donc que la première occurrence Next ' éventuellement trouvée. End Sub

Page 142: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 74

Private Sub BEnregistrer_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BEnregistrer.Click

Dim UnByte As Byte Dim NrLigne As Integer Dim i As Byte Dim FichierSortie As String = "X:\Tmp\Bonjour.Exe" Dim FSortie As BinaryWriter FSortie = New BinaryWriter(File.Open(FichierSortie, FileMode.Open, FileAccess.Write)) For NrLigne = 0 To LBAscii.Items.Count - 1 For i = 0 To LBAscii.Items.Item(NrLigne).Length - 1 UnByte = Char.ConvertToUtf32(LBAscii.Items.Item(NrLigne).Substring(i, 1), 0) FSortie.Write(UnByte) ' Chaque caractère est converti en sa valeur Next ' numérique Ascii et enregistré Next FSortie.Close() End Sub End Class L’enregistrement octet par octet est très peu performant. Il serait plus rentable de traiter un important nombre d’octets en mémoire et d’en commander l’enregistrement par lots, à l’instar de ce qui est fait pour la lecture du fichier. Et ce n’est pas la seule lacune de ce programme. En effet, la recherche de la chaîne à remplacer s’effectue à l’intérieur de chaque ligne de seize caractères de la ListBox et par conséquent, si cette chaîne ne se trouve pas entièrement sur une même ligne, elle n’est pas trouvée. Enfin, seule la première occurrence trouvée est remplacée. Le but n’était pas de fournir un réel utilitaire de bidouillage, mais seulement un autre exemple de traitement binaire d’un fichier. A propos des outils utilisés ConvertFromUtf32 La méthode ConvertFromUtf32 de la classe Char donne le caractère dont la valeur Ascii lui

est passée en paramètre. La méthode ConvertToUtf32(Chaîne, Index_du_caractère) réalise la convertion inverse.

Format La méthode Format de la classe String sert, comme son nom l’indique, à la mise en forme

de diverses valeurs. Elle est utilisée dans le programme précédent pour représenter les valeurs Ascii des caractères sous forme de paires de chiffres hexadécimaux : String.Format("{0:X2}", RechAsc.Substring(N, 1)). Les possibilités de formatage sont nombreuses. Plusieurs sont étudiées dans une autre partie de ce cours. En attendant, le lecteur peut une fois de plus consulter l’aide de son VisualStudio.Net et le site de MicroSoft déjà référencé.

Substring Cette autre méthode de la classe String permet l’extraction d’un ou plusieurs caractères d’une

chaîne à partir d’une position donnée de 0 à Length – 1. Dans la phrase de code ci-dessus, Substring extrait 1 caractère de RechAsc à la Nième position.

Length Cette méthode, toujours de la classe String, fournit le nombre de caractères d’une chaîne.

Elle a déjà été utilisée précédemment sur des chaînes contenues dans les composants, comme dans l’expression LBAscii.Items.Item(NrLigne).Length de la fonction BEnregistrer_Click. Dans ce programme elle est aussi utilisée pour connaître le nombre de caractères d’une variable dans l’expression RechAsc.Length – 1 de la fonction BRemplacer_Click.

IsMatch Cette méthode de la classe System.Text.RegularExpressions effectue la recherche

d’une chaîne donnée à l’intérieur d’une autre et retourne True en cas de succès. Elle est utilisée dans le programme pour vérifier l’existence de la chaîne recherchée à l’intérieur d’une ligne de LBAscii, dont les lignes sont présentées une par une à la méthode jusqu’à réussite de la recherche, ou jusqu’à la fin de la liste. Les expressions régulières sont étudiées davantage dans la partie suivante du cours.

Page 143: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 75

Inventaire des principaux outils concernant les dossiers et les fichiers Méthodes communes des classes Directory et File

Propriétés communes des classes DirectoryInfo et FileInfo

Méthodes communes des classes DirectoryInfo et FileInfo

Méthodes spécifiques de la classe Directory

Propriétés spécifiques de la classe DirectoryInfo

Méthodes spécifiques de la classe DirectoryInfo

Delete Supprime un fichier sans lever d’exception s’il n’existe pas, ou un répertoire et son contenu.

Exists Détermine si un fichier existe ou si le chemin d'accès donné fait référence à un répertoire existant sur disque.

GetCreationTime Retourne la date et l'heure de création d’un fichier ou d'un répertoire. GetLastAccessTime Retourne la date et l'heure de dernier accès au fichier ou au répertoire spécifié. GetLastWriteTime Retourne la date et l'heure de dernier accès en écriture au fichier ou au répertoire spécifié. Move Déplace un fichier ou un répertoire et son contenu vers un nouvel emplacement. SetCreationTime Définit la date et l'heure de création pour le fichier ou le répertoire spécifié. SetLastAccessTime Définit la date et l'heure de dernier accès au fichier ou au répertoire spécifié. SetLastWriteTime Définit la date et l'heure de dernier accès en écriture au fichier ou au répertoire.

Exists Cette valeur booléenne indique si le fichier ou le dossier représenté par l’instance de la classe existe.

Name Contient le nom du fichier ou du dossier.

Create Crée un fichier ou un dossier s’il n’existe pas. Delete Supprime un fichier ou un dossier. MoveTo Déplace le fichier ou le dossier et son contenu vers un chemin désigné.

CreateDirectory Crée tous les répertoires et sous répertoires comme spécifié par path. GetCurrentDirectory Obtient le répertoire de travail en cours de l'application. GetDirectories Obtient les noms des sous répertoires dans le répertoire spécifié. GetDirectoryRoot Retourne les informations relatives au volume, à la racine ou les deux pour le chemin

d'accès spécifié. GetFiles Retourne les noms des fichiers dans le répertoire spécifié. GetFileSystemEntries Retourne les noms de tous les fichiers et sous répertoires du répertoire spécifié. GetLogicalDrives Extrait les noms des lecteurs logiques sur cet ordinateur sous la forme « <lecteur>:\ ». GetParent Extrait le répertoire parent du chemin d'accès spécifié, y compris les chemins d'accès

absolus et relatifs. SetCurrentDirectory Définit le répertoire de travail en cours de l'application avec le répertoire spécifié.

Parent Contient le nom du dossier parent de celui représenté par l’instance. Root Contient la partie racine du dossier représenté par l’instance.

CreateSubDirectory Crée un sous dossier et ses dossiers parents s’ils n’existent pas. GetDirectories Retourne la liste des sous dossiers. GetFiles Retourne la liste des fichiers du dossier.

Page 144: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 76

Méthodes spécifiques de la classe File AppendText Crée StreamWriter qui ajoute du texte codé UTF-8 à un fichier existant. Copy Copie un fichier existant vers un nouveau fichier. Create Crée un fichier dans le chemin d'accès spécifié. CreateText Crée ou ouvre un fichier pour écrire du texte codé UTF-8. GetAttributes Obtient le FileAttributes du fichier sur le chemin d'accès. Open Ouvre FileStream sur le chemin d'accès spécifié. OpenRead Ouvre un fichier existant pour l'accéder en lecture. OpenText Ouvre un fichier texte codé UTF-8 existant pour l'accéder en lecture. OpenWrite Ouvre un fichier existant pour l'accéder en écriture. SetAttributes Définit les attributs du fichier spécifié. Propriétés spécifiques de la classe FileInfo

Archive L'état d'archivage du statut. Les applications utilisent cet attribut pour marquer les fichiers pour sauvegarde ou suppression.

Compressed Le fichier est compressé. Directory Le fichier est un répertoire. Encrypted Le fichier ou le répertoire est crypté. Cela signifie pour un fichier, que toutes ses

données seront cryptées. Pour un répertoire, cela signifie que tous les nouveaux fichiers et répertoires créés seront cryptés par défaut.

Hidden Le fichier est masqué et n'est donc pas compris dans un listing de répertoires ordinaire. Normal Le fichier est normal et aucun autre attribut n'est défini. Cet attribut n'est valable que

s'il est utilisé seul. NotContentIndexed Le fichier ne sera pas indexé par le service d'indexation du contenu du système

d'exploitation. Offline Le fichier est hors connexion. Les données du fichier ne sont pas disponibles. ReadOnly Le fichier est en lecture seule. ReparsePoint Le fichier contient un point d'analyse, qui est un bloc de données définies par

l'utilisateur associé à un fichier ou à un répertoire. SparseFile Le fichier est un fichier fragmenté. Les fichiers fragmentés sont généralement de gros

fichiers dont les données sont principalement des zéros. System Le fichier est un fichier système. Le fichier fait partie du système d'exploitation ou est

utilisé en mode exclusif par celui-ci. Temporary Le fichier est temporaire. Les systèmes de fichiers essaient de conserver toutes les

données dans la mémoire pour un accès plus rapide plutôt que de les envoyer vers le stockage de masse.

Méthodes spécifiques de la classe FileInfo

Directory Contient le nom du dossier parent. DirectoryName Chemin du dossier contenant le fichier. Length Contient la taille du fichier en octets. Attributes Contient les attributs d’un fichier représenté par une instance de la classe selon la

nomenclature spécifiée dans l’énumération FileAttributes suivante :

AppendText Crée un objet StreamWriter pour ajouter du texte dans un fichier. CopyTo Copie un fichier existant. CreateText Crée un objet StreamWriter pour écrire dans un fichier texte. Open Ouvre un fichier avec divers privilèges de lecture, d’écriture et de partage. OpenRead Crée un objet FileStream en lecture seule.OpenText Crée un objet StreamReader pour lire un fichier texte existant. OpenWrite Crée un objet FileStream en écriture.

Page 145: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 77

La classe Stream Cette classe est abstraite et n’est donc pas directement utilisable. Il faut utiliser ses classes dérivées qui sont les suivantes :

StreamWriter Ecriture de fichiers textes. StreamReader Lecture de fichiers textes. BinaryWriter Ecriture de fichiers binaires. BinaryReader Lecture de fichiers binaires. XmlTextWriter Ecriture de fichiers Xml. XmlTextReader Lecture de fichiers Xml.

Propriété commune BaseStream Obtient le flux sous-jacent qui sert d'interface avec un magasin de sauvegarde. Méthode commune Close Ferme le StreamWriter en cours et le flux sous-jacent. Propriétés et méthodes de la classe StreamWriter

Méthodes de la classe StreamReader DiscardBufferedData Permet à StreamReader d'ignorer ses données en cours. Peek Retourne le prochain caractère disponible, mais ne le consomme pas. Read Lit le caractère ou le jeu de caractères suivant dans le flux d'entrée. ReadBlock Lit un maximum de caractères à partir du flux en cours et écrit les données dans buffer, en

commençant par index. ReadLine Lit une ligne de caractères à partir du flux en cours et retourne les données sous forme de

chaîne. ReadToEnd Lit le flux entre la position actuelle et la fin du flux. Méthodes de la classe BinaryWriter Flush Efface toutes les mémoires tampons pour le writer actuel et provoque l'écriture des données

mises en mémoire tampon sur le périphérique sous-jacent. Seek Définit la position dans le flux actuel. Write Écrit une valeur dans le flux actuel.

AutoFlush Obtient ou définit une valeur indiquant si StreamWriter vide sa mémoire tampon vers le flux sous-jacent après chaque appel à StreamWriter.Write.

NewLine Obtient ou définit la chaîne de terminaison de ligne utilisée pour le TextWriter en cours. Flush Efface toutes les mémoires tampons pour le writer actuel et provoque l'écriture des données

mises en mémoire tampon dans le flux sous-jacent. Write Écrit dans le flux. WriteLine Écrit des données de la manière spécifiée par les paramètres surchargés, suivies d'un

terminateur de ligne.

Page 146: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPV - 78

Méthodes de la classe BinaryReader PeekChar Retourne le caractère disponible suivant et n'avance pas la position du caractère ou de

l'octet. Read Lit des caractères à partir du flux sous-jacent et avance la position actuelle du flux. ReadBoolean Lit une valeur Boolean à partir du flux actuel et avance la position actuelle du flux d'un

octet. ReadByte Lit l'octet suivant à partir du flux actuel et avance la position actuelle du flux d'un octet. ReadBytes Lit count octets à partir du flux actuel dans un tableau d'octets et avance la position actuelle

de count octets. ReadChar Lit le caractère suivant à partir du flux actuel et avance la position actuelle du flux en

fonction du codage utilisé et du caractère spécifique en cours de lecture à partir du flux. ReadChars Lit count caractères à partir du flux actuel, retourne les données dans un tableau de

caractères et avance la position actuelle en fonction du codage utilisé et du caractère spécifique en cours de lecture à partir du flux.

ReadDecimal Lit une valeur décimale à partir du flux actuel et avance la position actuelle du flux de 16 octets.

ReadDouble Lit une valeur à virgule flottante de 8 octets à partir du flux actuel et avance la position actuelle du flux de 8 octets.

ReadInt16 Lit un entier signé de 2 octets à partir du flux actuel et avance la position actuelle du flux de 2 octets.

ReadInt32 Lit un entier signé de 4 octets à partir du flux actuel et avance la position actuelle du flux de 4 octets.

ReadInt64 Lit un entier signé de 8 octets à partir du flux actuel et avance la position actuelle du flux de 8 octets.

ReadSByte Lit un octet signé à partir de ce flux et avance la position actuelle du flux d'un octet. ReadSingle Lit une valeur à virgule flottante de 4 octets à partir du flux actuel et avance la position

actuelle du flux de 4 octets. ReadString Lit une chaîne à partir du flux actuel. La chaîne est préfixée à l'aide de la longueur, codée

comme un entier à raison de sept bits à la fois. ReadUInt16 Lit un entier non signé de 2 octets à partir du flux actuel à l'aide du codage avec primauté

des octets de poids faible (Little endian) et avance la position du flux de 2 octets. ReadUInt32 Lit un entier non signé de 4 octets à partir du flux actuel et avance la position du flux de

4 octets. ReadUInt64 Lit un entier non signé de 8 octets à partir du flux actuel et avance la position du flux de

8 octets.

Page 147: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 1

Programmation avancée en

VB.Net

Page 148: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 2

Tables des matières des pages VBPA Autant savoir....................................................................................................................................................................... 3

Les traitements en ligne de commande .................................................................................................................... 3 Les assemblages ....................................................................................................................................................... 4

Manifeste de l'assemblage .............................................................................................................................. 5 Assemblages mono fichier et multi fichiers ................................................................................................... 5 Le désassemblage ........................................................................................................................................... 6 Espace de noms .............................................................................................................................................. 7 Nom complet .................................................................................................................................................. 7 Instructions au niveau de l'espace de noms .................................................................................................... 7 Références, Imports et Declare ...................................................................................................................... 8

Arguments d'appel d'une application ........................................................................................................................ 9 Application Console ....................................................................................................................................... 9 Application Windows .................................................................................................................................... 9

A partir d’une session Dos ................................................................................................................... 9 A partir de Windows .......................................................................................................................... 10

Les régions ............................................................................................................................................................. 11 Les fonctionnalités du Framework ................................................................................................................................... 12

L'espace de noms System ....................................................................................................................................... 12 La classe System.Object .............................................................................................................................. 12

Méthodes ............................................................................................................................................ 12 La classe String ............................................................................................................................................ 13

Propriété ............................................................................................................................................. 13 Méthodes ............................................................................................................................................ 13

La classe DateTime ...................................................................................................................................... 16 Propriétés ........................................................................................................................................... 17 Méthodes ............................................................................................................................................ 17

La classe RegEx ........................................................................................................................................... 19 Membres ............................................................................................................................................ 22

La classe Math ............................................................................................................................................. 25 Propriétés ........................................................................................................................................... 25 Méthodes ............................................................................................................................................ 25

La classe VBMath ........................................................................................................................................ 25 L'espace de nom System.Drawing .......................................................................................................................... 26

La classe Graphics ....................................................................................................................................... 26 L'espace de noms System.Collections .................................................................................................................... 31

Les classes offertes ....................................................................................................................................... 31 Membres communs à la plupart des collections........................................................................................... 31

Propriétes ........................................................................................................................................... 31 Méthodes ............................................................................................................................................ 31

La classe ArrayList ...................................................................................................................................... 32 Propriété ............................................................................................................................................. 32 Méthodes ............................................................................................................................................ 32

La classe BitArray ........................................................................................................................................ 35 Propriété ............................................................................................................................................. 35 Méthodes ............................................................................................................................................ 35

La classe Hashtable ...................................................................................................................................... 36 Propriétés ........................................................................................................................................... 36 Méthodes ............................................................................................................................................ 36

La classe SortedList ..................................................................................................................................... 37 Méthodes ............................................................................................................................................ 37

La classe Queue ........................................................................................................................................... 38 Méthodes ............................................................................................................................................ 38

La classe Stack ............................................................................................................................................. 39 Méthodes ............................................................................................................................................ 39

Une dernière comparaison ........................................................................................................................... 40 La classe CollectionBase ............................................................................................................................. 41 L’espace de noms Collections.Generic ........................................................................................................ 43

Page 149: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 3

Autant savoir

Les traitements en ligne de commande Visual Studio .Net offre, en session Dos, des commandes supplémentaires à celles ordinairement disponibles dans ce mode de travail. Pour avoir accès à ces commandes, il faut lancer le mode ligne de commande par l'outil approprié (et non par le raccourci habituel de Windows, ni par Démarrer/Exécuter/Cmd). L'accès à ce mode de travail se fait par …/Visual Studio Tools/Invite de commandes de Visual Studio. Les commandes ajoutées sont nombreuses et se répertorient en plusieurs catégories regroupant les outils de configuration, dont l'assembly linker (al.exe), les outils de débogage, les outils de sécurité, les outils généraux dont les assembleur et désassembleur (ilasm.exe et ildasm.exe). Le lecteur qui souhaite en savoir davantage sur ces outils peut consulter l'aide de son Visual Studio sous la rubrique Ligne de commande, .NET Framework (outils) et le site de MicroSoft déjà référencé plusieurs fois dans ce cours. Il faut encore savoir que chacune des commandes ajoutées dispose de son aide en ligne accessible par son option /?, par exemple ildasm /? fournit les informations essentielles pour l'usage de la commande ildasm. Voici quelques exemples de compilation en ligne de commande. Compilation du programme C++ dont voici le source Bonjour.cpp : #include "stdafx.h" #using <mscorlib.dll> using namespace System; int _tmain() { Console::WriteLine("Bonjour"); Console::ReadLine(); return 0; } La commande cl Bonjour.cpp /clr produit les fichiers Bonjour.obj et Bonjour.exe. La commande link Bonjour.obj /Out:EncoreBonjour.exe produit le fichier EncoreBonjour.exe. Compilation du programme J# dont voici le source Bonjour.jsl : public class UneClasse { public static void main() { System.Console.WriteLine("Bonjour"); System.Console.ReadLine(); } } La commande vjc Bonjour.jsl produit le fichier Bonjour.exe. Attention, le langage J# n’existe plus dans Visual Studio depuis la version 2008.

Page 150: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 4

Compilation du programme C# dont voici le source Bonjour.cs : using System; class UneClasse { public static void Main() { Console.WriteLine("Bonjour"); Console.ReadLine(); } } La commande csc Bonjour.cs produit le fichier Bonjour.exe. Compilation du programme VB dont voici le source Bonjour.vb : Imports System Module ModulePrincipal Sub Main()

Console.WriteLine("Bonjour") Console.ReadLine() End Sub End Module La commande vbc Bonjour.vb produit le fichier Bonjour.exe. La commande vbc /t:module Bonjour.vb produit le fichier Bonjour.netmodule destiné à être utilisé dans d'autres assemblages. La commande vbc /t:library Bonjour.vb produit le fichier Bonjour.dll qui est prêt à être livré pour être référencé dans des projets clients. La commande ildasm Bonjour.netmodule ouvre la fenêtre de désassemblage. La commande ildasm Bonjour.netmodule /Out:Bonjour.il /Text crée le fichier Bonjour.il qui contient le résultat de tout le désassemblage.

Les assemblages Les assemblages, qui se présentent sous la forme d'un ou plusieurs fichiers exécutables (.exe) et/ou de fichiers de bibliothèques de liaisons dynamiques (.dll), constituent les blocs de construction du .NET Framework. Ils fournissent le Common Language Runtime ainsi que les informations nécessaires à la reconnaissance des implémentations des types. Un assemblage ressemble à une collection de types et de ressources conçus pour fonctionner ensemble et qui forment une unité fonctionnelle logique. Un assemblage peut contenir aussi des références vers d'autres assemblages (notion de dépendances). Ces ressources, ces types et ces références sont décrits dans un bloc de données appelé le manifeste. Le manifeste fait partie de l'assemblage et en est la description. Un aspect important des assemblages est qu'ils font partie de l'identité d'un type. L'identité d'un type est constituée de l'assemblage qui le contient associé au nom du type. Ceci signifie, par exemple, si l'assemblage A expose un type appelé T, et qu'un assemblage B expose un autre type aussi appelé T, le runtime .NET verra ces deux types comme complètement différents. Enfin, les assemblages correspondent au niveau le plus bas du versioning dans .NET. Chaque assemblage possède un numéro de version qui reflète son niveau de compatibilité. Aussi chaque référence à un assemblage (depuis un autre) inclut le nom et la version de l'assemblage référencé. Le numéro de version comporte quatre parties numériques (5.5.2.33 par exemple) et il est modifiable dans le fichier AssemblyInfo.vb présent dans tous les projets en cours de développement. Conventionnellement, si seule la quatrième partie de ce numéro diffère entre deux assemblages, ils sont considérés comme compatibles. Si une des deux premières parties diffère, alors ils sont considérés comme incompatibles. Les autres cas de figure indiquent une possibilité de compatibilité. Étant donné que les assemblages contiennent des informations sur le contenu, la version et les dépendances, les applications créées à l'aide de Visual Studio .NET ne s'appuient pas sur les valeurs de la registry pour fonctionner correctement. Les assemblages réduisent les conflits de DLL et rendent les applications plus fiables, plus faciles à déployer. Ce sont les assemblages qui permettent l'exécution côte à côte, c'est-à-dire la cœxistence de plusieurs

Page 151: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 5

versions de code et la possibilité pour une application de choisir la version du Common Language Runtime ou d'un composant qu'elle utilise. Les installations ultérieures d'autres versions du runtime, d'une application ou d'un composant n'affectent pas les applications déjà installées. Dans la plupart des cas, l'installation d'une application .NET se réalise simplement en copiant les fichiers du dossier bin du projet sur l'ordinateur cible, c'est l'installation xcopy. Manifeste de l'assemblage Tous les assemblages contiennent donc un manifeste qui en est la description. Le manifeste de l'assemblage contient, en principal, le nom et le numéro de version de l'assemblage, ainsi qu'une liste de toutes les dépendances (fichiers .dll ou autres nécessaires à l'application) identifiées par leur nom, un identifiant et leur numéro de version. Assemblages mono fichier et multi fichiers Les exemples de compilation en ligne de commande vus précédemment constituent autant d'exemples d'assemblages mono fichier. L'exemple suivant illustre un assemblage multi fichiers. Voici le code source de l’application UneClasseVB écrite par un programmeur VB. Il y crée un espace de noms MonEspace qui contient une classe UneClasse exposant les méthodes Majuscule dont le rôle est de mettre en majuscules tous les caractères d'une chaîne qui lui est passée en paramètre, et DisBonjour dont le rôle est d'afficher la chaîne Bonjour. Un module ModulePrincipal contient le point d'entrée dans le programme (la Sub Main). La seule tâche de ce programme est d'afficher Bonjour. Imports System Namespace MonEspace ' Définition d'un Namespace Public Class UneClasse Public Function Majuscule(S As String) As String Return S.ToUpper End Function Public Sub DisBonjour() Console.Write("Bonjour ") Console.ReadLine() End Sub End Class End Namespace Module ModulePrincipal Sub Main() Dim Bonjour As New MonEspace.UneClasse Bonjour.DisBonjour() End Sub End Module Voici maintenant le code source du programmeur C#. Il crée une classe MonApplication dont le rôle est d'afficher la chaîne coucou en majuscule. Le programmeur C# souhaite réutiliser du code qu'il sait existant dans l'espace de nom MonEspace créé par son collègue VB. Il déclare donc utiliser cet espace de noms par une clause using et peut alors instancier un objet de type UneClasse et en utiliser les méthodes exposées, soit Majuscule dans cet exemple. using System; using UneClasseVB.MonEspace; ' Après avoir ajouté UneClasseVB aux class MonApplication ' références du projet C# { public static void Main() { UneClasse CetteInstance = new UneClasse(); Console.WriteLine(CetteInstance.Majuscule("coucou")); Console.ReadLine(); } } Le programmeur VB a réalisé une application qui se suffit à elle-même et peut produire un exécutable UneClasseVB.exe. Le programmeur C# a réalisé une application qui dépend de l'application VB et plusieurs situations peuvent se présenter. Soit le programmeur C# ne dispose que de l'exécutable UneClasseVB.exe, ou d'une librairie UneClasseVB.dll, soit il dispose d'un module d'assemblage, soit il dispose des sources VB.

Page 152: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 6

Dans le premier cas, le programmeur C# doit compiler son programme en référençant UneClasse.exe ou UneClasse.dll, selon le cas :

csc MonApplication.cs /r:UneClasseVB.exe ou csc MonApplication.cs /r:UneClasseVB.dll Dans le second cas, le programmeur C# doit compiler son programme en y ajoutant le module nécessaire :

csc MonApplication.cs /addmodule:UneClasseVB.netmodule Dans le troisième cas, le programmeur C# doit d'abord créer le module d'assemblage UneClasseVB.netmodule ou l'exécutable UneClasseVB.exe par une des commandes (vbc /t:module UneClasseVB.vb ou vbc UneClasseVB.vb) vues précédemment, et compiler ensuite son application comme indiqué ci dessus. Le désassemblage L'usage du MSIL Disassembler (Ildasm.exe en ligne de commande) permet l'examen du contenu d'un assemblage et de déterminer si un fichier est un assemblage ou un module. Il n'entre pas dans le cadre de ce cours de s'attarder sur le désassemblage, mais voici une illustration de ce qu’il peut montrer. Les trois illustrations ci-dessus correspondent respectivement au désassemblage des fichiers UneClasseVB.netmodule, UneClasseVB.exe et MonApplication.exe. Un double clic sur un des éléments provoque l'affichage dans une nouvelle fenêtre du résultat de son désassemblage. L'illustration suivante montre un extrait des codes produits par le désassemblage du Manisfeste de MonApplication.exe.

Page 153: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 7

Espace de noms Les espaces de noms permettent d'organiser les objets définis dans un assemblage. Les assemblages peuvent contenir plusieurs espaces de noms, qui peuvent à leur tour contenir d'autres espaces de noms. Les espaces de noms permettent d'éviter les ambiguïtés et de simplifier les références lors de l'utilisation de grands groupes d'objets, tels que les bibliothèques de classes. Les espaces de noms Visual Studio .NET permettent la résolution des problèmes d’encombrement des espaces de noms par l'usage des noms complets. Ce problème survient lorsque le développeur est confronté à la présence de classes homonymes dans différentes bibliothèques, cause des conflits de noms. Par défaut, tous les fichiers exécutables créés à l'aide de Visual Basic .NET contiennent un espace de noms portant le même nom que le projet. Par exemple, à défaut d'un espace de noms explicitement défini, l'exécutable MonProgramme.exe contient un espace de noms appelé MonProgramme. Plusieurs assemblages peuvent utiliser le même espace de noms. Visual Basic .NET les considère comme un ensemble unique de noms. Il est ainsi possible de définir des classes pour un espace de noms appelé MonEspace dans un assemblage nommé A1, et définir des classes supplémentaires pour le même espace de noms à partir d'un assemblage nommé A2. Nom complet Un nom complet est une référence à un objet préfixée avec le nom de l'espace de noms où l'objet est défini. Il permet d'éviter les conflits entre les noms, aussi appelés collisions de noms, en donnant au compilateur le moyen de déterminer l'objet en cours d'utilisation. Si une classe MaClasse est créée dans un MonProjetX, elle est accessible sans qualification partout dans ce projet selon la portée qui lui est attribuée. Mais si dans la même portée, la classe MaClasse du ProjetY doit aussi être utilisée, sa déclaration doit être qualifiée par le nom complet. Il en est de même pour toutes les références d'objets. Par exemple, dans MonProjetX qui contient la déclaration d'une classe MaClasse :

Dim UnObjet as New MaClasse Dim UnAutreObjet as New ProjetY.MaClasse

et dans un autre projet qui référencerait à la fois MonProjetX et ProjetY :

Dim UnObjetX = MonProjetX.MaClasse Dim UnObjetY = ProjetY.MaClasse

ou :

Imports MaClasseX = MonProjetX.MaClasse Imports MaClasseY = ProjetY.MaClasse ' … Dim UnObjetX As MaClasseX Dim UnObjetY As MaClasseY

Instructions au niveau de l'espace de noms Un espace de noms peut contenir des modules, des interfaces, des classes, des délégués, des énumérations, des structures et d'autres espaces de noms. Mais il ne peut contenir des propriétés, des procédures, des variables et des événements, ceux ci doivent être déclarés dans des conteneurs tels que des modules, des structures ou des classes.

Page 154: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 8

Références, Imports et Declare Pour utiliser un assemblage existant dans un projet Visual Basic .Net, il faut l'ajouter dans les références du projet et utiliser l'instruction Imports pour choisir l'espace de noms des éléments à utiliser. Une fois qu'un assemblage est référencé et importé, toutes les classes, propriétés, méthodes et autres membres de ses espaces de noms sont disponibles pour l'application comme si leur code faisait partie du fichier source. Un seul assemblage peut contenir plusieurs espaces de noms, et chaque espace de noms peut contenir différents regroupements d'éléments, y compris d'autres espaces de noms. Ainsi, si le programmeur C# de l'exemple précédent avait écrit son code en VB, il aurait pu référencer UneClasseVB.dll dans son projet et écrire le code suivant : Imports System Imports MonEspace Module Principal Public Sub Main() Dim CetteInstance = New UneClasse Console.WriteLine(CetteInstance.Majuscule("coucou")) Console.ReadLine() End Sub End Module L'instruction Imports permet le remplacement du nom de l'espace de noms par l'alias qui conviendrait mieux au programmeur :

Imports MonAlias = Nom_EspaceDeNoms C'est ainsi que le programme précédent pourrait s'écrire : Imports Scrn = System.Console Imports MonEspace Module Principal Public Sub Main() Dim CetteInstance = New UneClasse Scrn.WriteLine(CetteInstance.Majuscule("coucou")) Scrn.ReadLine() End Sub End Module Il ne faut pas confondre pas les références de projet avec les instructions Imports ou Declare. Les références de projet rendent les objets externes, tels que les objets des assemblages, disponibles pour les projets Visual Studio .NET. L'instruction Imports simplifie l'accès aux références de projet, mais ne constitue pas un accès à ces objets. Elle facilite l'accès aux méthodes des classes en éliminant le besoin de taper de manière explicite les noms complets des références. Elle permet donc une économie de dactylographie, un peu à la manière de l'instruction With utilisée pour les structures. Les alias permettent d'assigner un nom plus convivial à seulement une partie d'un espace de noms. L'instruction Declare permet de déclarer une référence à une procédure externe dans une bibliothèque de liaison dynamique (DLL) installée sur le système. L'exemple suivant, qui ne nécessite ni Imports ni référence, permet l'utilisation d'une MBox qui n'est autre que la MessageBox exposée par la librairie système user32.dll.

Public Class FBase Declare Auto Function MBox Lib "user32.dll" _ Alias "MessageBox" (ByVal hWnd As Integer, _

ByVal txt As String, ByVal caption As String, _ ByVal Typ As Integer) As Integer

Private Sub UneProcedure() MBox(Me.Handle.ToInt32, "Message", "Titre", 0) ' 0 = vbOKOnly End Sub

End Class

Page 155: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 9

Cet usage de l'instruction Declare est avantageusement abandonné au profit d'une méthode tout à fait conforme au concept DotNet, qui dispose de l'aide en cours de dactylographie du code, qui est plus succincte et plus lisible, comme l'illustre le code de l'exemple précédent modifié comme ceci :

Imports MBox = System.Windows.Forms.MessageBox Public Class FBase

Private Sub UneProcedure() MBox.Show("Message", "Titre", MessageBoxButtons.OK) ' vbOKOnly End Sub

End Class

Arguments d'appel d'une application L'usage des arguments d'appel d'une application permet à l'utilisateur de lancer le programme en lui passant des paramètres. Par exemple, la commande Format du Dos nécessite le passage d'un paramètre désignant l'unité à formater. De même, la commande Copy a besoin du nom du fichier à copier et du nom du fichier de destination.

Format D: Copy MonFichier.Txt MonFichier.Sav

Ces commandes travaillent ainsi parce que leurs arguments d'appel ont été déclarés lors de l'écriture de la procédure Main, où ils sont aussi appelés paramètres formels, et que les paramètres passés en ligne de commande, aussi appelés paramètres effectifs, leur sont substitués lors de l'exécution du code. Ces paramètres se déclarent comme étant un tableau de String. A l'exécution, chaque paramètre effectif occupe une place dans ce tableau. Application Console Le mécanisme des paramètres en ligne de commande impose qu'une procédure Main soit écrite et désignée comme objet de démarrage. C'est la signature choisie pour cette Sub Main qui permet ou interdit la prise en compte de paramètres effectifs.

Sub Main() ' Sans paramètre Sub Main(ByVal Arguments() As String) ' Avec paramètre(s)

Le code suivant est compilé pour fournir le programme Test.exe. Module MonModule Sub Main(ByVal Arguments() As String) For i As Integer = 0 To Arguments.Length - 1 Console.WriteLine(Arguments(i)) Next Console.ReadLine() End Sub End Module Application Windows Certaines applications Windows peuvent être lancées à partir d’une session Dos, d’autres seulement à partir de l’interface graphique, par un raccourci ou par un double-clic de leur nom dans l’Explorateur de Windows. La prise en compte des paramètres se programme différemment selon ces cas. A partir d’une session Dos L’application est initialement construite comme étant une application Console. Elle est composée du module MonModule et du formulaire FAffichage sur lequel une ListBox nommée MaListBox a été dessinée. La Sub Main a été désignée comme objet de démarrage dans les propriétés du projet. L'application compilée donne le programme Test.exe.

Page 156: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 10

Sub Main(ByVal Arguments() As String) Dim F As New FAffichage For i As Integer = 0 To Arguments.Length - 1 F.MaListBox.Items.Add(Arguments(i)) Next F.ShowDialog() End Sub

A partir de Windows L’application est construite comme étant une application Windows. Elle est uniquement composée du formulaire FBase sur lequel une ListBox nommée MaListBox a été dessinée. L'application compilée donne le programme Test.exe. Il peut être activé comme n’importe quelle autre application Windows. Toutefois, pour lui passer des paramètres d’exécution, le programme est activé à partir de la boîte de dialogue Exécuter de Windows. C’est la librairie MicroSoft.VisualBasic qui fournit l’outil nécessaire, la fonction Command, qui livre une chaîne de caractères composées des paramètres d’exécution. La méthode Split de la classe String permet la transformation de cette chaîne en un tableau où chaque paramètre est disponible distinctement. La prise en compte des paramètres d’exécution se programme ordinairement comme ci-dessous, dans la procédure de réponse à l’événement Load du formulaire de démarrage.

Private Sub FBase_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

Dim Arguments() As String = Microsoft.VisualBasic.Command.Split(" ")

For i As Integer = 0 To Arguments.Length - 1 MaListBox.Items.Add(Arguments(i)) Next End Sub

Page 157: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 11

Les régions Le mot clé Region, qui s'utilise précédé d'un dièse ( # ), est une instruction d'organisation du code qui permet d'en améliorer l'accessibilité et la lisibilité. Le programmeur peut regrouper plusieurs parties de son code en régions selon les règles qui lui conviennent. Il ne faut pas confondre cette instruction avec la classe Region de l'espace de noms System.Drawing, cette dernière servant à la description de l'intérieur d'une forme graphique. Chaque fois qu’une application Windows est créée sous Visual Studio 2003, une instruction Region est placée pour isoler le code produit par le Concepteur Windows Form. L’usage de Region à cet effet n’existe plus depuis Visual Studio 2005. Mais cette instruction n'est pas réservée à Visual Studio et le programmeur peut en user à sa guise. L'instruction #Region doit être suivie d'une chaîne de caractères qui constitue la seule indication visible de la région lorsque celle-ci est masquée. Tout bloc #Region doit se terminer par une instruction #End Region. Les régions peuvent être imbriquées, mais nul doute que d'en abuser produira un effet contraire à celui escompté ! Voici, pour illustrer l'usage de régions, un exemple un peu excessif pratiqué sur la classe InputBox étudiée dans la partie précédente du cours. Quatre régions ont été créées de sorte à rassembler les différentes parties du code de manière logique. Une fois les régions mises en place, leurs codes peuvent être rendus visibles ou masqués selon les nécessités du moment. Ci dessous, les régions Procédures événementielles privées et Interface ont été rendues visibles tandis que les régions Propriétés privées et Constructeur sont restées masquées.

' Régions imbriquées #Region " Interface " ' … #Region " Sorties " ' … #End Region ' Sorties #Region " Entrées " ' … #End Region ' Entrées #End Region ' Interface

Page 158: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 12

Les fonctionnalités du Framework Les fonctionnalités du Framework se répartissent en deux catégories, celle des classes organisées en espaces de noms arborescents qui fournissent tous les outils de développement utiles au programmeur, et celle des services internes qui assurent notamment la gestion de la mémoire, le contrôle des versions, la sécurité des applications. Les services internes opèrent de manière transparente pour le programmeur qui se contentera d'en appréhender seulement quelques concepts. Pour l'instant, il importe de passer en revue quelques classes du Framework et d'y repérer des outils fréquemment utiles.

L'espace de noms System L'espace de noms System est la base de toutes les classes du Framework. Bien que rarement référencé de manière explicite par le programmeur, il est automatiquement inclus dans les références de tout projet Visual Studio .Net et certaines de ses classes, telle que celle des types de données, sont exploitées en permanence. La classe System.Object Descendante directe de la classe System, la classe Object mérite un peu d'attention du fait que tous les objets de l'environnement DotNet en dérivent et héritent donc de ses méthodes. Méthodes Equals La méthode Equals retourne la valeur True si deux instances d'objet sont identiques et False dans le cas contraire. Le résultat livré par cette méthode doit être interprété de façon différente pour des variables de type valeur et celles de type référence. Pour les variables de type valeur, ce sont les contenus qui sont effectivement comparés. Pour les variables de type référence, seules les adresses sont comparées et un résultat True signifie donc que ces références pointent vers la même instance. Dim S1, S2, S3 As String ' Rappel : Les chaînes sont S1 = "AZERTY" ' de type valeur S2 = "QWERTY" S3 = "QWERTY" Console.WriteLine(S1.Equals(S2).ToString) ' Affiche False Console.WriteLine(S2.Equals(S3).ToString) ' Affiche True S3 = S1 Console.WriteLine(Equals(S1, S3).ToString) ' Affiche True GetHashCode Cette méthode retourne un identifiant de type Integer qui peut servir de base au calcul de la clé (hashcode) d’un objet à ajouter une collection. Des objets identiques fournissent le même entier. Par exemple, les lignes de codes ci-dessous expérimentées à la suite des précédentes : Console.WriteLine(S1.GetHashCode.ToString) ' Affiche 1229974258 Console.WriteLine(S2.GetHashCode.ToString) ' Affiche 1203825954 Console.WriteLine(S3.GetHashCode.ToString) ' Affiche 1229974258 GetType Cette méthode donne le type de l'objet. Console.WriteLine(S1.GetType.ToString) ' Affiche System.String ToString Cette méthode permet la représentation d'un objet par une chaîne de caractères non formatée. Pour certains types de données tels que les Date, Time et les numériques, la méthode offre des possibilités de formatage semblables à celles de la méthode Format. Par exemple F.ToString(".###") présente le réel F avec 3 décimales.

Page 159: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 13

La classe String Propriété Length Cette propriété contient le nombre de caractères d'une chaîne. Dim S1 As String S1 = "AZERTY" Console.WriteLine(S1.Length.ToString) ' Affiche 6 Méthodes Compare La méthode Compare retourne une valeur négative, nulle ou positive selon que des deux chaînes passées en paramètre, la première est plus petite, égale ou plus grande que la deuxième. Dim S1, S2, S3 As String S1 = "AZERTY" S2 = "QWERTY" S3 = "QWERTY" Console.WriteLine(String.Compare(S1, S2).ToString) ' Affiche -1 Console.WriteLine(String.Compare(S2, S1).ToString) ' Affiche 1 Console.WriteLine(String.Compare(S2, S3).ToString) ' Affiche 0 Un troisième paramètre de valeur True ou False peut être passé à la méthode pour indiquer si la casse doit être différenciée ou non lors de la comparaison (Compare(S1, S2, True) ou Compare(S1, S2, False)). Concat Cette méthode réalise la concaténation des chaînes qui lui sont passées en paramètre et restitue une nouvelle chaîne. Elle est équivalente à l'usage de l'opérateur de concaténation &. Console.WriteLine(String.Concat(S1, " ", S2)) ' Affiche AZERTY QWERTY Copy Cette méthode copie une chaîne dans une autre. Elle est équivalente à l'usage de l'opérateur d'affectation =. CopyTo La méthode CopyTo effectue la copie d'une sous chaîne vers un tableau de caractères. Ses arguments sont l'index de départ de la copie, le tableau destinataire, l'index de départ dans ce tableau et le nombre de caractères à copier. Dim S1 As String Dim TS(10) As Char S1 = "AZERTY" S1.CopyTo(2, TS, 0, 3) ' De l'index 2, vers TS à partir de 0, 3 caractères For i As Integer = 0 To 2 Console.Write(TS(i)) ' Affiche ERT Next IndexOf et LastIndexOf Ces méthodes fournissent respectivement la première et la dernière occurrence d'une sous chaîne dans une chaîne donnée. Elles renvoient -1 si la sous chaîne n’est pas trouvée. S1 = "AZERTYAZERTY" Console.WriteLine(S1.IndexOf("ZER")) ' Affiche 1 Console.WriteLine(S1.LastIndexOf("ZER")) ' Affiche 7

Page 160: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 14

Insert Permet l'insertion d'une sous chaîne dans une chaîne donnée à partir d'un index également donné. S1 = "AZERTY" Console.WriteLine(S1.Insert(4, " & ")) ' Affiche AZER & TY Remove Suppression dans une chaîne, d'un nombre de caractères donnés à partir d'un index également donné. S1 = "AZERTY" Console.WriteLine(S1.Remove(3, 2)) ' Affiche AZEY Replace Remplacement dans une chaîne, de toutes les occurrences d’une sous chaîne donnée par une autre. S1 = "AZERTY est un mot comme un autre" Console.WriteLine(S1.Replace("ER", "xxx")) ' Affiche AZxxxTY est un … Console.WriteLine(S1.Replace(" ", "")) ' Enlève tous les espaces Split Copie les sous chaînes délimitées par un ou plusieurs caractères dans un tableau de caractères. Dim S1 As String Dim S2 As Char() = {" "} ' Table des séparateurs Dim S3() As String ' Tableau des résultats S1 = "AZERTY, QWERTY sont des mots comme : Visual Studio; VB" S3 = S1.Split(S2) For Each S As String In S3 Console.WriteLine(S) Next

Affichage quand S2 est {" "} : AZERTY, QWERTY sont des mots comme : Visual Studio; VB

Affichage après remplacement de S2 par {" ", ",", ";", ":"} : AZERTY QWERTY sont des mots comme Visual Studio VB

Page 161: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 15

Substring La méthode Substring permet l'extraction d'une sous chaîne désignée par son index de début et son nombre de caractères. Dim S1 As String S1 = "AZERTY" Console.WriteLine(S1.Substring(2, 3)) ' Affiche ERT ToLower et ToUpper Ces méthodes effectuent les conversions de chaînes en minuscules pour ToLower et en majuscule pour ToUpper. Trim, TrimStart et TrimEnd Ces méthodes permettent la suppression d'un ou plusieurs caractères donnés, ou des espaces à défaut de spécification, aux extrémités d'une chaîne (Trim), ou au début (TrimStart) ou à la fin (TrimEnd) de la chaîne. Dim S1 As String Dim S2 As Char() = {"@", "#"} ' Table des caractères à ôter S1 = "#@AZERTY #@# QWERTY@#" Console.WriteLine(S1.TrimStart(S2)) ' Affiche AZERTY #@# QWERTY@# Console.WriteLine(S1.Trim(S2)) ' Affiche AZERTY #@# QWERTY Console.WriteLine(S1.TrimEnd(S2)) ' Affiche #@AZERTY #@# QWERTY Format La signature de la méthode Format se compose de deux parties, à l'instar de la fonction printf du langage C, l'une constituant une chaîne de formatage, l'autre la liste des valeurs à formater. Ainsi, dans l'expression String.Format("{1} + {0} = {2}", a, b, a + b), chaque paire d'accolades contient l'index d’une valeur à formater (de 0 à n-1) et les guillemets délimitent la chaîne résultat qui est alors suivie des valeurs à représenter. Si l’expression de cet exemple est contenue dans une instruction de sortie (auquel cas la mention String.Format peut être omise) et si les valeurs respectives de a et b sont 3 et 5, le résultat affiché sera 5 + 3 = 8. Entre les accolades, les index de chaque valeur peuvent être assortis d'attributs de formatage. En voici les principaux.

Codes Usages et exemples C Présentation d'un nombre au format monétaire.

String.Format("{0:C}", 1.25) ' Résultat 1,25€ D Présentation d'un nombre sans décimale avec zéros non significatifs.

String.Format("{0:D5}", 125) ' Résultat 00125 E Présentation d'un nombre au format scientifique.

String.Format("{0:E}", 123456.789) ' Résultat 1,234568E+005 F Présentation d'une valeur numérique avec un nombre déterminé de décimales.

String.Format("{0:F3}", 1234.56789) ' Résultat 1234,568 N Présentation d'un nombre avec les séparateurs de milliers.

String.Format("{0:N}", 1234567.89) ' Résultat 1 234 567,89 String.Format("{0:N3}", 1234.56789) ' Résultat 1 234,568

P Présentation d'un nombre sous forme de pourcentage. String.Format("{0:P}", 0.1234) ' Résultat 12,34%

R Présentation des nombres en virgule flottante avec leur précision maximale. String.Format("{0:R}", Math.Pi) ' Résultat 3,1415926535897931

X Présentation des nombres entiers sous forme hexadécimale. String.Format("{0:X}", 65535) ' Résultat FFFF String.Format("{0:X8}", 65535) ' Résultat 0000FFFF

. Définition de la position du séparateur décimal dans la chaîne résultante. , La virgule provoque le placement du séparateur de milliers dans la chaîne résultante. Elle doit être

placée à l’intérieur d’une chaîne de formatage composée des caractères # et 0 et à l’intérieur de la partie entière. Si elle est placée à l’extrême droite de cette partie, le nombre est divisé par 1000. String.Format("{0:##,#.##}", 123456.35) ' Résultat 123 456,35 String.Format("{0:###,.##}", 123456.35) ' Résultat 123,46

Page 162: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 16

# Définition de l’espace souhaité pour la présentation des nombres sans les zéros non significatifs. String.Format("{0:###}", 12.35) ' Résultat 12 String.Format("{0:###}", 12.65)) ' Résultat 13 String.Format("{0:###.##}", 12.35) ' Résultat 12,35 String.Format("{0:###.#}", 12.35) ' Résultat 12,4

0 Définition de l’espace souhaité pour la présentation des nombres avec placement des zéros non significatifs. String.Format("{0:000}", 12.35) ' Résultat 012 String.Format("{0:000}", 12.65) ' Résultat 013 String.Format("{0:000.00}", 12.3) ' Résultat 012,30 String.Format("{0:000.00}", 12.35) ' Résultat 012,35 String.Format("{0:000.0}", 12.35) ' Résultat 012,4

% La chaîne résultante représente le nombre multiplié par 100 et terminé par le caractère %. String.Format("{0:###.#%}", 12.35) ' Résultat 1235% String.Format("{0:###.#%}", 0.1265) ' Résultat 12,7%

- + $ et autres

caractères et textes divers

Ces caractères sont insérés dans la chaîne résultante tels qu'ils sont tapés dans la chaîne de formatage. C'est aussi le cas de la plupart des caractères. Certains, tels que des caractères de formatage, doivent être précédés d'un \ pour être affichés. C'est le cas du \ lui-même. String.Format("{0:>####}", 123) ' Résultat >123 String.Format("{0:\####}", 123) ' Résultat #123 String.Format("{0:\\###}", 123) ' Résultat \123 String.Format("{0:A###}", 123) ' Résultat A123

Les chaînes de formatage peuvent être construites préalablement en tout ou en partie par l'usage d'une ou plusieurs variables de type String et de chaînes littérales. S = "AZER" String.Format("{0:" & S & " TY ##}", 12) ' Résultat AZER TY 12 S = "{0:" & "T " & "Y ##}" String.Format(S, 12) ' Résultat T Y 12

d Présentation d’une date au format court. Dans les exemples suivants, D est un objet DateTime et sa propriété Now contient les date et heure de l'instant actuel. String.Format("{0:d}", D.Now) ' Résultat 7/03/2006

D Présentation d’une date au format long. String.Format("{0:D}", D.Now) ' Résultat mardi 7 mars 2006

t Présentation d’une heure au format court. String.Format("{0:t}", D.Now) ' Résultat 21:14

T Présentation d’une heure au format long. String.Format("{0:T}", D.Now) ' Résultat 21:14:25

M ou m Présentation d’une date par le jour et le mois. String.Format("{0:M}", D.Now) ' Résultat 7 mars

Y ou y Présentation d’une date par le mois et l’année. String.Format("{0:Y}", D.Now) ' Résultat mars 2006

f Présentation d’une date au format long avec l’heure au format court. String.Format("{0:f}", D.Now) ' Résultat mardi 7 mars 2006 21:14

F Présentation d’une date au format long avec l’heure au format long. String.Format("{0:F}", D.Now) ' Résultat mardi 7 mars 2006 21:14:25

g Présentation d’une date au format court avec l’heure au format court. String.Format("{0:g}", D.Now) ' Résultat 7/03/2006 21:14

G Présentation d’une date au format court avec l’heure au format long. String.Format("{0:G}", D.Now) ' Résultat 7/03/2006 21:14:25

s Présentation de la date et de l’heure sous la forme d’une clé pouvant fournir un index. String.Format("{0:s}", D.Now) ' Résultat 2006-03-07T21:14:25

La classe DateTime La classe DateTime offre toutes les fonctionnalités nécessaires aux traitements et présentations des dates et des heures. Ses propriétés et méthodes sont applicables aussi aux données de type System.Date. La méthode de formatage des dates et heures n'est autre que la méthode ToString qui est surchargée dans cette classe afin de permettre la prise en compte d'éventuels attributs de formatage. Les méthodes String.Format et DateTime.ToString sont donc parfois concurrentes en matière de représentation des dates et heures. Au programmeur de choisir celle qui lui convient le mieux selon les nécessités de son application. Voici quelques membres de cette classe.

Page 163: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 17

Propriétés Now, Date, Today, Day, DayOfWeek, DayOfYear, Month, Year, TimeOfDay, Hour, Minute, Second, Millisecond et Ticks Chacune de ces propriétés en lecture seule, restitue un élément de la date et de l'heure contenues dans un objet DateTime. Dim D As DateTime Dim S As String ' Now S = D.Now.ToString() ' Résultat dans S : 9/03/2006 14:39:25 ' Date S = D.Now.Date.ToString() ' Résultat dans S : 9/03/2006 00:00:00 ' Today S = D.Now.Today.ToString() ' Résultat dans S : 9/03/2006 00:00:00 ' Day S = D.Now.Day.ToString() ' Résultat dans S : 9 ' DayOfWeek S = D.Now.DayOfWeek.ToString() ' Résultat dans S : Thursday ' DayOfYear S = D.Now.DayOfYear.ToString() ' Résultat dans S : 68 ' Month S = D.Now.Month.ToString() ' Résultat dans S : 3 ' Year S = D.Now.Year.ToString() ' Résultat dans S : 2006 ' TimeOfDay S = D.Now.TimeOfDay.ToString() ' Résultat dans S : 14:39:25.3734016 ' Hour S = D.Now.Hour.ToString() ' Résultat dans S : 14 ' Minute S = D.Now.Minute.ToString() ' Résultat dans S : 39 ' Second S = D.Now.Second.ToString() ' Résultat dans S : 25 ' Millisecond (Bonne alternative au générateur de nombres aléatoires : valeurs de 0 à 999) S = D.Now.Millisecond.ToString() ' Résultat dans S : 373 ' Ticks : nombre d'intervalles de 100 nanosecondes écoulés depuis le 1er janvier 0001, 12:00 S = D.Now.Ticks.ToString() ' Résultat dans S : 632775119653734016 Méthodes AddYears, AddHours, AddMonths, AddDays, AddMinutes, AddSeconds, AddMilliseconds et AddTicks Chacune de ces méthodes ajoute une valeur donnée à la valeur correspondante (selon le nom de la méthode) de la date ou de l'heure contenues dans un objet DateTime. La valeur à ajouter peut être négative. Dim D As DateTime Dim DD As DateTime Dim S As String D = D.Now ' Test réalisé le 9/3/2006 à 14h.39' S = D.Day.ToString() ' Résultat dans S : 9 D = D.AddDays(3) ' Ajoute 3 jours à D S = D.Day.ToString() ' Résultat dans S : 12 S = D.Date.ToString() ' Résultat : 12/03/2006 00:00:00 D = D.AddDays(-12) ' Retire 12 jours à D S = D.Date.ToString() ' Résultat : 28/02/2006 00:00:00 S = D.Hour.ToString() ' Résultat dans S : 14 D = D.AddHours(3) ' Ajoute 3 heures à D S = D.Hour.ToString() ' Résultat dans S : 17

Page 164: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 18

DaysInMonth La méthode DaysInMonth retourne le nombre de jours du mois en fonction du mois et de l'année.

DateTime.DaysInMonth(2008, 2) ' Résultat 29 IsLeapYear La méthode IsLeapYear retourne la valeur True si l'année qui lui est passée en paramètre est bissextile et False dans le cas contraire. ToShortTimeString, ToLongTimeString, ToShortDateString et ToLongDateString Ces méthodes restituent des chaînes de caractères représentant les dates et heures sous le format court ou long. Console.WriteLine(D.Now.ToString) ' Affichage 9/03/2006 17:13:38 Console.WriteLine(D.Now.ToShortTimeString) ' Affichage 17:13 Console.WriteLine(D.Now.ToLongTimeString) ' Affichage 17:13:38 Console.WriteLine(D.Now.ToShortDateString) ' Affichage 9/03/2006 Console.WriteLine(D.Now.ToLongDateString) ' Affichage jeudi 9 mars 2006 ToString Comme déjà signalé, le méthode ToString est surchargée dans la classe DateTime afin de permettre la prise en compte de codes de formatage. En voici quelques uns.

Codes Usages et exemples : / Caractères séparateurs dans la présentation des heures ( : ) et dans celle des dates ( / ).

y %y yy yyyy M %M MM MMM MMMM d %d dd ddd dddd

Présentation des années, mois, et jours sous forme numérique ou alphabétique, cette dernière pouvant être abrégée non. L'usage du caractère % permet de représenter la seule valeur désignée. DateTime.Now.ToString("y") ' Résultat septembre 2006 DateTime.Now.ToString("%y") ' Résultat 6 DateTime.Now.ToString("yy") ' Résultat 06 DateTime.Now.ToString("yyyy") ' Résultat 2006 DateTime.Now.ToString("M") ' Résultat 6 septembre DateTime.Now.ToString("%M") ' Résultat 9 DateTime.Now.ToString("MM") ' Résultat 09 DateTime.Now.ToString("MMM") ' Résultat sept. DateTime.Now.ToString("MMMM") ' Résultat septembre DateTime.Now.ToString("d") ' Résultat 6/09/2006 DateTime.Now.ToString("%d") ' Résultat 6 DateTime.Now.ToString("dd") ' Résultat 06 DateTime.Now.ToString("ddd") ' Résultat mer. DateTime.Now.ToString("dddd") ' Résultat mercredi

H HH h hh m mm s ss

Présentation des heures, minutes et secondes, au format 12h ou 24h, avec ou sans zéros non significatifs. DateTime.Now.ToString("h:m:s") ' Résultat 6:13:8 DateTime.Now.ToString("H:m:s") ' Résultat 18:13:8 DateTime.Now.ToString("hh:mm:ss") ' Résultat 06:13:08 DateTime.Now.ToString("HH:mm:ss") ' Résultat 18:13:08

f ff …

fffffff

Présentation du nombre de millisecondes avec ses décimales sous forme d'une chaîne de 1 à 7 chiffres, selon le nombre de f placés. DateTime.Now.MilliSecond() ' Résultat 564 DateTime.Now.ToString("fff") ' Résultat 564 DateTime.Now.ToString("fffffff") ' Résultat 5642720

' \ et autres

caractères et textes divers

L'apostrophe et la barre oblique inverse permettent respectivement d'encadrer une chaîne littérale et de précéder un caractère littéral qui serait aussi un code de formatage. Dans la plupart des cas, les valeurs littérales peuvent être directement insérées dans la chaîne de formatage. DateTime.Now.ToString("'Wavre, le ' %d MMM yy") ' Résultat Wavre, le 6 sept. 06 DateTime.Now.ToString("\/\/ %d MM yyyy") ' Résultat // 6 09 2006 DateTime.Now.ToString("Wavre, le dd,MM,yy.") ' Résultat Wavre, le 06,09,06. DateTime.Now.ToString("Wavre, le %d-MM-yyyy") ' Résultat Wavre, le 6-09-2006

Page 165: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 19

La classe RegEx La classe RegEx offre de très efficaces moyens de manipulations des chaînes de caractères : les expressions régulières. Celles-ci permettent de tester le format d'une chaîne de caractères et d'effectuer des recherches de sous chaînes correspondant à un modèle contenant des caractères jokers, aussi appelés métacaractères. On peut, par exemple, vérifier qu'une chaîne représentant une date est bien au format jj/mm/aa en utilisant le modèle "\d\d/\d\d/\d\d" où le symbole \d désigne un chiffre. Pour réaliser des exemples d'utilisations des expressions régulières, comme pour les exploiter dans les applications, il faut mettre en place quelques éléments. Il faut tout d'abord importer l'espace de noms System.Text.RegularExpressions. Il faut disposer d'une chaîne sur laquelle doivent s'opérer les manipulations, ainsi qu'une chaîne destinée à contenir les modèles contenant les métacaractères. Et, dès lors que le traitement peut générer plusieurs résultats, il faut un tableau spécial de type MatchCollection pour les stocker. L'exécution du code suivant produit l'affichage de chacun des nombres présents dans la chaîne Chaine : 123, 456 et 789.

Imports System.Text.RegularExpressions ' … Dim Chaine As String ' Chaîne à manipuler Dim Modele As String ' Chaîne pour stocker le modèle Dim Reg As Regex ' Une instance de la classe RegEx Dim Resultats As MatchCollection ' Une collection de classe RegEx Dim i as Integer ' pour les résultats Chaine = "Chaine : 123, 456 et 789" ' Créer une chaîne pour les tests Modele = "\d+" ' Créer le modèle à tester Reg = New Regex(Modele) ' Initialiser la classe avec le modèle Resultats = Reg.Matches(Chaine) ' Obtenir les résultats d'une recherche For i = 0 To Resultats.Count - 1 ' Parcourir la collection des résultats Console.WriteLine(Resultats(i).Value) ' pour les obtenir un par un Next

Voici un large extrait des codes disponibles pour la définition des modèles recherchés :

Codes Usages et exemples \ La barre oblique inverse ne peut être employée seule dans un modèle. Elle permet d'utiliser un

métacaractère comme caractère littéral ou de définir un caractère ordinaire comme métacaractère. Par exemple, n correspond au caractère n, mais \n correspond à une marque de fin de ligne. La séquence\\ correspond à \ et \( correspond à (. Ou encore, $ est un métacaractère représentant la fin de la chaîne à traiter, mais \$ correspond au caractère $. Chaine = "C:\Cours\VBNet" Modele = "\\" ' Chaîne cherchée : \ 2 Résultats \ \

^ Indique que la recherche se fait seulement au début de la chaîne. Chaine = "C:\Cours\VBNet" Modele = "^C" ' Chaîne cherchée : C (en début) 1 Résultat C Modele = "^V" ' Chaîne cherchée : V (en début) 0 Résultat

$ Indique que la recherche se fait seulement à la fin de la chaîne. Chaine = "C:\Cours\VBNet" Modele = "C$" ' Chaîne cherchée : C (à la fin) 0 Résultat Modele = "Net$" ' Chaîne cherchée : Net (à la fin) 1 Résultat Net

* Joker pour le caractère qui le précéde, lequel peut être présent de 0 à n fois dans la chaîne recherchée. Chaine = "C:\Cours\VBNet\Cool" Modele = "Co*" ' Chaîne cherchée : C à Cooooo …. 3 Résultats C Co Coo

+ Joker pour le caractère qui le précéde, lequel peut être présent de 1 à n fois dans la chaîne recherchée. Chaine = "C:\Cours\VBNet\Cool" Modele = "Co+" ' Chaîne cherchée : Co à Cooooo … 2 Résultats Co Coo

Page 166: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 20

? Joker pour le caractère qui le précéde, lequel peut être présent 0 ou 1 fois dans la chaîne recherchée. Chaine = "C:\Cours\VBNet\Cool" Modele = "Co?" ' Chaîne cherchée : C et Co 3 Résultats C Co Co

. Le point est joker pour un et un seul caractère, quel qu'il soit, sauf la marque fin de ligne. Chaine = "C:\Cours\VBNet\Calculs" Modele = "C." ' Chaîne cherchée : C et un caractère 3 Résultats C: Co Ca

x|y|z|… Le pipe est le OU logique du modèle. La chaine recherchée correspond à l'une des expressions x, y, z séparées par le | . Chaine = "C:\Cours\VBNet\Cool" Modele = "VB|Co*" ' Chaînes cherchées : VB ou C à Coooo … 4 Résultats C: Co VB Coo

{n} Les expressions entre accolades se rapportent au caractère qui précède l'accolade ouverte. Ici, le nombre n qui est un entier non négatif signifie que la chaîne cherchée correspondant à exactement n fois le caractère. Chaine = "C:\Cours\VBNet\Cool" Modele = "Co{1}" ' Chaînes cherchées : Co 2 Résultats : Co Co Modele = "o{2}" ' Chaînes cherchées : oo 1 Résultat oo

{n,} Les expressions entre accolades se rapportent au caractère qui précède l'accolade ouverte. Ici, le nombre n qui est un entier non négatif signifie que la chaîne cherchée correspondant à au moins n fois le caractère. Chaine = "C:\Cours\VBNet\Cool" Modele = "Co{1,}" ' Chaînes cherchées : Co à Coooo … 2 Résultats Co Coo Modele = "Co{2,}" ' Chaînes cherchées : Coo à Coooo … 1 Résultat Coo Remarque : {1,} équivaut à + et {0,} équivaut à *.

{n,m} Les expressions entre accolades se rapportent au caractère qui précède l'accolade ouverte. Ici, les nombres n et m qui sont des entiers non négatifs, signifient que la chaîne cherchée correspondant à au moins n fois et au plus m fois le caractère. Chaine = "C:\Cours\VBNet\Cool" Modele = "Co{1,1}" ' Chaînes cherchées : Co 2 Résultats Co Co Modele = "Co{1,2}" ' Chaînes cherchées : Co et Coo 2 Résultats Co Coo Remarque : {0,1} équivaut à ?.

[xyz] La recherche porte sur n'importe quel caractère de la chaîne placée entre les crochets. Chaine = "C:\Cours\VBNet\Cool" Modele = "[:\\s]" ' Caractères cherchés : : \ s 5 Résultats \ s \ \ Modele = "[o]" ' Caractère cherché : o 3 Résultats o o o

[^xyz] La recherche porte sur n'importe quel caractère n'appartenant pas à la chaîne placée entre les crochets. Chaine = "C:\Cours\VBNet\Cool" Modele = "[^oursetl]" ' Caractères cherchés : Tous sauf o u r s e t l 10 Résultats C : \ C \ V B N \ C

[a-z] La recherche porte sur n'importe quel caractère appartenant à la plage de caractères désignée par les bornes a et z. Chaine = "C:\Cours\VBNet\Cool" Modele = "[A-Z]" ' Caractères cherchés : Toutes les majuscules 6 Résultats C C V B N C

[^a-z] La recherche porte sur n'importe quel caractère n'appartenant pas à la plage de caractères désignée par les bornes a et z. Chaine = "C:\Cours\VBNet\Cool" Modele = "[^a-z]" ' Caractères cherchés : Tous sauf les minuscules 10 Résultats C : \ C \ V B N \ C

\b Joker pour un seul caractère qui doit être un caractère d'espacement. Chaine = "Par monts et par vaux, quelque part …" Modele = "ar\b" ' Chaînes cherchées : ar suivie d'un blanc 2 Résultats ar ar Remarque : "ar\b" équivaut à "ar ".

Page 167: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 21

\B Joker pour un seul caractère qui ne peut être un caractère d'espacement. Chaine = "Par monts et par vaux, quelque part …" Modele = "ar\B" ' Chaînes cherchées : ar non suivie d'un blanc 1 Résultat ar

\s Signifie que le caractère précédent doit être suivi d'un espace, d'une tabulation, d'un saut de page, … . Remarque : \s équivaut à [\b\f\n\r\t\v].

\S Signifie que le caractère précédent ne peut être suivi d'un espace, d'une tabulation, … . Remarque : \S équivaut à [^\b\f\n\r\t\v].

\d Joker pour un seul caractère qui doit être un chiffre. \D Joker pour un seul caractère qui ne peut être un chiffre.

\f \n \r \t \v

Jokers pour un seul caractère d'échappement : \f pour un saut de page. \n pour une marque de fin de ligne. \r pour un retour chariot. \t pour une tabulation. \v pour une tabulation verticale.

\w Joker pour tout caractère numérique et alphabétique non accentué, ainsi que pour le caractère de soulignement. Remarque : \w équivaut à [A-Za-z0-9_].

\W Joker pour tout caractère non numérique ni alphabétique non accentué, et différent du caractère de soulignement. Remarque : \W équivaut à [^A-Za-z0-9_].

\nOct Le nombre nOct qui est un entier non négatif exprimé en octal représente le code ASCII du caractère cherché.

\xnHex Le nombre nHex qui est un entier non négatif exprimé en hexadécimal représente le code ASCII du caractère cherché.

(modèle) Les parenthèses permettent la création d'un modèle composé de plusieurs groupes (ou sous modèles) et de récupérer ensuite les chaînes trouvées et leurs sous groupes distinctement. Le code présenté avant ce tableau, qui a servi à l'expérimentation de tous les modèles précédents, peut toujours être utilisé mais il ne permet pas la pleine exploitation des groupes. Avec ce code : Chaine = "C:\Cours\VBNet\Cool\VBNet\Cool" Modele = "(VBNet)\\(Cool)" ' Chaînes cherchées : VBNet, Cool et VBNet\Cool 2 Résultats VBNet\Cool VBNet\Cool (sans distinction de VBNet et Cool) En modifiant le code précédent comme ci dessous, la même expérimentation donne davantage de résultats : ' … For i = 0 To Resultats.Count - 1 For j = 0 To Resultats(i).Groups.Count - 1 Console.WriteLine(Resultats.Groups(i).Value) Next Next Avec le code modifié : Chaine = "C:\Cours\VBNet\Cool\VBNet\Cool" Modele = "(VBNet)\\(Cool)" ' Chaînes cherchées : VBNet, Cool et VBNet\Cool 3 Résultats VBNet\Cool VBNet Cool (une chaîne complète et chaque sous groupes) Modele = "((VBNet)\\(Cool)){1,}" 4 Résultats VBNet\Cool VBNet\Cool VBNet Cool

\num et

?<nom>

Le nombre num, qui est un entier de 1 à n, doit ici être utilisé après un groupe entre parenthèses. Il est l'index (de 1 à n) d'un sous groupe à répéter dans le modèle qui en contient de 1 à n. Il apporte une économie de dactylographie. Chaine = "C:\Cours\VBNet\Cool\VBNet\Cool" Modele = "(VBNet)\\(Cool)\\\1\\\2" ' Chaînes cherchées : VBNet, Cool et 3 Résultats VBNet\Cool\VBNet\Cool VBNet Cool VBNet\Cool\VBNet\Cool Modele = "((VBNet)\\(Cool))\\\1" 4 Résultats VBNet\Cool\VBNet\Cool VBNet\Cool VBNet Cool Les sous groupes peuvent en outre être nommés pour permettre l'usage de noms significatifs à la place des index. Il faut pour cela utiliser la syntaxe : Modele = "(?<Nom>Modele)". Les deux lignes suivantes de définition de modèle sont équivalentes. Modele = "(?<GrpVb>VBNet)\\(?<GrpCo>Cool)\\\<GrpVb>\\\<GrpCo>" Modele = "(VBNet)\\(Cool)\\\1\\\2"

Page 168: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 22

Membres Options La propriété Options est un paramètre de type RegexOptions passé au constructeur Regex pour modifier le mode de travail du moteur d'exploitation des expressions régulières. Elle est définie par syntaxe Reg = New Regex(Modele, Option) avec de nombreuses valeurs possibles pour Option dont RegexOptions.SingleLine, RegexOptions.MultiLine, RegexOptions.IgnoreCase et RegexOptions.RightToLeft.

SingleLine Cette option modifie la signification du point (.) de sorte qu'il corresponde à n'importe quel caractère, sans l'exception de la marque de fin de ligne ( \n ).

MultiLine Cette option modifie la signification de ^ et $ de sorte qu'ils correspondent respectivement au

début et à la fin de n'importe quelle ligne, et non simplement au début et à la fin de la chaîne entière.

IgnoreCase Cette option a pour effet que la recherche ne tient pas compte de la casse des caractères utilisés

dans le modèle.

Chaine = "C:\Cours\VBNet\COOL\" Modele = "VB|Co" Reg = New Regex(Modele, RegexOptions.IgnoreCase) ' Résultats Co VB CO

RightToLeft Cette option provoque le traitement de chaîne de la fin vers le début.

Chaine = "C:\Cours\VBNet\Cool\" Modele = "VB|Co|Net" Reg = New Regex(Modele) ' Résultats Co VB Net Co Reg = New Regex(Modele, RegexOptions.RightToLeft)' Résultats Co Net VB Co

La propriété Options de l'instance Regex contient l'information de l'option passée au constructeur.

Reg = New Regex(Modele, RegexOptions.IgnoreCase) Console.WriteLine(Reg.Options.ToString) ' Affiche IgnoreCase

RightToLeft La propriété RightToLeft de l'instance Regex contient la valeur True ou False selon que l'option RightToLeft a été passée au constructeur ou non.

Reg = New Regex(Modele, RegexOptions.RightToLeft) Console.WriteLine(Reg.RightToLeft.ToString) ' Affiche True

IsMatch Cette méthode exécute une recherche et retourne la valeur True si une occurrence du modèle a été trouvée, et False dans le cas contraire. Chaine = "C:\Cours\VBNet\COOL\" Modele = "VB|Co" Reg = New Regex(Modele)

Console.WriteLine(Reg.IsMatch(Chaine).ToString) ' Affiche True Match La méthode Match d'une instance de Regex réalise la recherche et la lecture de la première occurrence du modèle dans la chaîne traitée. Chaine = "C:\Cours\VBNet\COOl\" Modele = "VB|CO" Reg = New Regex(Modele)

Page 169: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 23

Console.WriteLine(Reg.Match(Chaine).ToString) ' Affiche VB Reg = New Regex(Modele, RegexOptions.RightToLeft)

Console.WriteLine(Reg.Match(Chaine).ToString) ' Affiche CO Matches La méthode Matches d'une instance de Regex réalise la recherche et la lecture de toutes occurrences du modèle dans la chaîne traitée et retourne ses résultats dans une collection de type MatchCollection.

Dim Resultats As MatchCollection Chaine = "C:\Cours\VBNet\Cool\" Modele = "VB|Co"

Reg = New Regex(Modele) Resultats = Reg.Matches(Chaine) For i = 0 To Resultats.Count - 1

Console.WriteLine(Resultats(i).Value) ' Affiche Co VB Co Next

Chaque élément de la collection dispose de membres utiles dont Count, NextMatch, Groups, Groups.Count, Index et Groups.Index.

Count Cette propriété contient bien entendu le nombre de réponses stockées dans la MatchCollection. A la suite du code précédent :

Console.WriteLine(Resultats.Count) ' Affiche 3

NextMatch Cette méthode donne accès à l'élément suivant celui désigné dans une MatchCollection. Ainsi,

les deux lignes de codes suivantes produisent le même affichage. A la suite du code précédent :

Console.WriteLine(Resultats(2)) ' Affiche Co Console.WriteLine(Resultats(1).NextMatch) ' Affiche Co

Groups Cette propriété contient la collection des sous groupes de chaque élément de la MatchCollection.

Toujours à la suite du code précédent :

For i = 0 To Resultats.Count - 1 Console.WriteLine(Resultats(i).Groups.Count) ' Affiche 1 1 1

Next For i = 0 To Resultats.Count - 1

For j = 0 to Resultats(i).Groups.Count - 1 Console.WriteLine(Resultats(i).Groups(j)) ' Affiche Co VB Co Next

Next Chaine = "C:\Cours\VB\Cool\VB\Cool" Modele = "(VB)\\(Cool)\\\1\\\2" Reg = New Regex(Modele) Resultats = Reg.Matches(Chaine) Console.WriteLine(Resultats.Count) ' Affiche 1 Console.WriteLine(Resultats(0)) ' Affiche VB\Cool\VB\Cool Console.WriteLine(Resultats(0).Groups.Count) ' Affiche 3 For i = 0 To Resultats(0).Groups.Count - 1

Console.WriteLine(Resultats(0).Groups(i)) ' Affiche VB\Cool\VB\Cool VB Cool Next

Index Chaque élément stocké dans une MatchCollection, ou dans une collection de sous groupes,

possède une propriété Index qui conserve la position dans sa chaîne d'origine du modèle trouvé. En modifiant les sorties du code précédent, on obtient :

Console.WriteLine(Resultats(0).Index) ' Affiche 9 For i = 0 To Resultats(0).Groups.Count - 1 Console.WriteLine(Resultats(0).Groups(i).Index) ' Affiche 9 9 12 Next

Page 170: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 24

Replace La méthode Replace d'une instance de Regex réalise la recherche de toutes occurrences du modèle dans la chaîne traitée et retourne une autre chaîne dans laquelle ces occurrences ont été remplacées par une sous chaîne donnée. Dim Chaine As String Dim Modele As String Dim Resultat As String Dim Reg As Regex Chaine = "C:\Cours\VBNet\Cool\" Modele = "VB|Co+" Reg = New Regex(Modele) Resultat = Reg.Replace(Chaine, "XX") Console.WriteLine(Resultat) ' Affiche C:\XXurs\XXNet\XXl\

Chaine = "Autant en emporte le vent" Modele = "en.*\bv" Reg = New Regex(Modele) Resultat = Reg.Replace(Chaine, "d'arg") Console.WriteLine(Resultat) ' Affiche Autant d'argent Split La méthode Split d'une instance de Regex est tout à fait semblable à celle de la classe String, mais celle-ci permet la définition du modèle par l’usage des codes des expressions régulières et de leurs métacaractères. Les sous chaînes extraites par la méthode sont copiées dans un tableau de chaînes. Dim Chaine As String Dim Resultats() As String ' Tableau des résultats Dim Reg As Regex Dim Modele As String = "\W" Chaine = "AZERTY, QWERTY sont des mots comme : Visual Studio; VB" Reg = New Regex(Modele) Resultats = Reg.Split(Chaine) For Each S As String In Resultats Console.WriteLine(S) Next

Affichage quand Modele est "\W" : AZERTY QWERTY sont des mots comme Visual Studio VB

Affichage quand Modele est "\W{1,}" : AZERTY QWERTY sont des mots comme Visual Studio VB

Page 171: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 25

La classe Math La classe Math fournit des constantes et des méthodes pour les fonctions trigonométriques, logarithmiques et autres fonctions mathématiques courantes. Propriétés Les deux propriétés Pi et E sont en fait des constantes publiques. Elles ne peuvent donc pas être modifiées. Pi Contient la valeur de la constante de même nom : 3.14159265358979 E Contient la valeur de la constante de même nom, la base des logarithmes naturels : 2.71828182845905 Méthodes Voici l’énumération des méthodes de la classe Math. Le lecteur intéressé aura à cœur de consulter l'aide de son Visual Studio et de pratiquer les expérimentations nécessaires. Abs Retourne la valeur absolue d'un nombre spécifié.

Acos Retourne l'angle dont le cosinus est le nombre spécifié.

Asin Retourne l'angle dont le sinus est le nombre spécifié.

Atan Retourne l'angle dont la tangente est le nombre spécifié.

Atan2 Retourne l'angle dont la tangente est le quotient de deux nombres spécifiés.

BigMul Génère le produit intégral de deux nombres 32 bits.

Ceiling Retourne le plus petit nombre entier supérieur ou égal au nombre spécifié.

Cos Retourne le cosinus de l'angle spécifié.

Cosh Retourne le cosinus hyperbolique de l'angle spécifié.

DivRem Retourne le quotient de deux nombres, en passant le reste en tant que paramètre de sortie.

Exp Retourne e élevé à la puissance spécifiée.

Floor Retourne le plus grand nombre entier inférieur ou égal au nombre spécifié.

IEEERemainder Retourne le reste de la division d'un nombre spécifié par un autre.

Log Retourne le logarithme d'un nombre spécifié.

Log10 Retourne le logarithme de base 10 d'un nombre spécifié.

Max Retourne le plus grand de deux nombres spécifiés.

Min Retourne le plus petit de deux nombres.

Pow Retourne un nombre spécifié élevé à la puissance spécifiée.

Round Retourne le nombre le plus proche de la valeur spécifiée.

Sign Retourne une valeur indiquant le signe d'un nombre.

Sin Retourne le sinus de l'angle spécifié.

Sinh Retourne le sinus hyperbolique de l'angle spécifié.

Sqrt Retourne la racine carrée d'un nombre spécifié.

Tan Retourne la tangente de l'angle spécifié.

Tanh Retourne la tangente hyperbolique de l'angle spécifié.

Pour rappel, en informatique les angles sont toujours exprimés en Radians. Une mesure d'angle exprimée en Degrés doit être multipliée par Pi et divisée par 180 pour exprimer la même mesure en Radians. La classe VBMath La classe VBMath appartient à l’espace de noms Microsoft.VisualBasic et fournit les fonctions Randomize et Rnd utiles à la génération de nombres aléatoires. La ligne de code suivante affiche un nombre compris entre 1 et 100.

Console.WriteLine(CType(100 * Microsoft.VisualBasic.VBMath.Rnd() + 1, Integer))

Page 172: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 26

L'espace de nom System.Drawing L'espace de noms System.Drawing donne accès aux fonctionnalités graphiques de base du dessin vectoriel et contient d'autres espaces de noms tels que System.Drawing.Drawing2D, System.Drawing.Imaging et System.Drawing.Text qui fournissent des fonctionnalités plus sophistiquées. Il contient aussi l'espace de noms Printing déjà utilisé pour réaliser l'impression de documents. Toutes les fonctionnalités offertes par les différentes classes de System.Drawing ne sont pas explorées dans ces pages. Seul l’emploi de la classe Graphics est illustré ici par une petite application qui dessine à l'écran le graphe d'une fonction mathématique. Le lecteur intéressé par la programmation du graphisme peut bien entendu consulter l'aide de son VB.Net à propos de cet espace de noms, mais aussi les différents articles accessibles par une recherche sur GDI+ (Graphics Device Interface). Le GDI+ est la partie du système d'exploitation qui fournit les graphismes vectoriels à deux dimensions, les images et la typographie. La classe Graphics La classe Graphics fournit des méthodes de dessin sur le périphérique d'affichage. Dans l'exemple qui suit, la méthode DrawLine couvre l'essentiel des besoins. Comme elle le serait sur une feuille quadrillée, la fonction à l’écran est représentée par une série de points lesquels sont éventuellement joints par des segments de droites. Les coordonnées de chaque point sont constituées de son abscisse, qui est une des valeurs de X choisie dans la plage explorée, et de son ordonnée qui est la valeur Y calculée par l'application de la fonction sur la valeur de X. Le graphe de la fonction peut être plus ou moins dense selon la façon de choisir les valeurs de X. Par exemple, une fonction f(X) = 2.X + 1 peut être explorée avec des valeurs de X variant de 1 à 9 au pas de 2 ou au pas de 0.5. Avec le pas de 2, on calcule une valeur de Y pour chacune des valeurs de X : 1, 3, 5, 7, 9, soit 5 points. Avec le pas de 0.5, on calcule une valeur de Y pour chacune des valeurs de X : 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8, 8.5, 9, soit 17 points. Les coordonnées obtenues dans un cas et dans l'autre sont représentées par les paires XY suivantes : X 1 3 5 7 9 Y 3 7 11 15 19

X 1 1.5 2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5 8 8.5 9 Y 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

Le résultat du calcul des valeurs de Y par la variation des valeurs de X au pas de 2 est représenté ci contre par le graphe de gauche, tandis que celui de droite représente la même fonction, mais avec des valeurs de Y calculées pour chaque valeur de X obtenue au pas de 0.5. Bien sûr, comme le graphe de la fonction 2.X + 1 est une droite, les deux paires obtenues à partir des valeurs extrêmes de X sont suffisantes pour représenter correctement son graphe. Il en est autrement avec d'autres fonctions. Ainsi par exemple, la fonction trigonométrique f(X) = Sin(X) étudiée pour une gamme de valeurs de X s'étalant de -Pi à +Pi, peut aussi bien être explorée pour des valeurs de X variant au pas de Pi, ou au pas de Pi/2, ou de Pi/4, …, mais le graphe obtenu ne suggérera une sinusoïde que pour un pas suffisamment fin. Ci contre, le graphe du haut est le tracé de f(X) = Sin(X) pour X variant de -Pi à +Pi au pas de Pi/2. Cinq points ont été définis. Le graphe du milieu représente la même fonction, mais il est calculé cette fois pour chaque valeur de X obtenue au pas de Pi/4. Neuf points ont été définis. Enfin, en bas, le graphe a été calculé pour chaque valeur de X obtenue au pas de Pi/10. Vingt et un points ont été définis. Alors que le tracé en cinq points ne suggère guère la sinusoïde, le tracé en vingt et un points la représente parfaitement.

Page 173: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 27

0 X

Y

Par ailleurs, un pas exagérément fin produit des valeurs qui ne peuvent être représentées faute d'une précision suffisante des outils de traçage. Cela est déjà perceptible par la comparaison des graphes de la fonction 2.X + 1 calculés pour des valeurs de X variant au pas de 2 et au pas de 0.5. Les points de ce dernier graphe sont beaucoup plus proches les uns des autres. Si les valeurs de X étaient obtenues au pas de 0.01, il ne serait plus possible de distinguer un point d'un autre. De plus, il serait impossible de les tracer à l'aide d'une latte d'écolier, par exemple. Il y a donc tout intérêt à calculer le graphe d'une fonction sur base d'un échantillonnage fin des valeurs de X, mais sans toutefois atteindre une précision qui n'est plus physiquement représentable. L'unité de dessin à l'écran est le pixel. Il est inutile de calculer 200 points si on ne dispose que de 100 pixels pour les représenter. Par contre, n'en calculer que 50 aura pour effet de n'utiliser qu'un pixel sur deux et donc, de produire un graphe moins précis. C'est donc le nombre de pixels alloués horizontalement à un graphe qui détermine le meilleur pas d'échantillonnage des valeurs de X. Le nombre de valeurs de X à représenter est la valeur absolue de la différence des bornes de X, augmentée de 1 : NbValeurX = |XMax - Xmin| + 1. Cette valeur doit être utilisée pour calculer le pas de découpe de la plage des X en fonction du nombre de pixels. Le pas correspond au nombre de valeurs de X à représenter divisé par le nombre de pixels : PasX = NbValeurX / Largeur. Exemples :

Plage des X Unités de la plage Calcul de NbValeurX

NbValeurX PasX, pour une largeur de 500 pixels

-6 à 2 -6, -5, -4, -3, -2, -1, 0, 1, 2 |2 - -6| + 1 9 0.018 -6 à -2 -6, -5, -4, -3, -2 |-2 - -6| + 1 5 0.01 -2 à 6 -2, -1, 0, 1, 2, 3, 4, 5, 6 |-2 - 6| + 1 9 0.018

-1000 à 1000 -1000, -999, …, 999, 1000 |1000 - -1000| + 1 2001 4.002 -6.28 à +6.28 -6, -5, …, 0, 1, …, 5, 6 |6.28 - -6.28| + 1 13.56 0.02712

Les valeurs de Y sont déterminées par l'application de la fonction sur chacune des valeurs de X. L'écart entre deux valeurs de Y ne peut donc être fixé arbitrairement, mais l'amplitude du tracé peut être mise à l'échelle de l'espace disponible de sorte à l'occuper pleinement. Cet espace disponible pour la représentation des valeurs de Y est délimité par le nombre de pixels alloués verticalement au graphe. Attention toutefois que certaines fonctions apparaissent déformées lorsque les graphes sont réalisés dans des systèmes d'axes aux échelles différentes. Par exemple, les trois graphes ci contre ont été calculés avec les mêmes valeurs de X par la même fonction, et pourtant ils ont des allures bien différentes. Celui de gauche est tracé dans une zone graphique de 50 sur 200 pixels, celui du milieu dans une zone de 200 sur 50 pixels, et celui de droite dans une zone de 200 sur 200. Par ailleurs, le système de coordonnées de l’écran commence dans le coin supérieur gauche de la feuille active et son axe Y est de sens contraire à celui du système de coordonnées ordinaire des mathématiques. En mathématique, l’axe Y étale sa partie positive vers le haut. C’est pourquoi chaque Y calculé par un programme donne une valeur d’affichage Y’ valant .Height - Y.

Une valeur Y = 0 donne Y’ = .Height – 0, soit .Height, et cette valeur est bien représentée en bas de la zone d’affichage, à l’endroit du Y=0 mathématique.

Page 174: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 28

Les considérations précédentes à propos de la programmation de tracés de fonctions mathématiques, ont mis en évidence des outils utiles pour la détermination des valeurs de X. Il faut maintenant calculer le facteur d'amplitude de Y et la formule de sa représentation compte tenu de l'inversion de l'axe des Y. Le facteur d'amplitude est le rapport d'échelle entre la plage de pixels verticaux et la plage des Y calculés, soit FacteurY = Hauteur / NbValeurY. Il faut donc calculer NbValeurY et, en procédant comme pour la plage des X, on obtient la formule NbValeurY = |YMax - YMin| + 1. Pour la position à l'écran de la valeur de Y ajustée par son facteur d'amplitude, il faut encore corriger l'erreur induite par l'inversion de l'axe Y. Puisque le facteur d'amplitude garantit une représentation de chaque Y' proportionnelle à la valeur Y correspondante, on peut écrire : ( Hauteur - Y' ) / Hauteur = | YMin - Y | / NbValeurY ( Hauteur - Y' ) = | YMin - Y | / NbValeurY * Hauteur ( Hauteur - Y' ) = | YMin - Y | * Hauteur / NbValeurY ( Hauteur - Y' ) = | YMin - Y | * FacteurY - Y' = | YMin - Y | * FacteurY - Hauteur Y' = Hauteur - | YMin - Y | * FacteurY Comme réalisé dans cette dernière formule, la valeur de chaque Y calculé doit être convertie en distance par rapport à YMin, cette distance doit subir le facteur d'amplitude et le résultat doit être soustrait de la coordonnée Y du bas de la plage graphique pour donner la position de Y' sur cette plage. Enfin, pour finaliser ces formules, il faut encore déterminer les valeurs extrêmes de Y. Pour cela, le plus simple est de calculer toutes les valeurs de Y compte tenu du pas d'échantillonnage des valeurs de X et d'attraper au passage le plus petit et le plus grand des résultats.

YMin = Fonction(XMin) ' Supposons que f(XMin) donne le plus petit Y YMax = Fonction(XMax) ' Supposons que f(XMax) donne le plus grand Y For X = XMin To XMax Step PasX ' Echantillonnge des X au pas de PasX Y = Fonction(X) ' Calcul de l'Y correpondant If Y < YMin Then YMin = Y ' Si un Y est plus petit, on change If Y > YMax Then YMax = Y ' Si un Y est plus grand, on change Next

Il faut encore savoir si le tracé des axes X et Y est opportun. La présence de l'axe X est utile si la valeur 0 existe dans la plage des Y et celle de l'axe Y est utile si la valeur 0 existe dans la plage des X. Dans un cas comme dans l'autre, le produit des valeurs extrêmes donne un résultat négatif si la valeur 0 existe. Presque tous les ingrédients sont en place :

NbValeurX = |XMax - Xmin| + 1 ' Plage des X PasX = NbValeurX / Largeur ' Pas d'échantillonnage des X.

' Largeur correspond à Width NbValeurY = |YMax - YMin| + 1 ' Plage des Y FacteurY = Hauteur / NbValeurY ' Facteur d'amplitude des Y ' Hauteur correspond à Height Y' = Hauteur - | YMin - Y | * FacteurY ' Y' est la position de Y à l'écran.

If XMin * XMax < 0 Then … ' Tracer l'axe Y à l'écran. If YMin * YMax < 0 Then … ' Tracer l'axe X à l'écran.

0

Height

YMax

YMin

Facteur d'amplitude : FacteurY

La fonction calcule un Y à représenter à

l'écran

L'écran doit positionner un Y' en rapport avec

le résultat de la fonction

Hau

teur

NbV

aleu

rY

Y Y'

Page 175: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 29

Le dessin à l'écran se réalise à l'aide de segments de droite. Les coordonnées de départ du premier segment sont données par XMin et f(XMin), les coordonnées de l'extrémité de ce segment sont données par XMin + PasX et f(XMin + PasX). Ces coordonnées de l'extrémité du premier segment deviennent ensuite celles de départ du segment suivant. Ce processus est répété jusqu'à la fin du dernier segment. Dans le programme suivant, la fonction utilisée pour l'exemple est Y = Sinus(X). Elle est enfermée à l'intérieur d'une fonction nommée Fonction. L'utilisateur qui souhaite expérimenter une autre fonction doit en écrire le code à l'intérieur de Fonction et poser des bornes XMin et XMax cohérentes. Dans cet exemple, l’espace graphique utilisé est la surface d’un Panel de 400 pixels sur 400, nommé Graphe.

Imports System.Drawing ' Pour disposer de la classe Graphics Imports System.Math ' Pour disposer de Pi, Sin() et Abs() Private Function Fonction(ByVal x As Single) As Single Return Sin(x) End Function Dim MG As Graphics Dim NbValeurX, NbValeurY As Single ' Les plages de valeurs Dim XMin, XMax, YMin, YMax as Single ' Les valeurs extrêmes Dim PasX, FacteurY As Single ' Pas et facteur Dim X, Y As Single ' Le nécessaire au calcul Dim Xe, Largeur, Hauteur As Single ' Comptage des X "écran" et nombres de pixels Dim XDepart, YDepart As Single ' Coordonnées de départ à l'écran Dim XEcran, YEcran As Single ' Coordonnées de l'extrémité du segment Dim XEcrPrec, YEcrPrec As Single ' Coordonnées du début du segment suivant Graphe.BackColor = Drawing.Color.White ' Pour dessiner sur fond blanc Graphe.Refresh() ' Obliger le form à se redessiner MG = Graphe.CreateGraphics() ' Instanciation de l'objet Graphics ' Position et dimensions du tracé avec une marge de 5 pixels tout autour XDepart = 5 Largeur = Graphe.Width - 10 YDepart = Graphe.Height - 5 Hauteur = Graphe.Height - 10 ' Limites des X (idéalement à encoder par l'utilisateur) XMin = -2 * PI XMax = 2 * PI ' Mettre les bornes dans le bon ordre (en cas de mauvais encodage) If XMin > XMax Then Dim tmp As Single tmp = XMin XMin = XMax XMax = tmp End If ' Nombre d'unités de la plage des X NbValeurX = Abs(XMax - XMin) + 1 ' Calcul du pas d’échantillonnage des X PasX = NbValeurX / Largeur ' Limites des Y YMin = Fonction(XMin) YMax = Fonction(XMax) For X = XMin To XMax Step PasX Y = Fonction(X) If Y < YMin Then YMin = Y If Y > YMax Then YMax = Y Next

Page 176: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 30

' Nombre d'unités de la plage des Y NbValeurY = Abs(YMax - YMin) + 1 ' Calcul du facteur d'amplitude pour Y FacteurY = Hauteur / NbValeurY ' Tracer de l'axe Y si 0 entre XMin et XMax (en noir) If XMin * XMax < 0 Then XEcran = XDepart + Largeur * Abs(XMin) / NbValeurX MG.DrawLine(Drawing.Pens.Black, XEcran, YDepart, XEcran, YDepart - Hauteur) End If ' Tracer de l'axe X si 0 entre YMin et YMax (en noir) If YMin * YMax < 0 Then YEcran = YDepart - Abs(YMin) * FacteurY MG.DrawLine(Drawing.Pens.Black, XDepart, YEcran, XDepart + Largeur, YEcran) End If ' Tracer le graphe (en bleu) X = XMin Y = Fonction(X) XEcrPrec = XDepart YEcrPrec = YDepart - Abs(YMin - Y) * FacteurY ' YDepart correspond à Height, For Xe = 1 To Largeur ' donc à Hauteur (à la marge près) X = X + PasX Y = Fonction(X) XEcran = XDepart + Xe YEcran = YDepart - Abs(YMin - Y) * FacteurY MG.DrawLine(Drawing.Pens.Blue, XEcrPrec, YEcrPrec, XEcran, YEcran) XEcrPrec = XEcran YEcrPrec = YEcran Next

Et voilà le résultat :

Page 177: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 31

L'espace de noms System.Collections Une collection regroupe un ensemble d'objets quelconques, comme le fait un tableau, mais avec une notion d'indice moins marquée. Il est ordinaire d'ajouter et de retirer des éléments d'une collection sans en connaître la position. Tous les tableaux sont dérivés d'une classe Array et les collections sont dérivées de celles proposées dans System.Collections. Un exemple trivial de différence entre le tableau et la collection réside dans le moyen de connaître le nombre d'éléments : il est donné par la propriété Length d'un tableau et par la propriété Count d'une collection. La collection peut contenir des objets de types originels différents (entiers, chaînes, tableaux, autres collections, objets divers, …) alors que tous les éléments d'un tableau sont d'un même type. De plus, contrairement aux tableaux, le programmeur doit rarement se soucier de la taille d'une collection. L'espace de noms System.Collections fournit quelques classes directement opérationnelles ainsi qu'une classe CollectionBase offrant les outils de base d'une collection et permettant par héritage la construction de toute autre collection. Enfin, l’espace de noms System.Collections possède un sous espace System.Collections.Generic qui propose plusieurs sortes de collections pouvant être instanciées pour constituer une alternative à l’usage des tableaux. En effet, comme c’est le cas des tableaux, ces collections doivent être instanciées pour un type prédéfini de données. Elles permettent donc d’allier les avantageuses fonctionnalités des collections à la rigueur des structures fortement typées. L’espace de noms System.Collections.Generic est une nouveauté depuis VS 2005. Les classes offertes

ArrayList C'est la plus simple des collections. L'accès à un élément se fait essentiellement par son index. BitArray Il s'agit d'un tableau de valeurs booléennes stockées à raison d'un bit par valeur. Une valeur est

accessible par son index. Hashtable C'est une collection de paires objet-clé. L'accès à un élément se fait essentiellement par sa clé. SortedList Il s'agit d'une version améliorée de la Hashtable dans laquelle les éléments sont triés selon la

valeur de leur clé. Queue Comme son nom l'indique, il s'agit d'une collection dans laquelle on ajoute ou retire un élément

selon le principe de la file (FIFO : premier entré, premier sorti). Stack Comme son nom l'indique, il s'agit d'une collection dans laquelle on ajoute ou retire un élément

selon le principe de la pile (LIFO : dernier entré, premier sorti). Membres communs à la plupart des collections Les exemples de programmations relatifs aux membres communs illustrent dans les pages suivantes, l'étude des classes énumérées ci-dessus. Propriétes

Count Nombre d'éléments de la collection. IsFixedSize Cette propriété possède la valeur True ou False selon que la collection est de taille fixe ou non. IsReadOnly Cette propriété possède la valeur True ou False selon que la liste est en lecture seul ou non. Item L'Item fournit l'élément dont on lui a indiqué l'index ou la clé.

Méthodes

Add Ajoute un élément à une collection. Clear Vide la collection de tout son contenu. Clone Crée une copie partielle d'une collection. Contains Indique si un élément donné est présent dans la collection. CopyTo Copie partielle ou totale d'une collection dans un tableau à une dimension.

Page 178: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 32

La classe ArrayList Propriété Capacity Cette propriété représente le nombre d'éléments que l'ArrayList est capable de stocker. Il ne faut pas confondre cette propriété avec Count qui représente le nombre réel d'éléments stockés. La valeur de Capacity est toujours supérieure ou égale à Count. Si Count excède Capacity lors de l'ajout d'éléments, la capacité de la liste est doublée par une réallocation automatique du tableau interne. Méthodes AddRange Ajout d'une collection ArrayList à la fin de l'ArrayList. Dim A As New ArrayList ' Instanciation d'un ArrayList Dim B As New ArrayList Dim I As Integer Dim S As String Dim Obj As Object I = 5 S = "AZERTY" A.Add(I) ' Stocke un entier dans A Console.WriteLine("A " & A.Item(0)) ' Affiche A 5 A.Add(S) ' Stocke une chaîne dans A Console.WriteLine("A " & A.Item(1)) ' Affiche A AZERTY B.Add(I * 2) ' Stocke un entier dans B Console.WriteLine("B " & B.Item(0)) ' Affiche B 10 B.Add("B" & S) ' Stocke une chaîne dans B Console.WriteLine("B " & B.Item(1)) ' Affiche B BAZERTY A.AddRange(B) ' Stocke l' ArrayList B dans A For Each Obj In A Console.WriteLine("A = " & Obj) ' Affiche A = 5 Next ' A = AZERTY ' A = 10 ' A = BAZERTY GetType Obtient le type de toute instance ou d'un seul élément. Console.WriteLine(B.GetType) ' Affiche System.Collections.ArrayList Console.WriteLine(B.Item(1).GetType) ' Affiche System.String Insert Insère un élément dans ArrayList à l'index spécifié et provoque le déplacement de tous les autres éléments d'une position vers la fin. B.Insert(1, "Insertion") Console.WriteLine(B.Item(0)) ' Affiche 10 Console.WriteLine(B.Item(1)) ' Affiche Insertion Console.WriteLine(B.Item(2)) ' Affiche BAZERTY

Page 179: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 33

InsertRange Insère les éléments d'une collection ArrayList à l'index spécifié et provoque le déplacement de tous les autres éléments vers la fin.

' Contenu de A avant InsertRange : 5 AZERTY 10 BAZERTY A.InsertRange(1, B)

' Contenu de A après InsertRange : 5 10 Insertion BAZERTY AZERTY 10 BAZERTY

IndexOf Retourne l'index de base zéro de la première occurrence d'une valeur dans ArrayList ou dans une partie de celui-ci, ou -1 en cas d'échec de la recherche. Console.WriteLine(A.IndexOf(10)) ' Affiche 1 LastIndexOf Retourne l'index de base zéro de la dernière occurrence d'une valeur dans ArrayList ou dans une partie de celui-ci, ou -1 en cas d'échec de la recherche. Console.WriteLine(A.LastIndexOf(10)) ' Affiche 5 Remove Supprime la première occurrence d'un objet spécifique de ArrayList et provoque le déplacement de tous les éléments suivants d'une position vers le début.

' Contenu de A avant Remove : 5 10 Insertion BAZERTY AZERTY 10 BAZERTY A.Remove("BAZERTY")

' Contenu de A après Remove : 5 10 Insertion AZERTY 10 BAZERTY RemoveAt Supprime l'élément au niveau de l'index spécifié de ArrayList et provoque le déplacement de tous les éléments suivants d'une position vers le début.

' Contenu de A avant RemoveAt : 5 10 Insertion AZERTY 10 BAZERTY A.RemoveAt(4)

' Contenu de A après RemoveAt : 5 10 Insertion AZERTY BAZERTY RemoveRange Supprime un nombre d'éléments donné de ArrayList à partir de l'index également donné et provoque le déplacement de tous les éléments suivants vers le début. La syntaxe est RemoveRange(Index, Nombre).

' Contenu de A avant RemoveRange : 5 10 Insertion AZERTY BAZERTY A.RemoveRange(1, 3)

' Contenu de A après RemoveRange : 5 BAZERTY Repeat Initialise une ArrayList en recopiant un élément donné un nombre de fois également donné. L'ArrayList est préalablement vidée de son contenu. La syntaxe est Repeat(Objet, Quantité).

' Contenu de A avant Repeat : 5 10 Insertion AZERTY BAZERTY A = ArrayList.Repeat(0, 3)

' Contenu de A après Repeat : 0 0 0

Page 180: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 34

Reverse Inverse l'ordre des éléments dans ArrayList ou dans une partie de celui-ci. La syntaxe est Reverse() ou Reverse(Index, Nombre).

' Contenu de A avant Reverse : 5 10 Insertion AZERTY BAZERTY A.Reverse()

' Contenu de A après Reverse : BAZERTY AZERTY Insertion 10 5 Sort Trie les éléments dans ArrayList. Si tous les éléments de la collection sont des objets ou s'ils appartiennent à des types de bases différents, il faut indiquer la fonction de comparaison à utiliser. Ce point sera abordé plus tard. Si les éléments sont tous d'un même type de base, la syntaxe est Sort(). SetRange Copie les éléments d'une collection sur une plage d'éléments d'un ArrayList à partir d'un index donné. Cette opération écrase les éléments éventuellement existants. La syntaxe est SetRange(Index, Collection).

' Contenu de A avant SetRange : 5 10 15 20 17 25 30 ' Contenu de B avant SetRange : BA BX BW

A.SetRange(2, B) ' Contenu de A après SetRange : 5 10 BA BX BW 25 30 ' Contenu de B après SetRange : BA BX BW

CopyTo Copie les éléments de ArrayList vers un tableau unidimensionnel. Ceci n'est possible que si tous les éléments de la collection et le tableau cible sont de même type. Ainsi, s'il faut copier l'ArrayList A de l'exemple précédent dans un tableau de chaînes, il faut d'abord procéder à la conversion des valeurs numériques en chaînes. Au programmeur de savoir quelles sont les conséquences de cette conversion de la collection dans son application. Dim Resultat(10) As String ' … For I = 0 To A.Count - 1 A.Item(I) = A.Item(I).ToString Next A.CopyTo(Resultat) Clone Copie les éléments d'un ArrayList vers un autre. Cette opération écrase les éléments éventuellement existants dans l'ArrayList cible. La méthode Clone ne pas copie pas les objets référencés, mais seulement leurs références. Après la copie, une référence d'une collection et son homologue de l'autre pointent toutes les deux la même instance. A.Add(0) ' Stocke l'entier 0 dans A Dim T = New Integer() {1, 2} ' Crée un tableau T(0) = 1 et T(1) = 2 A.Add(T) ' Stocke le tableau T dans A B = A.Clone() ' Copie de A sur B Console.WriteLine("A = " & A.Item(0)) ' Affiche A = 0 Console.WriteLine("A = " & A.Item(1)(0)) ' Affiche A = 1 Console.WriteLine("A = " & A.Item(1)(1)) ' Affiche A = 2 Console.WriteLine("B = " & B.Item(0)) ' Affiche B = 0 Console.WriteLine("B = " & B.Item(1)(0)) ' Affiche B = 1 Console.WriteLine("B = " & B.Item(1)(1)) ' Affiche B = 2 T(1) = 99 ' Change T(1) de 2 en 99 Console.WriteLine("A = " & A.Item(0)) ' Affiche A = 0 Console.WriteLine("A = " & A.Item(1)(0)) ' Affiche A = 1 Console.WriteLine("A = " & A.Item(1)(1)) ' Affiche A = 99 Console.WriteLine("B = " & B.Item(0)) ' Affiche B = 0 Console.WriteLine("B = " & B.Item(1)(0)) ' Affiche B = 1 Console.WriteLine("B = " & B.Item(1)(1)) ' Affiche B = 99

Page 181: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 35

ToArray Copie les éléments de ArrayList vers un nouveau tableau de type Object. Dim T(3) As Object A.Add(0) A.Add(1) A.Add("AZE") T = A.ToArray() For Each Obj In T Console.WriteLine("T = " & Obj) ' Affiche 0 1 AZE Next Contains Cette méthode retourne True si l'objet désigné est présent dans la collection et False dans le cas contraire. Console.WriteLine(A.Contains("AZE").ToString) ' Affiche True La classe BitArray Une instance de BitArray est une collection d'un nombre prédéterminé de valeurs booléennes. Cette collection a peu de points communs avec les autres mais possède néanmoins les membres déjà étudiés Count, IsReadOnly, GetType, Clone, CopyTo, Equals et ToString. Propriété Length Cette propriété représente le nombre d'éléments que BitArray est capable de stocker. Elle est identique à la propriété Count, sauf que cette dernière est en lecture seule, tandis que Length peut être affectée en cours d'exécution pour modifier la taille de la collection. Méthodes Set et SetAll La collection BitArray ne possède pas de méthode Add du fait que tous ses éléments existent dès son instanciation. Il est toutefois nécessaire d'affecter les valeurs des éléments selon les impératifs des applications. C'est ce que permettent les méthodes Set et SetAll. Il est également possible d'affecter un élément désigné par son index et la propriété Item. Dim A As New BitArray(8) ' Instanciation d'un BitArray A.SetAll(False) ' Initialise tout à False A.Set(0, True) ' Initialise à True à l'index 0 A.Item(6) = True ' Initialise à True à l'index 6 Get Méthode de lecture d'un élement, Get retourne True ou False selon la valeur de l'élément à un index donné. La lecture de la propriété Item produit le même résultat. Console.WriteLine(A.Get(0).ToString) ' Affiche True Console.WriteLine(A.Item(1).ToString) ' Affiche False Not La méthode Not inverse toutes les valeurs logiques du BitArray. Le résultat peut être copié dans un autre BitArray. Dim A As New BitArray(8) Dim B As New BitArray(8)

Page 182: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 36

A.SetAll(False) B = A.Not() ' Les éléments de A et B sont True And, Or et Xor Ces méthodes exécutent les opérations logiques correspondantes entre deux BitArray et retournent le résultat dans un autre BitArray. C = A.And(B) C = A.Or(B) C = A.Xor(B) La classe Hashtable Dim A As New Hashtable ' Instanciation d'un Hashtable A.Add("AZ", "AZERTi") ' Ajout d'éléments dans A A.Add("QW", "QWERTY") A.Add("PA", "PANZANI") For Each Obj As Object In A.Keys Console.WriteLine(Obj.ToString) ' Affichage AZ QW PA Next For Each Obj As Object In A.Values Console.WriteLine(Obj.ToString) ' Affichage AZERTi QWERTY PANZANI Next A.Item("AZ") = "AZERTY" ' Modification de l'élément de clé AZ Console.WriteLine(A.Item("AZ").ToString) ' Affiche AZERTY Propriétés Item Cette propriété permet la lecture et la modification d'une valeur correspondant à une clé donnée. Si les clés sont numériques et ordonnées de 0 à Count - 1, la propriété peut être utilisée comme dans le cas de l'ArrayList. Mais alors, à quoi bon utiliser la classe Hashtable ? L'usage de cette dernière n'a de sens que lorsqu'on utilise vraiment des clés. A.Item("AZ") = "AZERTY" ' Modification de l'élément de clé AZ Keys Cette propriété contient la collection des clés associées aux valeurs de Hashtable. For Each Obj As Object In A.Keys Console.WriteLine(Obj.ToString) ' Affichage PA QW AZ Next Values Cette propriété contient la collection des valeurs associées aux clés de Hashtable. For Each Obj As Object In A.Values Console.WriteLine(Obj.ToString) ' Affichage PANZANI QWERTY AZERTi Next Méthodes Add Ajoute un élément avec la clé et la valeur spécifiées dans Hashtable. A.Add("AZ", "AZERTi") ' Ajout d'un élément dans A

Page 183: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 37

Contains, ContainsKey et ContainsValue Ces trois méthodes retournent la valeur True si l'élément cherché est présent dans la collection, et la valeur False dans le cas contraire. Les deux premières, Contains et ContainsKey, réalisent la recherche d'un élément parmi les clés de la collection, tandis que ContainsValue réalise la recherche parmi les valeurs. Console.WriteLine(A.Contains("QW")) ' Affiche True Console.WriteLine(A.Contains("PANZANI")) ' Affiche False Console.WriteLine(A.ContainsKey("QW")) ' Affiche True Console.WriteLine(A.ContainsValue("PANZANI")) ' Affiche True Remove Supprime l'élément ayant la clé spécifiée. A.Remove("PA") Console.WriteLine(A.ContainsValue("PANZANI")) ' Affiche False La classe SortedList La classe SortedList présente les mêmes membres que la classe Hashtable et quelques méthodes qui lui sont spécifiques. La particularité de SortedList est de conserver tous les éléments dans l'ordre croissant des clés. Ceci implique que toutes les clés soient du même type. Dim A As New SortedList ' Instanciation d'un SortedList A.Add("AZ", "AZERTi") ' Ajout d'éléments dans A A.Add("QW", "QWERTY") A.Add("PA", "PANZANI") For Each Obj As Object In A.Keys Console.WriteLine(Obj.ToString) ' Affichage AZ PA QW Next For Each Obj As Object In A.Values Console.WriteLine(Obj.ToString) ' Affichage AZERTi PANZANI QWERTY Next A.Item("AZ") = "AZERTY" ' Modification de l'élément de clé AZ Console.WriteLine(A.Item("AZ").ToString) ' Affiche AZERTY Méthodes IndexOfKey Retourne l'index de base zéro de la clé spécifiée ou -1 en cas d'échec de la recherche. Console.WriteLine(A.IndexOfKey("QW")) ' Affiche 2 IndexOfValue Retourne l'index de base zéro de la première occurrence de la valeur spécifiée ou -1 en cas d'échec de la recherche. Console.WriteLine(A.IndexOfValue("PANZANI")) ' Affiche 1 GetByIndex Obtient la valeur à l'index spécifié. Console.WriteLine(A.GetByIndex(2)) ' Affiche QWERTY

Page 184: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 38

GetKey Obtient la clé à l'index spécifié. Console.WriteLine(A.GetKey(1)) ' Affiche PA SetByIndex Remplace la valeur au niveau de l'index spécifié. Console.WriteLine(A.IndexOfValue("PANZANI")) ' Affiche 1 A.SetByIndex(1, "RANZANI") ' Change PANZANI en RANZANI Console.WriteLine(A.GetKey(1)) ' Affiche PA Console.WriteLine(A.Item("PA").ToString) ' Affiche RANZANI RemoveAt Supprime l'élément au niveau de l'index spécifié. A.RemoveAt(1) ' Supprime la paire PA - RANZANI GetKeyList Retourne une collection des clés du SortedList. For Each Obj As Object In A.GetKeyList Console.WriteLine(Obj.ToString) ' Affichage AZ QW Next GetValueList Retourne une collection des valeurs du SortedList. For Each Obj As Object In A.GetValueList Console.WriteLine(Obj.ToString) ' Affichage AZERTY QWERTY Next La classe Queue Un objet Queue est une collection gérée selon le principe de la file (FIFO : premier entré, premier sorti). Méthodes Outre les membres Contains, Count et Clear communs à la plupart des collections, la classe Queue possède trois méthodes qui lui sont spécifiques : Enqueue, Dequeue et Peek. Dim A As New Queue ' Instanciation d'un Queue A.Enqueue("AZERTY") ' Enfilement d'éléments dans A A.Enqueue("QWERTY") A.Enqueue("PANZANI") For Each Obj As Object In A Console.WriteLine(Obj.ToString) ' Affichage AZERTY QWERTY PANZANI Next Enqueue La classe Queue ne possède pas de méthode Add. C'est la méthode Enqueue qui la remplace. Peek La méthode Peek retourne la valeur du premier élément disponible, c'est-à-dire le plus ancien de la collection sans le supprimer de la collection.

Page 185: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 39

Console.WriteLine(A.Count.ToString) ' Affiche 3 Console.WriteLine(A.Peek.ToString) ' Affiche AZERTY Console.WriteLine(A.Count.ToString) ' Affiche encore 3 Console.WriteLine(A.Peek.ToString) ' Affiche encore AZERTY Console.WriteLine(A.Count.ToString) ' Affiche toujours 3 Dequeue La méthode Dequeue retourne la valeur du premier élément disponible, c'est-à-dire le plus ancien de la collection et le supprime de la collection. Console.WriteLine(A.Count.ToString) ' Affiche 3 Console.WriteLine(A.Dequeue.ToString) ' Affiche AZERTY Console.WriteLine(A.Count.ToString) ' Affiche 2 Console.WriteLine(A.Dequeue.ToString) ' Affiche QWERTY Console.WriteLine(A.Count.ToString) ' Affiche 1 La classe Stack Un objet Stack est une collection gérée selon le principe de la pile (LIFO : dernier entré, premier sorti). Méthodes Outre les membres Contains, Count et Clear communs à la plupart des collections, la classe Stack possède trois méthodes qui lui sont spécifiques : Push, Pop et Peek. Dim A As New Stack ' Instanciation d'un Stack A.Push("AZERTY") ' Empilement d'éléments dans A A.Push("QWERTY") A.Push("PANZANI") For Each Obj As Object In A Console.WriteLine(Obj.ToString) ' Affichage PANZANI QWERTY AZERTY Next Push La classe Stack ne possède pas de méthode Add. C'est la méthode Push qui la remplace. Peek La méthode Peek retourne la valeur du premier élément disponible, c'est-à-dire le plus récent de la collection sans le supprimer de la collection. Console.WriteLine(A.Count.ToString) ' Affiche 3 Console.WriteLine(A.Peek.ToString) ' Affiche PANZANI Console.WriteLine(A.Count.ToString) ' Affiche encore 3 Console.WriteLine(A.Peek.ToString) ' Affiche encore PANZANI Console.WriteLine(A.Count.ToString) ' Affiche toujours 3 Pop La méthode Pop retourne la valeur du premier élément disponible, c'est-à-dire le plus récent de la collection et le supprime de la collection. Console.WriteLine(A.Count.ToString) ' Affiche 3 Console.WriteLine(A.Pop.ToString) ' Affiche PANZANI Console.WriteLine(A.Count.ToString) ' Affiche 2 Console.WriteLine(A.Pop.ToString) ' Affiche QWERTY Console.WriteLine(A.Count.ToString) ' Affiche 1

Page 186: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 40

Une dernière comparaison ' ArrayList Dim A As New ArrayList A.Add("AZERTY") A.Add("QWERTY") A.Add("RANZANI") A.Add("PANZANI") For Each Obj As Object In A ' Affichage en ordre d'encodage : Console.WriteLine(Obj.ToString) ' AZERTY QWERTY RANZANI PANZANI Next ' ' BitArray Dim B As New BitArray(4) B.Set(0, True) B.Set(1, True) B.Set(2, False) B.Set(3, True) For Each Obj As Object In B ' Affichage en ordre d'encodage : Console.WriteLine(Obj.ToString) ' True True False True Next ' Hashtable ' Affichage des clés et valeurs en ordre Dim C As New Hashtable ' d’encodage depuis VS 2005, mais en ordre C.Add("AZ", "AZERTY") ' inverse sous VS 2003 … C.Add("QW", "QWERTY") C.Add("RA", "RANZANI") C.Add("PA", "PANZANI") For Each Obj As Object In C.Keys ' Affichage en ordre d'encodage : Console.WriteLine(Obj.ToString) ' AZ QW RA PA Next For Each Obj As Object In C.Values ' Affichage en ordre d'encodage : Console.WriteLine(Obj.ToString) ' AZERTY QWERTY RANZANI PANZANI Next ' SortedList Dim D As New SortedList D.Add("AZ", "AZERTY") D.Add("QW", "QWERTY") D.Add("RA", "RANZANI") D.Add("PA", "PANZANI") For Each Obj As Object In D.Keys ' Affichage en ordre croissant des clés : Console.WriteLine(Obj.ToString) ' AZ PA QW RA Next For Each Obj As Object In D.Values ' Affichage en ordre croissant des clés : Console.WriteLine(Obj.ToString) ' AZERTY PANZANI QWERTY RANZANI Next ' Queue Dim E As New Queue E.Enqueue("AZERTY") E.Enqueue("QWERTY") E.Enqueue("RANZANI") E.Enqueue("PANZANI") For Each Obj As Object In E ' Affichage en ordre d'encodage : Console.WriteLine(Obj.ToString) ' AZERTY QWERTY RANZANI PANZANI Next ' Stack Dim F As New Stack F.Push("AZERTY") F.Push("QWERTY") F.Push("RANZANI") F.Push("PANZANI") For Each Obj As Object In F ' Affichage en ordre inverse de l'encodage : Console.WriteLine(Obj.ToString) ' PANZANI RANZANI QWERTY AZERTY Next

Page 187: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 41

La classe CollectionBase Depuis le début de ce cours, les propriétés et méthodes les plus utilisées des classes les plus exploitées ont été étudiées. Tous ces membres étudiés ont une particularité commune : ils sont tous membres Public des classes qui les exposent. Les programmeurs qui ont conçu ces classes ont bien du utiliser aussi des variables locales et des membres Private ou Friend, mais nul n'en saura jamais rien, à moins que leurs sources soient publiées … . Chaque classe expose également des membres qui n’ont pas été repris dans les énumérations du cours, ce sont les membres Protected. Le programmeur souhaitant dériver des classes du Framework a tout intérêt à consulter l'aide de VB.Net pour connaître les membres Protected dont il peut disposer. Tous les membres Public des classes sont accessibles à partir de toute instance. Les membres Protected quant à eux, ne sont accessibles que par héritage dans des classes dérivées. Une classe dérivée dispose alors des membres Public et des membres Protected de la classe dont elle hérite. La classe CollectionBase expose peu de membres Public, pas assez pour gérer la moindre collection. Elle est spécialement destinée à la création de collections inédites et c'est au programmeur d'implémenter les outils et membres nécessaires. Pour illustrer l'emploi de CollectionBase, une collection MesChaines est créée. Elle présente la particularité de la SortedList de conserver ses éléments en ordre alliée à la simplicité de l'ArrayList. La collection MesChaines ne peut contenir que des objets de type String qui ne sont pas associés à des clés et qui sont toujours dans le bon ordre. Cette programmation qui est illustrative, n'a aucune prétention de performance. Class MesChaines Inherits CollectionBase End Class Avec ce code, la nouvelle classe MesChaines existe mais son exploitation dans du code client reste fort limitée. Elle ne dispose en effet que de quatre méthodes et d'une propriété. Elle ne dispose d'aucun moyen d'ajouter des éléments à la collection, ni d'en extraire. Il faut donc implémenter une méthode Add et une propriété Item, ainsi que les moyens souhaités pour la classe MesChaines. Voici le code complet de cette nouvelle classe. Class MesChaines Inherits CollectionBase ' Héritage de CollectionBase Dim Sens As Integer ' Sens du tri : croissant/décroissant Dim FlgTri As Boolean Public Sub New(Optional ByVal Ordre As Integer = 1) ' Un constructeur est programmé pour Sens = Ordre ' prendre en compte un paramètre End Sub ' indiquant le sens du tri. Public ReadOnly Property SensTri() As String Get If Sens = -1 Then ' La propriété SensTri en ReadOnly Return "D" ' permet au code client de connaître Else ' le paramètrage actuel du tri. Return "C" ' La propriété SensTri vaut D si le End If ' tri est décroissant ou C s'il est End Get ' croissant. End Property Public Property Item(ByVal Index As Integer) As String Get Return MyBase.InnerList(Index) ' La propriété Item permet la lecture End Get ' et l'écriture de l'élément dont Set(ByVal Value As String) ' l'index lui est passé. MyBase.Innerlist(Index) = Value ' Cette propriété gère la propriété If Not FlgTri Then Tri() ' Innerlist reçue par héritage. End Set ' FlgTri empêche l’appel récursif du End Property ' tri quand Item y est modifié.

Page 188: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 42

Public Sub Add(ByVal Obj As String) MyBase.InnerList.Add(Obj) ' La méthode Add permet l'ajout Tri() ' d'un élément à la collection et End Sub ' enclenche le tri. Public Sub ChangeOrdre(Optional ByVal Ordre As Integer = 0) If Ordre < -1 Or Ordre > 1 Then Ordre = 0 If Ordre <> Sens Then ' La méthode ChangeOrdre permet au Sens = Sens * -1 ' code client d'inverser l'ordre du Tri() ' tri. End If End Sub Private Sub Tri() Dim i As Integer ' La procédure privée Tri réalise le Dim Comparaison As Integer ' tri de la collection. Dim Temp As String ' L'algorithme de tri utilisé ici Dim TemoinPermut As Byte ' n'est autre qu'une variante du tri If Count < 2 Then Exit Sub ' à bulles. FlgTri = True Do TemoinPermut = 0 For i = 0 To Count - 2 Comparaison = String.Compare(Item(i + 1), Item(i)) If Comparaison <> Sens And Comparaison <> 0 Then Temp = Item(i) Item(i) = Item(i + 1) ' Il y a ici affectation de la Item(i + 1) = Temp ' propriété Item de la collection. TemoinPermut = 1 End If Next Loop While TemoinPermut = 1 FlgTri = False End Sub End Class Utilisation de la classe MesChaines dans une application cliente : Dim MC As New MesChaines ' Instanciation d'un MesChaines Console.WriteLine(MC.SensTri().ToString) ' Affiche C MC.Add("AZERT") ' Ajout d'un élément dans MC MC.Add("QWERT") MC.Add("RANZA") MC.Add("PANZA") For Each S As String In MC Console.WriteLine(S) ' Affichage AZERT PANZA QWERT RANZA Next MC.ChangeOrdre() ' Inverse l'ordre de tri For Each S As String In MC Console.WriteLine(S) ' Affichage RANZA QWERT PANZA AZERT Next Autre exemple : Dim MC As New MesChaines(-1) ' Instanciation pour tri décroissant Console.WriteLine(MC.SensTri().ToString) ' Affiche D MC.Add("AZERT") ' Ajout d'un élément dans MC MC.Add("QWERT") MC.Add("RANZA") MC.Add("PANZA") MC.Item(1) = "Quartz" ' Change QWERT en Quartz For Each S As String In MC Console.WriteLine(S) ' Affichage RANZA Quartz PANZA AZERT Next

Page 189: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 43

L’espace de noms Collections.Generic L’espace de noms System.Collections.Generic fournit des classes de base pour la mise en exploitation de collections dont les données sont d’un type prédéfini choisi parmi les types standard du Framework. L’utilité évidente de ces classes est d’offrir une alternative à l’usage des tableaux. L’utilisation d’une collection à la place d’un tableau présente notamment comme avantage d’être dynamique de façon transparente pour le programmeur, ou encore de disposer d’une propriété Count qui contient le nombre d’éléments effectivement présents dans la collection alors que la propriété Length d’un tableau contient le nombre de positions réservées, mais pas le nombres de positions effectivement utilisées. Voici quelques unes des collections Generic proposées :

List Liste de valeurs accessibles par leur index. Cette collection dispose des méthodes de recherche, de tri et de manipulation de listes, comme l’ArrayList.

Dictionary Collection de clés et de valeurs, comme la HashTable. SortedDictionary Collection des paires clé-valeur qui sont triées sur la clé, comme la SortedList. LinkedList Liste doublement chaînée offrant notamment des méthodes de parcours Previous et

Next ainsi que des possibilités d’insertion de types First, Last, Before et After. Queue Collection d'objets FIFO, comme la Collections.Queue, avec les mêmes méthodes. Stack Collection de type LIFO, comme la Collections.Stack, avec les mêmes méthodes.

Quelques exemples. ' List (Of String)

Dim Ls As New Collections.Generic.List(Of String) Ls.Add("AZERTY") Ls.Add("QWERTY") Ls.Add("RANZANI") Ls.Add("PANZANI") For Each Obj As Object In Ls ' Affichage en ordre d'encodage : Console.WriteLine(Obj.ToString) ' AZERTY QWERTY RANZANI PANZANI Next

' List (Of Integer)

Dim Li As New Collections.Generic.List(Of Integer) Li.Add(456) Li.Add(123) Li.Add(789) Li.Add(258) For Each Obj As Object In Li ' Affichage en ordre d'encodage : Console.WriteLine(Obj.ToString) ' 456 123 789 258 Next

' Dictionary (Keys String, Values Integer)

Dim Dsi As New Collections.Generic.Dictionary(Of String, Integer) Dsi.Add("AZ", 456) Dsi.Add("QW", 123) Dsi.Add("RA", 789) Dsi.Add("PA", 258) For Each Obj As Object In Dsi.Keys ' Affichage en ordre d'encodage : Console.WriteLine(Obj.ToString) ' AZ QW RA PA Next For Each Obj As Object In Dsi.Values ' Affichage en ordre d'encodage : Console.WriteLine(Obj.ToString) ' 456 123 789 258 Next

Page 190: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 44

' Dictionary (Keys Integer, Values String) Dim Dis As New Collections.Generic.Dictionary(Of Integer, String) Dis.Add(456, "AZERTY") Dis.Add(123, "QWERTY") Dis.Add(789, "RANZANI") Dis.Add(258, "PANZANI") For Each Obj As Object In Dis.Keys ' Affichage en ordre d'encodage : Console.WriteLine(Obj.ToString) ' 456 123 789 258 Next For Each Obj As Object In Dis.Values ' Affichage en ordre d'encodage : Console.WriteLine(Obj.ToString) ' AZERTY QWERTY RANZANI PANZANI Next

' SortedDictionary (Keys Integer, Values String)

Dim SDis As New Collections.Generic.SortedDictionary(Of Integer, String) SDis.Add(456, "AZERTY") SDis.Add(123, "QWERTY") SDis.Add(789, "RANZANI") SDis.Add(258, "PANZANI") For Each Obj As Object In SDis.Keys ' Affichage en ordre croissant des clés : Console.WriteLine(Obj.ToString) ' 123 258 456 789 Next For Each Obj As Object In SDis.Values ' Affichage en ordre croissant des clés : Console.WriteLine(Obj.ToString) ' QWERTY PANZANI AZERTY RANZANI Next

' LinkedList

Dim LKs As New Collections.Generic.LinkedList(Of String) Dim NoeudAPlacer As LinkedListNode(Of String) Dim NoeudCible As LinkedListNode(Of String) NoeudAPlacer = New LinkedListNode(Of String)("AZER") LKs.AddFirst(NoeudAPlacer) NoeudAPlacer = New LinkedListNode(Of String)("RANA") LKs.AddLast(NoeudAPlacer) NoeudCible = LKs.FindLast("AZER") NoeudAPlacer = New LinkedListNode(Of String)("KIWI") LKs.AddAfter(NoeudCible, NoeudAPlacer) ' Attention : erreur si NoeudCible = Nothing NoeudCible = LKs.FindLast("KIWI") NoeudAPlacer = New LinkedListNode(Of String)("KAWA") LKs.AddBefore(NoeudCible, NoeudAPlacer) NoeudAPlacer = New LinkedListNode(Of String)("PANA") LKs.AddAfter(NoeudCible, NoeudAPlacer) NoeudAPlacer = New LinkedListNode(Of String)("ERGO") LKs.AddFirst(NoeudAPlacer) For Each Obj As Object In LKs ' Affichage en ordre attendu : Console.WriteLine(Obj.ToString) ' ERGO AZER KAWA KIWI PANA RANA Next

Les types de données des collections génériques peuvent être des types construits par le programmeur. Voici pour exemple une LinkedList de Personne, le type Personne étant une structure.

Private Structure Personne Dim NomPers As String Dim AgePers As Byte End Structure Dim MaListe As New Collections.Generic.LinkedList(Of Personne) Dim NoeudAPlacer As LinkedListNode(Of Personne) Dim NoeudCible As LinkedListNode(Of Personne)

Page 191: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBPA - 45

Dim UnePersonne As Personne UnePersonne.NomPers = "AZER" UnePersonne.AgePers = 22 NoeudAPlacer = New LinkedListNode(Of Personne)(UnePersonne) MaListe.AddFirst(NoeudAPlacer) UnePersonne.NomPers = "RANA" UnePersonne.AgePers = 66 NoeudAPlacer = New LinkedListNode(Of Personne)(UnePersonne) MaListe.AddLast(NoeudAPlacer) UnePersonne.NomPers = "AZER" UnePersonne.AgePers = 22 NoeudCible = MaListe.FindLast(UnePersonne) ' Recherche d'un noeud If NoeudCible IsNot Nothing Then ' Si le noeud est trouvé UnePersonne.NomPers = "KIWI" UnePersonne.AgePers = 44 NoeudAPlacer = New LinkedListNode(Of Personne)(UnePersonne) MaListe.AddAfter(NoeudCible, NoeudAPlacer) End If

Page 192: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 1

Programmation orientée objet en

VB.Net

Page 193: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 2

Tables des matières des pages VBOBJ Introduction ........................................................................................................................................................................ 3

Depuis 1960 .............................................................................................................................................................. 3 Quelles différences ? ................................................................................................................................................ 3

Programmation orientée objet............................................................................................................................................. 4 Classe et structure ..................................................................................................................................................... 4 Gestion de la mémoire .............................................................................................................................................. 8

Ramasse-miettes ............................................................................................................................................. 9 Enveloppement, contrôle d’accès, encapsulation ..................................................................................................... 9

Une propriété par défaut .............................................................................................................................. 12 L'héritage ................................................................................................................................................................ 13

Programmation de l'héritage ........................................................................................................................ 14 Membre statique ................................................................................................................................ 15 Dérivations publique, protégée et privée ........................................................................................... 18 Constructeurs et destructeurs ............................................................................................................. 19 Constructeur par recopie .................................................................................................................... 21

Interface et Implements ................................................................................................................................ 22 Le polymorphisme .................................................................................................................................................. 26

Classe abstraite et méthode virtuelle ............................................................................................................ 26 Surdéfinition, surcharge et masquage .......................................................................................................... 30 La surcharge des opérateurs ......................................................................................................................... 33

L'interdiction de dériver ......................................................................................................................................... 33 Les événements ...................................................................................................................................................... 34

Emission d'événements ................................................................................................................................ 34 Réception et traitement d'événements .......................................................................................................... 35

Aspects avancés ................................................................................................................................................................ 37 La délégation .......................................................................................................................................................... 37 Programmation asynchrone .................................................................................................................................... 40

La commande asynchrone Shell ................................................................................................................... 40 Programmation de méthodes asynchrones ................................................................................................... 41

Le multithreading ................................................................................................................................................... 43 Les processus actifs ...................................................................................................................................... 43 Synchrone, asynchrone et multithreading .................................................................................................... 44 Programmation et gestion du multithreading ............................................................................................... 45

Accès à une ressource partagée ......................................................................................................... 45 Accès exclusif à une ressource .......................................................................................................... 47 Instanciation d'un thread par délégation ............................................................................................ 48 Echange d'informations avec un thread ............................................................................................. 48 Le marshaling .................................................................................................................................... 51 Programmation asynchrone et multithreading : une synthèse ........................................................... 56

La persistance ......................................................................................................................................................... 57

Page 194: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 3

Introduction

Depuis 1960 L'approche objet, incontournable aujourd'hui, n'est pas une idée neuve. Dès le début des années soixante en Norvège, Ole-Johan Dahl et Kristen Nygaard créent le langage Simula (Simple universal language). Ce langage, qui est dérivé de l'Algol 60, se présente comme un Algol intégrant de nouveaux concepts de programmation, dont celui de la programmation orientée objet. C'est pourquoi Simula est reconnu comme étant le premier langage orienté objet. Par Simula, on entend généralement Simula-67, c'est-à-dire la deuxième version commercialisée, celle de 1967. Un pas important vers la programmation orientée objet se fait ensuite au début des années septante avec l'arrivée du langage Smalltalk, inspiré de Lisp et de Simula, qui implémente notamment les concepts d'enveloppement, d'héritage et de polymorphisme. Ce langage implémente aussi de nombreuses techniques largement reprises en Java et en DotNet. Ce sont les techniques du ramasse-miettes qui s'occupe du nettoyage de la mémoire de façon transparente pour le programmeur, de la compilation en code intermédiaire destiné à une machine virtuelle, la translation dynamique qui permet à la machine virtuelle de compiler le code intermédiaire vers le code machine au moment de l'exécution. C'est cette dernière technique qu'on nomme maintenant « compilation Just-in-time », ou JIT. C'est de l'évolution des concepts et techniques de Smalltalk, que dérivent les langages orientés objets tels que C++ (créé par Bjarne Stroustrup en 1982), Delphi, Eiffel, Python, Ruby, Loops, … et surtout, Java, Delphi.Net, C.Net, VB.Net, C#.Net.

Quelles différences ? En programmation procédurale, les instructions sont réunies en sous programmes (procédures ou fonctions) qui ont pour objet le traitement de données définies par ailleurs. Les fonctions, procédures et autres suites d’instructions accèdent aux zones où sont stockées les données. Les données et leurs moyens de traitements sont dissociés. En programmation orientée objet, un programme est vu comme un ensemble d’entités. Ces entités regroupent les données et les instructions nécessaires à leur traitement. Au cours de l'exécution du programme, les entités collaborent et s’envoient des messages nécessaires au bon déroulement des traitements. Un objet représente une entité du monde réel, ou d'un monde virtuel dans le cas d’un objet immatériel, qui se caractérise par une identité, des états significatifs et par un comportement. L’identité d’un objet permet de distinguer les objets les uns par rapport aux autres. Son état correspond aux valeurs de tous ses attributs à un instant donné. Enfin, le comportement d’un objet est défini par l’ensemble des opérations qu’il peut exécuter en réaction aux messages qu'il reçoit des autres objets. Ces messages sont essentiellement des ordres lancés par les objets afin de faire exécuter une tâche par le destinataire ou d'en obtenir une information. Même si une classe peut être dérivée de nombreuses autres (par héritage), l'objet reste néanmoins l'instance d’une et une seule classe : sa classe d’appartenance. Elle est un modèle décrivant le contenu et le comportement des futurs objets de la classe. C'est dans cette classe que sont définies les propriétés et les opérations de l’objet. Une classe est l’abstraction d’un ensemble d’objets qui possèdent une structure identique (liste des attributs) et un même comportement (liste des opérations). Ainsi, contrairement à l'approche procédurale qui dissocie données et traitements, l’approche objet se caractérise par le regroupement dans une même classe de la description des données (appelées attributs, propriétés ou données membres) et des opérations de traitements (appelées méthodes ou fonctions membres). Ce regroupement est l’enveloppement, principe initial de la POO, qui améliore l’autonomie et l’indépendance de chaque classe et augmente le potentiel de réutilisation de chacune d’elles. Attention, les concepts procéduraux de la programmation ne sont pas à jeter. Les moyens de traitements sont écrits dans les classes par de la programmation procédurale.

Traitement 1

Traitement 2

Traitement …

Traitement n

Données

Objet 1 Traitements 1

Données 1

Objet 2 Traitements 2

Données 2

Objet … Traitements …

Données …

Objet n Traitements n

Données n

Communications

Programmation objet Programmation procédurale

Page 195: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 4

Les concepts fondamentaux de la programmation orientée objet sont l’enveloppement, le contrôle d’accès, l'héritage et le polymorphisme. Pour certains auteurs, enveloppement et encapsulation sont synonymes et le contrôle d’accès porte le nom d’abstraction. Pour d’autres, l’encapsulation est l’association de l’enveloppement et du contrôle d’accès. Voici l'étude de la programmation orientée objet, de sa mise en œuvre sous VB.Net, et des techniques avancées de programmation qui en découlent.

Programmation orientée objet

Classe et structure Tout code exécutable doit être écrit à l'intérieur d'une procédure ou d'une fonction, c’est connu. Ce qu'il faut encore dire, c'est que toute procédure ou fonction doit être écrite à l'intérieur d'un module, d'une structure ou d'une classe. Les exemples de programmation à l'intérieur d'un module sont déjà nombreux. En ce qui concerne la structure, elle a été étudiée en tant que moyen d'organisation des données et il est nécessaire de compléter l'information à son sujet. Une classe peut être définie en dehors de toute autre structure de programmation, auquel cas elle est obligatoirement Public, ou définie comme sous classe à l'intérieur d'une autre classe, où le programmeur peut lui donner la portée qu'il souhaite. Elle peut également être définie à l'intérieur d'un module où elle peut être Public, Friend ou Private, mais dans la mesure où un module n'est pas accessible par instanciation ou héritage dans une application cliente, les portées Public et Friend sont tout à fait équivalentes. Une classe peut aussi être définie à l'intérieur d'une structure. Deux attributs peuvent être associés à la déclaration de portée de la classe pour préciser son comportement en matière d'héritage : MustInherit et NotInheritable.

MustInherit Indique que la classe sert de classe de base dans une relation d'héritage et elle ne peut être instanciée. En général, dans une telle classe, seules les déclarations des méthodes sont définies. Le contenu doit être rédigé dans les classes dérivées.

NotInheritable Indique que la classe est la dernière de la hiérarchie. Aucune autre classe ne peut en dériver.

Public Class MaClasse ' Inherits UneClasseMere ' Implements UneInterface ' Implements UneAutreInterface Public Function MonCinq() As Integer Return 5 End Function End Class Module MonModule Sub Main() Dim MC As MaClasse MC = New MaClasse Console.WriteLine(MC.MonCinq) Console.ReadLine() End Sub End Module La classe MaClasse écrite en dehors de toute autre structure est bien Public et elle expose une méthode, la fonction MonCinq. Les lignes en commentaires ne sont évidemment pas utiles ici, sauf pour présenter l'endroit où il faut, le cas échéant, déclarer une classe mère et des interfaces. L'utilisation du mot clé Inherits précède la classe dont hérite celle en cours. Il ne peut y avoir qu'une seule ligne de ce type dans une classe. Le mot clé Implements précède le nom d'une interface souhaitée. Il peut y en avoir plusieurs par classe. Une interface ressemble un peu à une classe mais elle contient uniquement des descriptions de propriétés et de méthodes, sans code à l'intérieur, ni même de fin de code (End Sub, End Function). Implémenter une interface revient à s'engager à en écrire le contenu pour toutes les méthodes et propriétés. L'usage de l'interface est étudié plus loin. L'application cliente de MaClasse est le module MonModule. Dans sa procédure Main, la déclaration Dim MC As MaClasse attribue une variable pour la manipulation de l'objet. L'affectation de cette variable est réalisée par l'appel du constructeur de la classe qui réalise l'instanciation de l'objet, c'est-à-dire sa création en mémoire. Il faut remarquer au passage que le code du constructeur New de la classe n'a pas été écrit. Dans ce cas, le compilateur en crée un d'office ou plus exactement, il en recherche un en remontant la généalogie des classes jusqu’à la classe Object s’il le faut.

Page 196: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 5

L'exemple suivant illustre la création d'une sous classe et celle d'une classe à l'intérieur d'un module, ainsi que leur instanciation. Public NotInheritable Class MaClasse ' Nul ne pourra en hériter Private Shared UneValeur As Integer ' Une variable de classe Public Function MaFonction1() As Integer ' Une méthode UneValeur = 5 Return UneValeur ' Retourne la valeur de la variable de classe End Function Public Function MaFonction2() As Integer ' Une autre méthode Dim C As UneSousClasse C = New UneSousClasse ' Instanciation d'une sous classe Return C.UneFonction / 2 ' Retourne la valeur reçue d'une méthode End Function ' de la sous classe après l'avoir divisée Public Class UneSousClasse ' Définition de la sous classe UneSousClasse Public Function UneFonction() As Integer UneValeur = 10 Return UneValeur * 2 ' La méthode retourne un entier End Function End Class End Class Module MonModule Public Class ClasseModule ' Définition d'une classe dans le module Public Function UneAutreFonction() As String Return "MomMessage" ' La méthode retourne une chaîne End Function End Class Sub Main() Dim MC As MaClasse ' Une variable pour MaClasse Dim MSC As MaClasse.UneSousClasse ' Une variable pour accéder à la sous classe Dim MCM As ClasseModule ' Une variable pour ClasseModule MC = New MaClasse ' Création d'un objet MaClasse MSC = New MaClasse.UneSousClasse ' Création d'un objet UneSousClasse MCM = New ClasseModule ' Création d'un objet ClasseModule Console.WriteLine(MC.MaFonction1) ' Affiche 5 Console.WriteLine(MC.MaFonction2) ' Affiche 10 Console.WriteLine(MSC.UneFonction) ' Affiche 20 Console.WriteLine(MCM.UneAutreFonction) ' Affiche MonMessage Console.ReadLine() End Sub End Module Tout fonctionne aussi bien, dans les exemples précédents quand le mot clé Class est remplacé par le mot clé Structure. Cependant, il y a des différences et notamment celles-ci :

Un type Structure est de type valeur alors qu'une classe est de type référence. Un type Structure doit déclarer au moins une variable membre ou un événement. Un type Structure peut déclarer un constructeur, mais alors il doit obligatoirement être paramétré. Un type Structure ne peut pas servir de base pour un autre qui en hériterait. Un type Structure ne peut pas spécifier une classe dont il hériterait. Un type Structure ne peut pas déclarer un destructeur.

Page 197: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 6

Le premier exemple : Public Structure MaStructure ' Implements UneInterface ' L'implémentation d'interfaces est permise Dim UneVariable As Integer ' Une structure doit contenir au moins une Public Function MonCinq() As Integer ' variable membre. Return 5 End Function End Structure Module MonModule Sub Main() Dim MS As MaStructure ' MS = New MaStructure ' Instanciation superflue. Toutefois, les Console.WriteLine(MS.MonCinq) ' membres de type référence doivent être Console.ReadLine() ' initialisés End Sub End Module Le deuxième exemple : Public Structure MaStructure Private Shared UneValeur As Integer ' Ne compte pas comme variable membre Private XMS As Integer ' Voici une variable membre Public Function MaFonction() As Integer ' Une fonction membre qui utilise la fonction Dim C As UneSousStructure ' membre d'une sous structure ' C = New UneSousStructure ' Instanciation superflue. Return C.UneFonction / 2 End Function Public Structure UneSousStructure ' Définition de la sous structure et de ses Dim XMSS As Integer ' membres, dont une fonction Public Function UneFonction() As Integer UneValeur = 10 Return UneValeur * 2 End Function End Structure Public Class ClasseStructure ' Définition d'une classe dans la structure Public Function UneFonction() As Integer ' et d'une méthode UneFonction Dim X As Integer = 30 Return X End Function End Class End Structure Module MonModule Public Structure StructureModule ' Définition d'une structure dans le module Dim XSM As Integer ' et de ses membres, dont une fonction Public Function UneAutreFonction() As String Return "MomMessage" End Function End Structure Sub Main() Dim MS As MaStructure Dim MSS As MaStructure.UneSousStructure Dim MSM As StructureModule Dim MCS As MaStructure.ClasseStructure MCS = New MaStructure.ClasseStructure ' Une classe est un objet de type référence Console.WriteLine(MS.MaFonction) ' Affiche 10 Console.WriteLine(MSS.UneFonction) ' Affiche 20 Console.WriteLine(MSM.UneAutreFonction) ' Affiche MonMessage Console.WriteLine(MCS.UneFonction) ' Affiche 30 Console.ReadLine() End Sub End Module

Page 198: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 7

Un dernier exemple pour illustrer encore les similitudes existant entre les Class et les Structure, mais aussi l'une de leurs différences, ainsi que l'emploi d'un constructeur personnalisé. Public Structure MaStructure ' Définition d'une structure MaStructure et Public XMS As Integer ' de ses membres, soit une variable de type Public Tab1() As Integer ' entier, un tableau d'entiers, une variable Public Classe As ClasseEnStructure ' de type objet ClasseEnStructure Public Sub New(ByVal NbEntiers As Integer) ' La structure déclare un constructeur dont Tab1 = New Integer(NbEntiers -1) {} ' le rôle est de créer le tableau pour un Classe = New ClasseEnStructure ' nombre d'entiers passé en paramètre et End Sub ' de créer l'objet pour la variable Classe Public Class ClasseEnStructure Public Tab2 = New Integer(5) {} ' La classe expose un tableau pour 6 entiers End Class End Structure Public Class MaClasse ' Définition d'une classe MaClasse et Public XMC As Integer ' de ses membres, soit une variable de type Public Tab1() As Integer ' entier, un tableau d'entiers, une variable Public Classe As ClasseEnClasse ' de type objet ClasseEnClasse Public Sub New(ByVal NbEntiers As Integer) ' La structure déclare un constructeur dont Tab1 = New Integer(NbEntiers -1) {} ' le rôle est de créer le tableau pour un Classe = New ClasseEnClasse ' nombre d'entiers passé en paramètre et End Sub ' de créer l'objet pour la variable Classe Public Class ClasseEnClasse Public Tab2 = New Integer(5) {} ' La classe expose un tableau pour 6 entiers End Class End Class Jusqu'ici, il n'y a pas de différences importantes entre les approches Structure et Class. Module MonModule Sub Main() Dim MS, MS2 As MaStructure ' Déclaration des variables pour MaStructure Dim MC, MC2 As MaClasse ' Déclaration des variables pour MaClasse MS = New MaStructure(5) ' Création d'une MaStructure en mémoire MS.XMS = 123 ' Affectation d'un membre de MaStructure MS.Tab1(1) = 10 ' Affectation d'un autre membre MC = New MaClasse(5) ' Création d'une MaClasse en mémoire MC.XMC = 456 ' Affectation d'un membre de MaClasse MC.Tab1(1) = 100 ' Affectation d'un autre membre MS2 = MS ' Affectation d'une autre MaStructure par MS MS2.XMS = 321 ' Modification des valeurs de cette variable MS2.Tab1(1) = 101 ' MC2 = MC ' Affectation d'une autre MaClasse par MC MC2.XMC = 654 ' Modification des valeurs de cette variable MC2.Tab1(1) = 1001 ' Console.WriteLine(MS.XMS) ' Affiche 123 Console.WriteLine(MS2.XMS) ' Affiche 321 Console.WriteLine(MS.Tab1(1)) ' Affiche 101 Console.WriteLine(MS2.Tab1(1)) ' Affiche 101 Console.WriteLine(MC.XMC) ' Affiche 654 Console.WriteLine(MC2.XMC) ' Affiche 654 Console.WriteLine(MC.Tab1(1)) ' Affiche 1001 Console.WriteLine(MC2.Tab1(1)) ' Affiche 1001 End Sub End Module

Une différence !

Page 199: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 8

La seule différence constatée dans l'exemple précédent réside apparemment dans le fait que la variable de type valeur est bien traitée comme telle dans la structure et non dans la classe. En réalité, c'est la structure elle-même qui est de type valeur et lors de l'affectation de MS à MS2, toutes les valeurs de la première sont copiées dans la deuxième. Dans le cas de l'objet de type MaClasse, seule la référence de MC est copiée dans MC2 lors de l'affectation, et c'est bien normal puisqu'une classe est de type référence. Dès lors, et en première approche, il est étonnant que les tableaux de la structure ne soient pas simplement copiés eux aussi, qu'ils soient traités comme dans la classe. Il faut se souvenir qu'un tableau est de type référence et que seule la référence est conservée dans la variable. Ainsi, tout s'explique. Donc, dans la structure comme dans la classe, seule la référence d'un membre de type référence est conservée dans la variable correspondante et dans le cas de la structure, cette référence est bien copiée par l'affectation, tandis que dans le cas de la classe, seule la référence de l'instance de la classe elle-même est copiée. Le choix entre Class et Structure est simple dès lors qu’est abordée la programmation orientée objet. Comme la structure ne peut hériter d'une classe et qu'aucune classe ou autre structure ne peut en dériver, il est évident qu'elle n'intègre pas le concept d'héritage de la POO, ni donc celui du polymorphisme. La structure est donc à rejeter de la POO, sauf pour organiser des lots de données sous la forme de types complexes définis par le programmeur. La classe intègre tous les concepts de la POO, elle est maintenant le principal objet de ces pages.

Gestion de la mémoire La déclaration d’un objet de type valeur provoque la réservation en mémoire de l’emplacement nécessaire pour stocker la ou les valeur(s) de cet objet. L’initialisation par défaut de la variable le représentant, est la valeur nulle selon le type. L'opération d'affectation entre objets de ce type copie toutes les valeurs de l'objet. Les valeurs de ce type sont stockées dans la pile ou stack. Les données de la pile sont dépilées et perdues quand l'application quitte la portée qui leur a été allouée. L'espace occupé dans la pile est celui nécessaire au stockage des valeurs des variables. La déclaration d’un objet de type référence provoque la réservation en mémoire de l’emplacement nécessaire pour stocker l’adresse où sera stocké cet objet. L’initialisation par défaut de la variable le représentant, qui est donc un pointeur, est Nothing. Ce n'est qu'à la création effective de l'objet que cette variable reçoit l'adresse de l'objet. Une variable de type référence doit être initialisée par des valeurs ou par l’usage de l’opérateur New pour permettre au compilateur d’évaluer et de réserver l’espace nécessaire en mémoire. Quand cette variable représente l'instance d'une classe, New, qui est le constructeur de toute classe, est le seul opérateur habilité à l'initialiser. L'opération d'affectation entre objets de ce type ne copie que l'adresse mémoire de l'objet, sa référence. Les références des objets sont stockées dans la pile et leurs valeurs dans le tas ou heap. Comme pour les objets de type valeur, les références dans la pile sont dépilées et perdues quand l'application quitte la portée qui leur a été allouée. Cependant, ce processus ne libère pas l'espace occupé dans le tas par les objets. Cette libération de la mémoire est réalisée automatiquement par le ramasse-miettes ou garbage collector. L'affectation de la valeur Nothing à une référence n'influence en rien l'occupation de la mémoire, aussi bien côté pile que côté tas. L'espace occupé dans la pile est celui nécessaire au stockage d'une adresse, soit quatre ou huit octets selon le système, et celui occupé dans le tas est l'espace nécessaire pour les données des objets. Les codes, comme ceux des méthodes par exemple, sont stockés en d'autres endroits de la mémoire et n'affectent ni la pile, ni le tas.

Opérations Quelque part dans la pile Quelque part dans le tas

Etiquettes Adresses supposées

Valeurs Adresses supposées

Valeurs

… Dim XN As Integer XN 14000000H 0 XN = 5 XN 14000000H 5 Dim MC As MaClasse MC 13FFFFFCH Nothing MC = New MaClasse MC 13FFFFFCH 10000000H 10000000H Début d'un

objet MaClasse

Dim MC2 As MaClasse MC2 13FFFFF8H Nothing MC2 = MC MC2 13FFFFF8H 10000000H Dim T() As Integer T 13FFFFF4H Nothing T = New Integer(5) {} T 13FFFFF4H 10001000H 10001000H Espace pour

6 entiers T(1) = 7 T 13FFFFF4H 10001000H 10001004H 7…

Page 200: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 9

Ramasse-miettes Le ramasse-miettes ou garbage collector réalise la récupération des ressources allouées et devenues inutiles. Son travail est transparent pour le programmeur qui ne doit pratiquement jamais s'en occuper. Il existe toutefois des cas où il ne suffit pas de compter sur le ramasse-miettes. Ce dernier libère en effet automatiquement la mémoire allouée à un objet managé lorsque cet objet n'est plus utilisé, mais il est impossible de prévoir quand il va le faire. De plus, le ramasse-miettes n'a pas connaissance des ressources non managées telles que les handles de fenêtres et les fichiers et flux ouverts. Les objets managés sont ceux dont le code est spécifiquement conçu pour le Framework et sont, de ce fait, contrôlés par le CLR (Common language runtime). A l’inverse notamment, des composants appartenant à d'anciennes versions de VB se retrouvent parmi les objets non managés. Lorsqu'il doit ajouter une référence dans un projet, la fenêtre de dialogue qui s'ouvre au programmeur possède plusieurs onglets dont NET et COM. Les références proposées sous l'onglet NET sont des ressources managées. Celles de l'onglet COM ne le sont pas. Le programmeur peut parfois souhaiter que la libération d'une ressource soit immédiate. Il doit par ailleurs absolument libérer les ressources qui sont inconnues du ramasse-miettes. Plusieurs moyens existent pour libérer explicitement les ressources en association avec le ramasse-miettes. Ce sont les méthodes Close, Dispose et Finalize. L'étude des destructeurs est abordée un peu plus loin dans ces pages, mais il faut déjà noter qu'il est préférable d'utiliser la méthode Close quand l'objet concerné l'implémente. Cette méthode appelle la méthode Dispose sans lui passer de paramètres. Si l'objet ne possède pas cette méthode ou s'il est nécessaire de paramétrer le destructeur, il faut utiliser la méthode Dispose adéquate. La méthode Finalize de la classe Object ne doit jamais être appelée explicitement, elle est exécutée automatiquement lors de la destruction de l'objet.

Enveloppement, contrôle d’accès, encapsulation L'enveloppement est le mécanisme consistant à rassembler les données et les méthodes au sein d'une classe et le contrôle d’accès est celui qui consiste à cacher l'implémentation de l'objet, c'est-à-dire à empêcher l'accès aux données par d'autres moyens que ceux exposés par l'interface de la classe. L'interface de la classe est l'ensemble de ses membres accessibles à partir d'instances. Le contrôle d’accès veut que cette interface ne contienne que des méthodes. Dans l'exemple ci contre, le contrôle d’accès n'est pas réalisé car la variable Cinq déclarée Public est accessible de toute instance de la classe. Il existe pourtant une méthode MonCinq qui est habilitée à renvoyer la valeur de cette variable. Le contrôle d’accès est donc facile dans ce cas : il suffit de remplacer l'attribut de portée de la variable par Dim ou Private. L'enveloppement renferme les biens d'une classe à l'intérieure de celle-ci et leur accès ne peut se faire que par les méthodes habilitées. Ces concepts réunis réalisent l’encapsulation, la protection de l'intégrité des données contenues dans l'objet. L'interface de la classe expose les membres selon les niveaux de visibilité définis par le programmeur. Il existe essentiellement trois niveaux de visibilité des membres d'une classe :

Publique

Tous les membres Public sont toujours exposés et donc toujours accessibles par les fonctions de n'importe quelle autre classe. Il s'agit du plus bas niveau de protection des données.

Protégé Les membres Protected ne sont exposés que dans des classes dérivées. Ils sont réservés aux fonctions des classes héritières.

Privé Les membres Private ne sont pas exposés et leur accès est limité aux méthodes de la classe elle même. Il s'agit du niveau de protection des données le plus élevé.

A cela peuvent s'ajouter les déclarations de portées Friend et Protected Friend. La visibilité Friend est équivalente à Public dans le projet en cours et à Private dans les applications clientes. La visibilité Protected Friend est équivalente à Public dans le projet en cours et dans les classes dérivées, mais Private dans les applications clientes qui ne pratiquent que des instanciations de la classe.

Public Class MaClasse Public Cinq As Integer = 5 Public Function MonCinq() As Integer Return Cinq End Function End Class

Page 201: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 10

La nécessité du contrôle d’accès concerne essentiellement les données de la classe. Il est cependant superflu de déclarer Public des procédures et des fonctions qui ne servent que la classe elle-même. Il faut réserver la portée Public aux méthodes qui doivent répondre aux ordres lancés par d'autres objets. Par exemple, la classe suivante MesPremiers, est destinée au traitement de nombres premiers. Elle n'est pas complète, mais elle expose déjà une méthode Premier qui retourne True si le nombre qui lui est passé est premier et False dans le cas contraire. La vérification d'un nombre est faite par le test du reste de sa division par un entier impair qui varie au pas de 2, à partir de 3 jusqu'à une limite calculée. Cette limite est calculée par la fonction locale Limite qui retourne la partie entière de la racine carrée du nombre ou 0 si le nombre est un carré parfait. De plus, les nombres pairs et multiples de 5 sont détectés par la fonction locale PairEt5 de sorte à ne pas entreprendre les calculs pour eux. En cours d'itérations cette fonction permet encore d'éviter le calcul du reste de la division par 5. L'outil fonctionne aussi bien si les attributs de portée des fonctions Limite et PairEt5 sont remplacés par Public ou Protected. Mais est-il bien nécessaire qu'une application cliente ait accès à ces fonctions spécialement conçues pour servir la méthode Premier ? A défaut d'une analyse orientée objet bien menée, c'est la question que doit se poser le programmeur pour déterminer si un membre présente un intérêt publique ou pas. Public Class MesPremiers Public Function Premier(ByVal N As Integer) As Boolean Dim Diviseur As Integer Dim LimiteCalcul As Integer = 0 If N = 2 Or N = 3 Or N = 5 Then Return True If Not (PairEt5(N)) Then LimiteCalcul = Limite(N) Diviseur = 3 While (Diviseur < LimiteCalcul) If N Mod Diviseur = 0 Then Return False Diviseur += 2 If PairEt5(Diviseur, False) Then Diviseur += 2 End While Else Return False End If If LimiteCalcul > 0 Then Return True Else Return False End If End Function Private Function PairEt5(ByVal N As Integer, Optional ByVal Tout As Boolean = True)

As Boolean Dim Resultat As Boolean Resultat = (N Mod 5 = 0) ' Test si multiple de 5 If Tout Then ' Tester aussi si multiple de 2 Resultat = Resultat OrElse (N Mod 2 = 0) End If Return Resultat End Function Private Function Limite(ByVal N As Integer) As Integer Dim R As Single R = Math.Sqrt(N) ' Calculer de la racine carrée If R > Math.Floor(R) Then ' Arrondir vers le bas Return CType(R, Integer) Else Return 0 ' Si R vaut son arrondi, N est un carré End If End Function End Class

Page 202: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 11

En ce qui concerne les données de l'objet, plusieurs moyens sont à la disposition du programmeur pour les encapsuler et permettre quand même aux applications clientes d'accéder à certaines d'entre elles. Le programmeur peut user de fonctions et de procédures publiques, donc de méthodes au sens commun, ainsi que de méthodes appropriées et appelées Property. Selon le cas, il doit autoriser l'accès en lecture seule, ou en écriture seule, ou encore permettre la lecture et l'écriture des valeurs. Public Class MaClasse Dim ValeurLocale As Integer Public Enum SetGet DoSet = 0 : DoGet = 1 ' Valeurs constantes End Enum ' Méthode commune d'accès en lecture seule ' Usage dans une application cliente : ' Dim Obj As New MaClasse Public Function LireValeurM() As Integer ' Dim N as Integer Return ValeurLocale ' N = Obj.LireValeurM() End Function ' Méthode commune d'accès en écriture seule ' Usage dans une application cliente : ' Dim Obj As New MaClasse Public Sub EcrireValeurM(ByVal UneValeur As Integer) ' Dim N as Integer = 5 ValeurLocale = UneValeur ' Obj.EcrireValeurM(N) End Sub ' Méthode commune d'accès en lecture et en écriture Public Function LireEcrireValeurM(ByVal SG As SetGet, Optional ByVal UneValeur

As Integer = 0) As Integer If SG = SetGet.DoSet Then ' Usage dans une application cliente : ValeurLocale = UneValeur ' Dim Obj As New MaClasse Else ' Dim N as Integer Return ValeurLocale ' N = Obj.LireEcrireValeurM(1) End If ' N += 5 End Function ' Obj.LireEcrireValeurM(0, N) ' Méthode Property d'accès en lecture seule, aussi apellée "Accesseur" Public ReadOnly Property LireValeurP() As Integer ' Mot clé ReadOnly requis Get ' Usage dans une application cliente : Return ValeurLocale ' Dim Obj As New MaClasse End Get ' Dim N as Integer End Property ' N = Obj.LireValeurP ' Méthode Property d'accès en écriture seule, aussi apellée "Modifieur" ou "Modificateur" Public WriteOnly Property EcrireValeurP() As Integer ' Mot clé WriteOnly requis Set(ByVal UneValeur As Integer) ' Usage dans une application cliente : ValeurLocale = UneValeur ' Dim Obj As New MaClasse End Set ' Dim N as Integer = 5 End Property ' Obj.EcrireValeurP = N ' Méthode Property d'accès en lecture et en écriture ("Accesseur" ou "Modifieur" selon son usage) Public Property LireEcrireValeurP() As Integer Get ' Usage dans une application cliente : Return ValeurLocale ' Dim Obj As New MaClasse End Get ' Dim N as Integer Set(ByVal UneValeur As Integer) ' N = Obj.LireEcrireValeurP ValeurLocale = UneValeur ' N += 5 End Set ' Obj.LireEcrireValeurP = N End Property End Class

Page 203: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 12

Il faut encore noter que l'initialisation de variables de classes à l'aide d'un constructeur paramétré respecte le principe du contrôle d’accès. Dans l'exemple suivant, deux constructeurs ont été programmés. L'emploi de plusieurs constructeurs permet le choix de celui qui convient le mieux à un moment donné dans l'application cliente. Le premier, sans paramètre, fait appel au constructeur dont tout objet hérite implicitement de la classe originelle, à savoir la classe Object. La programmation de cet appel est facultative car il est exécuté automatiquement, mais c’est la seule tâche qui puisse être programmée ici, ce constructeur n’ayant rien d’autre à faire. Le deuxième constructeur est défini avec un paramètre formel destiné à l'affectation de la variable de la classe. Ce constructeur surcharge le premier. La spécification Optional de son paramètre aurait dispensé de la programmation du premier constructeur. Le mot clé Overloads ne doit pas être employé pour la surcharge des constructeurs. Public Class MaClasse Dim DonneeLocale As Integer Public Sub New() ' Définition du premier constructeur MyBase.New() ' Appel du constructeur de base End Sub Public Sub New(ByVal Donnee As Integer) ' Définition du deuxième constructeur DonneeLocale = Donnee ' Affectation de la variable de classe End Sub Public Function Donnee() As Integer ' Méthode de lecture de la variable Return DonneeLocale End Function End Class Module MonModule Sub Main() Dim MC As MaClasse MC = New MaClasse ' Appel du constructeur sans paramètre Console.WriteLine(MC.Donnee) ' Affiche 0 MC = New MaClasse(123) ' Appel du constructeur avec paramètre Console.WriteLine(MC.Donnee) ' Affiche 123 End Sub End Module Une propriété par défaut La propriété par défaut est celle qui est utilisée lors de l’accès à une instance sans précision de membre. Elle est définie par une méthode Property dotée d'un attribut particulier : Default. Voici, pour l'exemple, une classe ClTableauDePersonnes construite à l'image d'une ArrayList mais dont la propriété Item peut être omise à l'usage. La classe gère bien entendu un vecteur de personnes, chacune étant une instance d'une classe ClPersonne. Cette dernière possède ici un seul membre, une méthode Property, qui permet l'accès au nom de la personne. Public Class ClPersonne Dim NomLocal As String Public Property Nom() As String Get Return NomLocal End Get Set(ByVal UneValeur As String) NomLocal = UneValeur End Set End Property End Class

Page 204: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 13

Public Class ClTableauDePersonnes Private DesPersonnes() As ClPersonne Default Public Property Item(ByVal Index As Integer) As ClPersonne Get Return CType(DesPersonnes(Index), ClPersonne) End Get Set(ByVal Value As ClPersonne) If DesPersonnes Is Nothing Then ReDim DesPersonnes(8) ' Réservation par tranches de 9 If Index <> 0 Then Exit Property ' Lever une exception serait mieux ! End If ' … Out of range … Else If Index = DesPersonnes.Length Then ReDim Preserve DesPersonnes(Index + 8) ElseIf Index > DesPersonnes.Length Then Exit Property ' Lever une exception serait mieux ! End If End If DesPersonnes(Index) = Value End Set End Property End Class Module MonModule Sub Main() Dim UnePersonne As ClPersonne Dim TPersonne As New ClTableauDePersonnes For i As Integer = 0 To N - 1 ' N étant le nombre de personnes UnePersonne = New ClPersonne UnePersonne.Nom = Console.ReadLine() TPersonne(i) = UnePersonne ' L'emploi de Item est optionnel Next End Sub End Module

L'héritage L’héritage permet de construire une classe à partir d’une ou de plusieurs autres. Le nom héritage provient du fait que la classe dérivée, ou classe fille, contient les attributs et les méthodes de sa superclasse, ou classe mère, dont elle dérive. L'intérêt majeur de l'héritage est de pouvoir définir de nouveaux attributs et de nouvelles méthodes pour la classe dérivée, qui viennent s'ajouter à ceux et celles hérités. C'est par ce moyen que peut se créer une hiérarchie de classes de plus en plus spécialisées. L'avantage majeur est de ne pas devoir repartir de zéro quand il est nécessaire de spécialiser une classe existante. C'est le principal outil permettant la réutilisabilité de codes existants. Les exemples précédents illustrent bien la notion de spécialisation sous jacente à celle d'héritage. Ils montrent bien aussi le mécanisme de cette spécialisation qui consiste à enrichir l'apport de la classe mère en y ajoutant les propriétés et méthodes appropriées à la nouvelle classe.

Classe Animal

Classe Mammifère

Classe Rongeur

Dans ces exemples, la flèche désigne le parent d'où provientl'héritage. Ce sont des flèches creuses conformément au formalisme UML (Unified Modeling Language). De même, les représentations des classes sont divisées en 3 parties dans le schéma de droite. Ces parties correspondent au nom de la classe, à ses propriétés exposées et à ses méthodes.

Classe Véhicule Propriétaire, Marque, NoPlaque

Avancer( ), Reculer(), Arrêter( )

Classe Automobile NbPlaces

Liste_Passagers( )

Classe Camion Tonnage, TypeChargement

Classe Camionnette Type_ Utilisation

Page 205: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 14

L'exemple de la classe Véhicule présente aussi un aspect de l'héritage qui n'est pas réalisable en VB, pas plus qu'en Java d'ailleurs, mais qui est possible en C++ : l'héritage multiple. En effet, la classe Camionnette ne peut exister que par l'héritage des membres de la classe Automobile et de ceux de la classe Camion. Il n'y a pas à regretter l'absence d'héritage multiple en VB. Cette technique peut engendrer des problèmes logiques complexes quand elle n'est pas parfaitement maîtrisée, notamment quand les différentes classes mères possèdent des membres homonymes. De plus, l'usage des interfaces avec l'instruction Implements procure une sorte d'héritage plus simple et plus sûr. Il faut noter que l'héritage ne transmet pas tout. Outre les membres Private et Friend, qui ne sont bien évidemment pas transmis par l'héritage, les constructeurs, destructeurs et opérateurs d'égalité ne le sont pas non plus. Chaque classe dispose d’un constructeur par défaut, celui de la classe Object. Toutefois, si des membres de la classe mère doivent être initialisés lors de l'instanciation, il faut que la classe dérivée appelle explicitement un constructeur de la classe mère dans son propre constructeur et ce, avant même de procéder à l'initialisation de ses propres membres. Par contre si un destructeur est explicitement défini dans la classe dérivée, il ne doit pas faire appel à celui de la classe mère. Ce dernier est automatiquement exécuté lors de la destruction de l'instance de la classe dérivée. Public Class UneClasseDerivee Inherits UneClasseMere Dim UneValeurLocale As Integer Public Sub New(ByVal ValeurInitiale As Integer) MyBase.New() ' Appel du constructeur de la classe mère UneValeurLocale = ValeurInitiale ' Initialisation d'un membre End Sub ' … … … Programmation de l'héritage Voici une classe ordinaire ClPersonne. Elle est Public et n'est pas dotée d'attributs particuliers. Elle n'expose qu'une propriété Nom en lecture et en écriture. Public Class ClPersonne Dim NomLocal As String Public Property Nom() As String Get Return NomLocal End Get Set(ByVal UneValeur As String) NomLocal = UneValeur End Set End Property End Class La classe dérivée ClPersonneSpecialisee hérite de la classe ClPersonne et n'utilise explicitement aucun membre de sa classe mère, mais y ajoute une propriété Specialite. Public Class ClPersonneSpecialisee Inherits ClPersonne Dim SpecialiteLocale As String Public Property Specialite() As String Get Return SpecialiteLocale End Get Set(ByVal UneValeur As String) SpecialiteLocale = UneValeur End Set End Property End Class Si la classe ClPersonne avait reçu l'attribut NotInheritable, sa dérivation dans la classe ClPersonneSpecialisee aurait été impossible.

Page 206: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 15

Dans une application cliente, il peut être fait usage, selon les besoins, aussi bien de la classe ClPersonne que de la classe ClPersonneSpecialisee. Alors que la classe ClPersonneSpecialisee ne définit aucune propriété Nom, son instance dans l'application cliente en dispose. C'est l'héritage de ClPersonne. L'inverse n'est pas vrai. L'instance de ClPersonne ne dispose pas des propriétés d'une de ses classes dérivées. Dim UnePersonne As ClPersonne UnePersonne = New ClPersonne UnePersonne.Nom = "Pol" Dim UnePersonneSpecialisee As ClPersonneSpecialisee UnePersonneSpecialisee = New ClPersonneSpecialisee UnePersonneSpecialisee.Nom = "Pierre" UnePersonneSpecialisee.Specialite = "Informatique" Si la classe ClPersonne avait reçu l'attribut MustInherit, son instanciation dans l'application cliente aurait été impossible. Lors de la programmation, l'instance d'une classe mère semble permettre l'exploitation des membres d'une de ses classes dérivées. L'instanciation d'une classe mère sous la référence d'une de ses classes dérivées donne bien accès aux membres de cette dernière. Mais la conversion s'avère impossible lors de l'exécution et une exception est levée. A l'exécution, la classe mère ne sait rien de sa classe dérivée. Son instance ne contient pas les membres de la classe dérivée. L'instanciation d'une classe de base par une référence de classe dérivée est un nonsens. Dim UneAutrePersonneSpecialisee As ClPersonneSpecialisee UneAutrePersonneSpecialisee = New ClPersonne Par contre, avec la manœuvre inverse, celle qui consiste à instancier une classe dérivée sous la référence de sa classe mère, tout fonctionne pour le mieux, tant à la programmation qu'à l'exécution. Mais l'instance de la classe dérivée n'accède qu'aux membres de la classe mère ! A l'exécution, la classe dérivée connaît sa classe mère. Son instance contient les membres de la classe mère. L'instanciation d'une classe dérivée par une référence de classe de base limite l'accès aux seuls membres de la classe de base. Dim UneAutrePersonne As ClPersonne UneAutrePersonne = New ClPersonneSpecialisee Mais alors, à quoi bon instancier une classe dérivée si seuls les membres de la classe mère sont nécessaires ? C'est utile pour accéder à un membre non spécialisé, et donc connu de toutes les classes dérivées, lorsque la classe mère ne peut être instanciée du fait qu'elle soit une classe abstraite, une classe spécifiée MustInherit. Les remarques précédentes concernant les croisements de types par instanciation s'appliquent également aux croisements par affectation. Dim UnePersonne As ClPersonne Dim PS As ClPersonneSpecialisee UnePersonne = New ClPersonne ' PS = UnePersonne ' Erreur à l'exécution Dim UnePersonneSpecialisee As ClPersonneSpecialisee Dim P As ClPersonne UnePersonneSpecialisee = New ClPersonneSpecialisee P = UnePersonneSpecialisee ' Pas d'erreur, mais seuls les membres de la

' classe ClPersonne sont disponibles Membre statique La programmation d'un membre statique est réalisée en VB par l'usage du mot clé Shared. A l'instar de la variable Static, un membre statique conserve l'état dans lequel il a été positionné par la dernière instance qui l'a utilisé. Pour rappel, il est unique pour toutes les instances de sa classe et des classes dérivées. Un membre statique, s'il est Public, peut être invoqué directement sous une référence de sa classe sans que celle-ci ait été instanciée. Mais ceci est en infraction du principe du contrôle d’accès.

Page 207: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 16

Par contre, s'il est Private, il peut néanmoins être utilisé par l'instance d'une classe dérivée via une méthode habilitée. Pour peu que l'accès à ce membre privé soit la seule raison d'être de cette instanciation, elle peut se faire sous une référence à la classe mère. Par exemple : Public Class ClPersonne Public NbPersonnes As Integer ' Déclaration d'un membre Public, qui n'est ' … … … ' pas un membre statique Sub Main() Dim UnePersonne As ClPersonne ' UnePersonne.NbPersonnes = 15 ' Erreur à l'exécution car UnePersonne n'est ' … … … ' pas une instance de ClPersonne Autre exemple : Public Class ClPersonne Public Shared NbPersonnes As Integer ' Déclaration d'un membre Public, qui est ' … … … ' aussi un membre statique Sub Main() Dim UnePersonne As ClPersonne UnePersonne.NbPersonnes = 15 ' Pas d'erreur car NbPersonnes est un ' … … … ' membre statique Et encore : Public Class ClPersonne Private Shared NbPersonnes As Integer ' Déclaration d'un membre Private, qui est ' … … … ' aussi un membre statique Sub Main() Dim UnePersonne As ClPersonne ' UnePersonne.NbPersonnes = 15 ' Erreur car NbPersonnes, qui est un membre ' … … … ' Private n'est pas accessible ici Pour réaliser un meilleur exemple de l'usage d'un membre statique, avec une donnée telle que NbPersonnes qui doit indiquer en permanence le nombre d'instances effectives de la classe, il faut y intégrer le comptage et le décomptage au fur et à mesure des instanciations et destructions. Il faut donc doter la classe d'un constructeur et d'un destructeur. Public Class ClPersonne Dim NomLocal As String Private Shared NbPersonnes As Integer ' Déclaration d'un membre Private, qui est

' aussi un membre statique Public Sub New() ' Programmation d'un constructeur adéquat NbPersonnes += 1 End Sub Public Sub Dispose() ' Programmation d'un destructeur fictif NbPersonnes -= 1 ' mais suffisant pour cet exemple End Sub Public Property Nom() As String Get Return NomLocal End Get Set(ByVal UneValeur As String) NomLocal = UneValeur End Set End Property

Page 208: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 17

Public ReadOnly Property NombreInstance() As Integer Get Return NbPersonnes ' NombreInstance est la méthode d'accès au End Get ' membre statique NbPersonnes End Property End Class Public Class ClPersonneSpecialisee ' La classe dérivée est identique à celle de Inherits ClPersonne ' l'exemple précédent Dim SpecialiteLocale Public Property Specialite() As String Get Return SpecialiteLocale End Get Set(ByVal UneValeur As String) SpecialiteLocale = UneValeur End Set End Property End Class Sub Main() Dim UnePersonne As ClPersonne Dim UnePersonneSpecialisee As ClPersonneSpecialisee UnePersonne = New ClPersonne Console.WriteLine(UnePersonne.NombreInstance) ' Affichage 1 UnePersonneSpecialisee = New ClPersonneSpecialisee Console.WriteLine(UnePersonne.NombreInstance) ' Affichage 2 Console.WriteLine(UnePersonneSpecialisee.NombreInstance)' Affichage 2 Dim UneAutrePersonne As ClPersonne UneAutrePersonne = New ClPersonneSpecialisee Console.WriteLine(UnePersonne.NombreInstance) ' Affichage 3 Console.WriteLine(UnePersonneSpecialisee.NombreInstance)' Affichage 3 Console.WriteLine(UneAutrePersonne.NombreInstance) ' Affichage 3 UneAutrePersonne.Dispose() Console.WriteLine(UnePersonne.NombreInstance) ' Affichage 2 Console.WriteLine(UnePersonneSpecialisee.NombreInstance)' Affichage 2 Console.WriteLine(UneAutrePersonne.NombreInstance) ' Affichage 2 End Sub Une classe dérivée hérite des membres de sa classe mère et il est dès lors bien normal dans cet exemple que, pour n'importe quelle instance, il soit toujours permis d’accéder à NombreInstance, méthode qui renvoie par ailleurs une donnée unique pour toutes les instances puisque Shared. Cependant, il peut être nécessaire de faire la distinction entre le nombre total d'instances et celui des instances de la classe dérivée. Il faut pour cela écrire la classe dérivée différemment.

Public Class ClPersonneSpecialisee Inherits ClPersonne Dim SpecialiteLocale Private Shared NbPersonnes As Integer Public Sub New() ' New redéfinie (sans Overloads ni Overrides) NbPersonnes += 1 End Sub

Public Overloads Sub Dispose() ' Dispose surchargé. NbPersonnes -= 1 ' Fait son travail. Le ramasse-miettes End Sub ' s'occupera de la classe mère plus tard

Public Property Specialite() As String Get Return SpecialiteLocale End Get Set(ByVal UneValeur As String) SpecialiteLocale = UneValeur End Set End Property

Page 209: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 18

Public Overloads ReadOnly Property NombreInstance() As Integer Get ' La méthode d'accès au nombre d'instances Return NbPersonnes ' est surchargée. End Get End Property End Class Sub Main() Dim UnePersonne As ClPersonne UnePersonne = New ClPersonne Console.WriteLine(UnePersonne.NombreInstance) ' Affichage 1

Après avoir instancié UnePersonne de type ClPersonne, sa propriété NombreInstance contient bien comme valeur le nombre de ClPersonne instanciées, soit 1 après cette première instanciation.

Dim UneAutrePersonne As ClPersonne UneAutrePersonne = New ClPersonneSpecialisee Console.WriteLine(UnePersonne.NombreInstance) ' Affichage 2 Console.WriteLine(UneAutrePersonne.NombreInstance) ' Affichage 2

Après avoir instancié UneAutrePersonne de type ClPersonneSpecialisee sous la référence de sa classe mère ClPersonne, la propriété NombreInstance de ClPersonne a bien été incrémentée. Elle peut être consultée à partir de l'objet UnePersonne aussi bien qu'à partir de l'objet UneAutrePersonne. Elle vaut 2 après cette deuxième instanciation. Aucun pointeur de ClPersonneSpecialisee n'a encore été spécifié et il est impossible d'accéder à la propriété NombreInstance de cette classe. C'est pourtant son constructeur qui a été appelé pour instancier UneAutrePersonne …

Dim UnePersonneSpecialisee As ClPersonneSpecialisee UnePersonneSpecialisee = New ClPersonneSpecialisee Console.WriteLine(UnePersonne.NombreInstance) ' Affichage 3 Console.WriteLine(UneAutrePersonne.NombreInstance) ' Affichage 3 Console.WriteLine(UnePersonneSpecialisee.NombreInstance)' Affichage 2 ' … … …

Après avoir instancié UnePersonneSpecialisee de type ClPersonneSpecialisee sous la référence appropriée, la consultation de la méthode NombreInstance de l'objet UnePersonne indique bien une instance de plus, soit 3. La même consultation à partir de l'objet UneAutrePersonne donne le même résultat. Mais la propriété consultée par UnePersonneSpecialisee donne 2 alors qu'on ne dispose effectivement que d'un seul objet de type ClPersonneSpecialisee. Ces exemples mettent en évidence que la déclaration des objets détermine celui dont les membres sont sollicités. Ainsi, les déclarations Dim UnePersonne As ClPersonne et Dim UneAutrePersonne As ClPersonne ont pour effet que la propriété NombreInstance accédée est bien celle de la classe ClPersonne, même si l'instanciation fait appel au constructeur d'une classe dérivée comme dans UneAutrePersonne = New ClPersonneSpecialisee. Mais attention dans ce cas, même si ce n'est pas visible par défaut de pointeur, les valeurs internes affectées par le constructeur de l'objet ClPersonneSpecialisee ont bien été changées. Dérivations publique, protégée et privée La classe dérivée ClPersonneSpecialisee des exemples précédent est la représentation commune de la dérivation publique. Une classe publique dérivée d'une classe mère également publique, permet sa dérivation dans toute autre nouvelle classe. Les membres Public et Protected de la classe mère restent disponibles, chacun avec sa portée, dans les classes dérivées. Public Class ClPersonne Protected Sub Protection() ' … End Sub Public Sub PourTous() ' … End Sub

' … … … End Class

Page 210: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 19

Public Class ClPersonneSpecialisee Inherits ClPersonne Private Sub MaSub() MyBase.Protection() MyBase.PourTous() ' … End Sub

' … … … End Class

Public Class ClPersonneTresSpecialisee Inherits ClPersonneSpecialisee Private Sub UneSub() MyBase.Protection() ' MyBase connaît tous ses ascendants End Sub

End Class Les dérivations protégées et privées, lorsqu'elles sont réalisables, ont pour effet de limiter ou interdire la spécialisation et l'accès aux membres de la classe mère par l'usage de la classe ainsi dérivée. Ces dérivations ne sont pas possibles en VB, sauf à écrire autrement la classe mère, car toute instance d'une classe dérivée accède aux membres Public de sa classe mère. La dérivation protégée consiste en principe, au remplacement des portées Public de membres hérités par une portée Protected. Elle est réglée par le programmeur qui ne souhaite pas qu'une instance de la classe dérivée qu'il met au point, puisse accéder aux membres Public de sa classe mère. Il n'autorise cet accès qu'à des classes dérivées. La dérivation privée consiste à rendre Private, dans la classe dérivée, tous les membres hérités de sa classe mère. Constructeurs et destructeurs Le constructeur, qui est invoqué dans les codes clients par l'opérateur New, est une méthode New écrite ou non dans les classes utilisées. Si cette méthode n'est pas écrite, le constructeur par défaut, celui de la classe Object, est exécuté. Par contre si la méthode New est programmée dans une classe, c'est elle qui est exécutée lors de l'instanciation de cette classe. Plusieurs méthodes New peuvent être programmées dans une même classe à condition que les signatures soient différentes. Il s'agit du mécanisme de surcharge qui se programme ici sans usage du mot clé Overloads. La présence de plusieurs constructeurs dans une classe permet aux codes clients de choisir celui qui convient le mieux dans une situation donnée. Le constructeur est le moyen d'initialisation dynamique des données d'une classe. Un constructeur peut aussi être redéfini dans une classe dérivée. Il s'agit dans ce cas du mécanisme de surdéfinition qui se programme ici sans usage du mot clé Overrides. Plusieurs constructeurs peuvent être écrits dans une classe dérivée, en surdéfinition ou non de ceux de la classe mère. Si la classe mère possède un ou plusieurs constructeurs tous paramétrés, chaque constructeur d’une classe dérivée doit obligatoirement réaliser l’appel de l’un d’entre eux. Par contre, si la classe mère possède aussi un constructeur sans argument, ou si elle ne possède aucun constructeur, l’appel MyBase.New()n'est plus nécessaire. S'il est quand même programmé, c'est le constructeur par défaut de la classe mère qui est invoqué, c'est-à-dire celui sans argument s'il existe, ou bien celui de la classe Object. Le rôle du destructeur est intimement lié au fonctionnement du ramasse-miettes et il peut être utile de faire appel aux services de la classe System.GC (Garbage Collector). Le lecteur peut consulter l'aide en ligne de son Visual Studio.Net par une recherche sur GC pour obtenir davantage d'informations sur le sujet. L'important ici est de savoir comment mettre en œuvre la destruction explicite des objets. Le ramasse-miettes effectue le suivi des objets alloués dans la mémoire managée et en récupère l'espace lorsqu'il le peut. Ce traitement porte le nom de garbage collection. Il récupère la mémoire allouée aux objets qui ne possèdent pas de références valides. Il est aussi exécuté automatiquement lorsqu'une demande de mémoire ne peut pas être satisfaite avec la mémoire disponible. Il en résulte que la réinitialisation à Nothing de la dernière référence à un objet, livre ce dernier aux bons soins du ramasse-miettes. Mais si cet objet est lié à des ressources non managées, celles ci ne sont pas forcément libérées dès la destruction de l'objet. La destruction par défaut de tous les objets a lieu lors de l'arrêt de l'application qui les a instanciés et leurs ressources sont libérées ensuite. Si cette destruction par défaut est satisfaisante pour certaines applications, elle ne l'est plus lorsque les ressources sont sollicitées par d'autres applications, ni lorsque les applications tournent sur des serveurs, où elles ne sont peut-être jamais arrêtées et où, de toutes manières, les ressources sont pleinement sollicitées. Il y a donc des situations qui nécessitent un travail préliminaire à la destruction d'objets. Ce travail consiste en la libération des ressources utilisées par l'objet avant sa mise à la disposition du ramasse-miettes. Ces ressources sont des handles de fenêtres (hwnd), des connexions de bases de données, des références à des objets non managés tels que les COM, par exemple.

Page 211: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 20

Le programme ci contre peut intégrer la destruction de l'instance de MaClasse de deux façons. Soit il utilise Dispose comme précédemment et réinitialise sa référence à Nothing, soit il utilise une autre méthode Dispose qui se charge elle-même de réinitialiser la référence :

MonObjet.Dispose() MonObjet = Nothing

ou MonObjet.Dispose(MonObjet)

Le premier moyen de programmer un destructeur est de redéfinir la méthode Finalize. Si la classe pour laquelle est réalisée cette programmation dérive d'une classe qui n'est pas dotée d'une méthode Finalize, c'est celle de la classe Object qui est redéfinie. Une méthode Finalize ne doit jamais être appelée explicitement, elle est exécutée automatiquement lors de la destruction de l'objet. C'est la raison pour laquelle il est impératif d'accorder la portée Protected Overrides à toute redéfinition de la méthode. Cela permet aux classes dérivées de l'implémenter également et interdit son accès à partir d'une instance. Chaque classe dérivée peut ainsi programmer la libération de ses ressources. Pour rappel, l'appel du destructeur d'une classe dérivée ne doit pas être assorti de l'appel de celui de sa classe mère. Protected Overrides Sub Finalize() ' … Codes réalisant la libération des ressources utilisées End Sub De nombreuses classes du Framework présentent une méthode Dispose qui peut être utilisée pour libérer explicitement la mémoire occupée par un objet. Ces classes, dont la liste peut être consultée dans l'aide en ligne par une recherche sur IDisposable, ont en commun d'implémenter l'interface IDisposable. Il n'est pas encore question d'étudier l'Interface, mais il faut savoir qu'il regroupe une ou plusieurs déclarations de propriétés, méthodes et événements, sans codes, tous destinés à être implémentés dans les classes qui en font usage. Chaque application cliente qui implémente un Interface donné est donc contrainte d'en implémenter tous les membres. Ce mécanisme constitue un indice de compatibilité entre les classes implémentant les mêmes Interface, ainsi qu'un indice de conformité aux règles du DotNet. Programmer la destruction d'objets par l'implémentation de IDisposable, revient donc à fournir le code nécessaire à une méthode Dispose. Contrairement à la méthode Finalize qui ne doit pas être explicitement appelée, la méthode Dispose doit être explicitement appelée à partir du code client. Elle doit donc recevoir la portée Public. Public Class MaClasse Implements IDisposable Public Sub Dispose() Implements System.IDisposable.Dispose ' … Codes réalisant la libération des ressources utilisées End Sub ' … … … End Class Module MonModule Sub Main() Dim MonObjet As MaClasse MonObjet = New MaClasse ' … … … MonObjet.Dispose() MonObjet = Nothing End Sub End Module La libération programmée dans le destructeur des ressources utilisées par l'objet ne dispense pas de détruire l'objet. Le destructeur Dispose s'exécute exactement au moment voulu par le programmeur et la réinitialisation de la référence de l'objet à Nothing le met à la disposition du ramasse-miettes. Il n'y a dès lors plus de raison que soit exécutée la méthode Finalize. Il peut arriver que l'application cliente oublie l'appel explicite de la méthode Dispose alors que Finalize ne doit pas être appelée. Une combinaison des méthodes Finalize et Dispose est intéressante. Module MonModule Sub Main() Dim MonObjet As MaClasse MonObjet = New MaClasse ' … … … ' Destruction de l'objet End Sub End Module

Page 212: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 21

La classe MaClasse ci après réalise une bonne combinaison des méthodes Finalize et Dispose. Elle propose deux méthodes Dispose, l'une ordinaire, qui contient les codes de libération des ressources, est munie d'une variable Static pour prévenir l'exécution inutile de son code. Elle empêche l'exécution redondante de Finalize. L'autre, qui est une surcharge de la précédente, appelle celle ci pour la libération des ressources et réinitialise la référence de l'objet à Nothing. Enfin, la classe est dotée d'une méthode Finalize qui n'est exécutée que si le programmeur de l'application cliente oublie l'appel de Dispose. Public Class MaClasse Implements IDisposable Public Overloads Sub Dispose() Implements System.IDisposable.Dispose Static Detruit As Boolean If Detruit Then Exit Sub ' … Codes réalisant la libération des ressources utilisées Detruit = True GC.SuppressFinalize(Me) ' La méthode SuppressFinalize de la classe GC End Sub ' empêche l'exécution de la méthode Finalize Public Overloads Sub Dispose(ByRef Ref As MaClasse) Dispose() Ref = Nothing End Sub Protected Overrides Sub Finalize() Dispose() End Sub ' … … … End Class Constructeur par recopie Dans l'exemple suivant, l'affectation d'un objet à une autre référence que celle sous laquelle il a été instancié, copie dans cette nouvelle référence le contenu de la précédente, c'est-à-dire l'adresse de l'objet dans le tas. Il en résulte l'existence de deux références désignant le même objet et sa modification par l'une est visible par l'autre. Dim A, B As ArrayList A = New ArrayList A.Add("QWERTY") Console.WriteLine(A.Item(0)) ' Affiche : QWERTY B = A A.Item(0) = "AZERTY" ' La modification de A modifie B Console.WriteLine(A.Item(0)) ' Affiche : AZERTY Console.WriteLine(B.Item(0)) ' Affiche : AZERTY Cependant, il est fréquent que le programmeur souhaite instancier un nouvel objet dans un état strictement identique à un autre à ce moment. De nombreuses classes du Framework offrent une méthode Clone qui permet cette programmation. C'est le constructeur par recopie. Il ne faut toutefois pas oublier que ce copieur ne copie pas les valeurs des types références contenues dans l’objet à copier, mais seulement les références. Dim A, B As ArrayList A = New ArrayList A.Add("QWERTY") Console.WriteLine(A.Item(0)) ' Affiche : QWERTY B = A.Clone() A.Item(0) = "AZERTY" ' La modification de A n'affecte pas B Console.WriteLine(A.Item(0)) ' Affiche : AZERTY Console.WriteLine(B.Item(0)) ' Affiche : QWERTY Le programmeur peut également programmer un constructeur par recopie dans ses classes. L'exemple suivant illustre la programmation et l'usage d'une méthode Clone qui permet effectivement l'instanciation d'un nouvel objet par copie d'un existant.

Page 213: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 22

La méthode Clone doit affecter toutes les propriétés du nouvel objet avec les valeurs des propriétés de l'objet existant. Dans cet exemple, UneClasse n'a qu'une seule valeur à copier. Si elle en avait eu plusieurs, elle aurait du posséder aussi une méthode surchargée New() sans argument et la méthode Clone aurait du être programmée comme ceci : Public Function Clone() As UneClasse Dim Nouveau As UneClasse Nouveau = New UneClasse Nouveau.Element = Elem ' … … … Return Nouveau End Class

Public Class UneClasse Dim Elem As Integer Public Sub New(ByVal ArgElem As Integer) Elem = ArgElem End Sub Public Function Clone() As UneClasse Return New UneClasse(Elem) End Function Public Property Element() Get Return Elem End Get Set(ByVal Valeur) Elem = Valeur End Set End Property End Class

Module MonModule Sub Main() Dim X, Y As UneClasse X = New UneClasse(5) Console.WriteLine(X.Element) ' Affiche : 5 Y = X.Clone() X.Element = 7 ' La modification de X n'affecte pas Y Console.WriteLine(X.Element) ' Affiche : 7 Console.WriteLine(Y.Element) ' Affiche : 5 End sub End Module Jusqu'ici, les principaux membres de nombreuses classes du Framework ont été étudiés. Ces membres sont toujours accessibles sur simple instanciation. Ce sont des membres déclarés Public dans les classes dont ils constituent les interfaces. Il faut savoir aussi, pour en terminer avec l'héritage, que presque toutes ces classes possèdent également des membres Protected qui sont des voies ouvertes à la spécialisation. C'est une fois de plus la consultation de l'aide en ligne du Visual Studio qui permet l'obtention de la liste des membres protégés de chaque classe. La même consultation permet aussi de se rendre compte que certaines classes possèdent aussi, ou seulement, des membres de type Interface accessibles par implémentation de la classe. Il est temps d'en dire un peu plus sur ces Interface. Interface et Implements Les Interface, qui peuvent être considérées comme des Class Interface, afin de ne pas les confondre avec les interfaces de classes ou les interfaces utilisateurs (ou IHM pour Interface Homme Machine), permettent la déclaration d'ensembles de membres sans code. Ces déclarations, qui ne peuvent être que des Property, Sub, Function et Event, ne peuvent pas être assorties d'attributs de portée, ni des fins de blocs usuels (End Property, End Sub, …). Les codes des membres ainsi déclarés doivent être tous produits dans les classes qui implémentent l'Interface. Cela signifie notamment que plusieurs classes implémentant les mêmes Interface peuvent offrir une interface de classe identique, ou contenant au moins une même liste de membres, ceux définis dans les Interface implémentés.

Public Interface MonInterface Sub UneProcedure(ByVal X As Integer) Function UneFonction(ByVal S As String) As Integer Property UnePropriete() End Interface Public Class UneClasse Implements MonInterface Public Sub UneProcedure(ByVal Entier As Integer)

Implements MonInterface.UneProcedure ' … … … End Sub

Page 214: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 23

Public Function UneFonction(ByVal Chaine As String) As Integer Implements MonInterface.UneFonction

' … … … End Function Public Property UnePropriete() Implements MonInterface.UnePropriete Get ' … … … End Get Set(ByVal Value) ' … … … End Set End Property ' … … … End Class

L'héritage multiple n'est pas possible en VB, mais l'utilisation des Interface permet de résoudre sa programmation le cas échéant. Ainsi, l'illustration de l'héritage multiple présentée précédemment est réalisable par l'usage des Interface. Les classes définies dans l'illustration ne possèdent pas de membres homonymes et les Interface les représentant peuvent être écrites comme ceci :

Public Interface IVehicule Property Proprietaire() Property Marque() Property NoPlaque() Sub Avancer(ByVal Distance As Integer) Sub Reculer(ByVal Distance As Integer) Sub Arreter() End Interface Public Interface IVoiture Inherits IVehicule Property NbPlaces() Function Liste_Passagers() As String() End Interface Public Interface ICamion Inherits IVehicule Property Tonnage() Property TypeChargement() End Interface Public Interface ICamionnette Inherits IVoiture Inherits ICamion Property Type_Utilisation() End Interface Public Class ClCamionnette Implements ICamionnette ' … et il n'y plus qu'à programmer chaque membre de chaque Interface …

Il est permis de donner aux membres implémentés et à leurs arguments, des noms locaux différents de ceux déclarés dans l'Interface d'origine.

Public Interface MonInterface Sub UneProcedure(ByVal X As Integer) End Interface Public Class UneClasse Implements MonInterface Public Sub MaSub(ByVal Entier As Integer) Implements MonInterface.UneProcedure ' … … … End Sub

Page 215: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 24

Mais l'Interface n'a pas pour vocation première de combler l'absence de mécanisme d'héritage multiple en VB.Net. La création d'une procédure de tri capable de trier n'importe quel objet est un exemple plus approprié de son usage. Public Class ClPersonne ' La classe ClPersonne est l'objet de base de Dim NomLocal As String ' de cet exemple Public Property Nom() As String Get Return NomLocal End Get Set(ByVal UneValeur As String) NomLocal = UneValeur End Set End Property End Class Public Interface IComparer ' L'Interface de l'exemple Function Compare(ByVal Objet As Object, ByVal Index As Byte) As Integer ReadOnly Property Information(ByVal Choix As Byte) As Object End Interface Public Class ClTri ' La classe ClTri capable de trier toute ' table d'objets implémentant IComparer Public Sub Tri(ByVal Table As IComparer(), _

Optional ByVal CleTri As Integer = 1, _ Optional ByVal Sens As Integer = 1)

Dim Tmp As Object ' CleTri désigne le membre de l'objet sur Dim i As Integer ' lequel doit s'effectuer le tri. C'est le Dim Permutation As Boolean = True ' premier par défaut While Permutation ' Sens vaut 1 par défaut et provoque un tri Permutation = False ' croissant, sa valeur doit être -1 pour un For i = 0 To Table.Length - 2 ' tri décroissant If Table(i).Compare(Table(i + 1), CleTri) = Sens Then Tmp = Table(i) Table(i) = Table(i + 1) Table(i + 1) = Tmp Permutation = True End If Next End While End Sub End Class Public Class ClEtudiant ' La classe ClEtudiant est une spécialisation Inherits ClPersonne ' de la classe ClPersonne et elle implémente Implements IComparer ' IComparer Private Resultat As Single Private Section As String Public ReadOnly Property Information(ByVal Choix As Byte) As Object

Implements IComparer.Information Get Select Case Choix Case 1 Return Nom Case 2 Return Resultat Case 3 Return Section End Select End Get End Property

Page 216: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 25

Public Sub New(ByVal ArgNom As String, ByVal ArgResultat As Single, ByVal ArgSection As String)

Nom = ArgNom Resultat = ArgResultat Section = ArgSection End Sub Public Function Compare(ByVal Objet As Object, ByVal Index As Byte) As Integer

Implements IComparer.Compare Select Case Me.Information(Index) Case Is < Objet.Information(Index) Return -1 Case Is = Objet.Information(Index) Return 0 Case Is > Objet.Information(Index) Return 1 End Select End Function End Class Module MonModule ' Une application cliente de la classe Sub Main() ' ClEtudiant et du tri polyvalent ClTri.Tri Dim MesEtudiant(5) As ClEtudiant Dim Tri As ClTri = New ClTri Dim i As Integer MesEtudiant(0) = New ClEtudiant("Michel", 10, "Linux") MesEtudiant(1) = New ClEtudiant("Marc", 15, "Windows") MesEtudiant(2) = New ClEtudiant("Pierre", 12, "Analyse") MesEtudiant(3) = New ClEtudiant("Albert", 17, "Windows") MesEtudiant(4) = New ClEtudiant("Christian", 16, "Analyse") MesEtudiant(5) = New ClEtudiant("Bruno", 19, "Linux") ' Avant le tri For i = 0 To MesEtudiant.Length - 1 With MesEtudiant(i) Console.WriteLine(.Information(1)) Console.WriteLine(.Information(2)) Console.WriteLine(.Information(3)) End With Next Tri.Tri(MesEtudiant, 2, -1) ' Après le tri For i = 0 To MesEtudiant.Length - 1 With MesEtudiant(i) Console.WriteLine(.Information(1)) Console.WriteLine(.Information(2)) Console.WriteLine(.Information(3)) End With Next End Sub End Module La modification du code précédent en vue d'expérimenter ClTri.Tri sur une table de ClPersonne provoque la levée d'une exception lors de l'exécution car ClPersonne n'implémente pas l'Interface requise.

Dim MesEtudiant(5) As ClEtudiant Dim MesPersonnes(5) As ClPersonne Dim Tri As ClTri = New ClTri Dim i As Integer MesEtudiant(0) = New ClEtudiant("Michel", 10, "Linux") ' … … … For i = 0 To MesEtudiant.Length - 1

MesPersonnes(i) = New ClPersonne MesPersonnes(i) = MesEtudiant(i)

Next ' Tri.Tri(MesPersonnes, 1, -1) ' Erreur à l'exécution ' … … …

Page 217: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 26

Le polymorphisme Le polymorphisme est un ensemble de mécanismes permettant à une application d'instancier plusieurs classes avec des propriétés et des méthodes homonymes dont les implémentations sont différentes. Certains différencient les polymorphismes en polymorphisme de spécialisation, de redéfinition, de surcharge, lesquels peuvent encore recevoir des appelations différentes, selon les auteurs. Il n'est pas utile ici de faire la part des choses entre les points de vues de ces différents auteurs. Toutefois leurs diverses approches sous entendent des modes de programmation différents et des comportements appropriés des compilateurs. Ces particularités sont mises en évidence dans les exemples traités ci après. Les différentes définitions et explications qui traitent des polymorphismes ont une idée commune : les méthodes concernées, qui sont dites méthodes polymorphes, peuvent être utilisées sans considération du type d'objets auxquels elles sont appliquées et ce, de manière transparente pour le programmeur de l'application cliente. Elles s'adaptent à l'objet pour lequel elles sont invoquées. Dans l'exemple précédent, la méthode Compare de l'Interface IComparer, est bien une méthode polymorphe puisqu'elle peut comparer n'importe quel objet, sans différenciation ni spécification de type. Il s'agit dans cet exemple, d'un polymorphisme de spécialisation, dit aussi de sous typage ou de dérivation. Il découle directement de l'héritage. C'est le polymorphisme ordinairement associé au concept objet. L'idée est de partir d'un type et de le modifier, de créer une classe de base et d'en faire des classes dérivées. Toutefois, l'héritage ne procure pas le polymorphisme dans tous les cas. Le polymorphisme de spécialisation s'obtient par la programmation d'une classe de base, éventuellement abstraite, dans laquelle sont déclarées des méthodes virtuelles ou définies des méthodes spécifiées redéfinissables. L'application cliente peut utiliser la classe de base qui va automatiquement appeler les méthodes des classes dérivées appropriées selon le type d'objet à traiter. Classe abstraite et méthode virtuelle Une méthode virtuelle ne peut être déclarée qu'à l'intérieur d'une classe abstraite. Une classe est déclarée abstraite par le mot clé MustInherit et une méthode est déclarée virtuelle par MustOverride. Comme dans le cas de l'Interface, une méthode virtuelle n'est représentée dans une classe abstraite que par sa déclaration, sans aucun code ni fin de bloc. Contrairement à l'Interface, la classe abstraite peut contenir d'autres membres et même des méthodes sans code mais non virtuelle et dont le comportement est différent. Public MustInherit Class UneClasseAbstraite Public MustOverride Function UneVirtuelle() As String End Class Public Class UneClasseDerivee Inherits UneClasseAbstraite Public Overrides Function UneVirtuelle() As String Return "Fonction en UneClasseDerivee" End Function End Class Module MonModule Sub Main() Dim ObjetUneClasseAbstraite As UneClasseAbstraite Dim ObjetUneClasseDerivee As UneClasseDerivee ObjetUneClasseAbstraite = New UneClasseDerivee ObjetUneClasseDerivee = New UneClasseDerivee Console.WriteLine("1 " & ObjetUneClasseAbstraite.UneVirtuelle) Console.WriteLine("2 " & ObjetUneClasseDerivee.UneVirtuelle) End Sub End Module Les exemples étudiés précédemment dans les cas d'héritages simples montrent que le type déclaré d'une référence d'objet détermine si les membres accédés sont ceux de la classe mère ou ceux de la classe dérivée. Mais lorsque ces membres sont des méthodes virtuelles, ce sont les méthodes correspondantes de la classe dérivée qui sont invoquées quel que soit le type de la référence. C'est précisément l'accès aux méthodes d'une classe dérivée par une référence de sa classe mère qui induit le polymorphisme.

Page 218: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 27

L'exemple suivant illustre que, quel que soit l'objet instancié, la référence à la classe mère conduit bien à la méthode de la classe dérivée appropriée. Public MustInherit Class UneClasseAbstraite Public MustOverride Function UneVirtuelle() As String End Class Public Class UneClasseDerivee Inherits UneClasseAbstraite Public Overrides Function UneVirtuelle() As String Return "Fonction en UneClasseDerivee" End Function End Class Public Class UneAutreDerivee Inherits UneClasseAbstraite Public Overrides Function UneVirtuelle() As String Return "Fonction en UneAutreDerivee" End Function End Class Module MonModule Sub Main() Dim UnObjet As UneClasseAbstraite UnObjet = New UneClasseDerivee Console.WriteLine("1 " & UnObjet.UneVirtuelle) UnObjet = New UneAutreDerivee Console.WriteLine("2 " & UnObjet.UneVirtuelle) Console.ReadLine() End Sub End Module Un autre exemple illustre bien le polymorphisme. Une classe abstraite ClForme contient une méthode virtuelle Surface. Diverses classes dérivées implémentent la méthode Surface. L'accès à leurs instances par une référence de type ClForme permet d'invoquer la méthode appropriée de manière transparente pour le programmeur. Public MustInherit Class ClForme Public MustOverride Function Surface() As Single End Class Public Class ClCarre Inherits ClForme Dim Cote As Single Public Sub New(ByVal Mesure As Single) Cote = Mesure End Sub Public Overrides Function Surface() As Single Return Cote * Cote End Function End Class Public Class ClRectangle Inherits ClForme Dim Longueur As Single Dim Largeur As Single Public Sub New(ByVal ArgLongueur As Single, ByVal ArgLargeur As Single) Longueur = ArgLongueur Largeur = ArgLargeur End Sub Public Overrides Function Surface() As Single Return Longueur * Largeur End Function End Class

Page 219: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 28

Public Class ClCercle Inherits ClForme Dim Rayon As Single Dim Diametre As Single Public Sub New(ByVal Mesure As Single, Optional ByVal EstRayon As Boolean = True) If EstRayon Then Rayon = Mesure Diametre = Rayon * 2 Else Diametre = Mesure Rayon = Diametre / 2 End If End Sub Public Overrides Function Surface() As Single Return Math.PI * Rayon * Rayon End Function End Class Public Class ClTriangle Inherits ClForme Dim Base As Single Dim Hauteur As Single Public Sub New(ByVal ArgBase As Single, ByVal ArgHauteur As Single) Base = ArgBase Hauteur = ArgHauteur End Sub Public Overrides Function Surface() As Single Return Base * Hauteur / 2 End Function End Class Module MonModule Sub Main() Dim MaForme As ClForme MaForme = New ClCarre(5) ' Instanciation d'un carré Console.WriteLine(MaForme.Surface) ' Obtention de l'aire de ce carré MaForme = New ClRectangle(5, 3) ' Instanciation d'un rectangle Console.WriteLine(MaForme.Surface) ' Obtention de l'aire de ce rectangle MaForme = New ClCercle(5) ' Instanciation d'un cercle par son rayon Console.WriteLine(MaForme.Surface) ' Obtention de l'aire de ce cercle MaForme = New ClCercle(10, False) ' Instanciation d'un cercle par son diamètre Console.WriteLine(MaForme.Surface) ' Obtention de l'aire de ce cercle MaForme = New ClTriangle(5, 3) ' Instanciation d'un triangle Console.WriteLine(MaForme.Surface) ' Obtention de l'aire de ce triangle End Sub End Module Lors de l'étude de l'Interface, un exemple montre une méthode de tri capable de trier n'importe quel vecteur d'objets pourvu qu'ils implémentent l'Interface IComparer. Le tri est testé sur un vecteur d'objets ClEtudiant, dont la classe hérite de la classe ClPersonne et implémente l'Interface IComparer. Cet exemple peut être réalisé presque de la même manière par l'usage d'une classe abstraite ClComparer. Le tri peut alors traiter n'importe quel vecteur d'objets qui héritent de ClComparer et en implémentent les membres. Mais dans ce nouvel exemple, la classe ClEtudiant ne peut hériter à la fois de ClPersonne et de ClComparer puisque l'héritage multiple n'est pas possible. Pour cet exemple, le seul membre de ClPersonne utilisé précédement, sa propriété Nom, a été intégré dans la classe ClEtudiant. L’autre façon de faire, pour conserver la classe ClPersonne, était que ce soit cette dernière qui hérite de ClComparer. La classe ClEtudiant dérivant de ClPersonne en aurait hérité aussi. L'application cliente, le module MonModule est strictement identique à celui de l'exemple concernant l'Interface et n'est pas reproduit ici. Public MustInherit Class ClComparer ' La classe abstraite de l'exemple MustOverride Function Compare(ByVal Objet As Object, ByVal Index As Byte) As Integer MustOverride ReadOnly Property Information(ByVal Choix As Byte) As Object End Class

Page 220: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 29

Public Class ClTri ' La classe ClTri capable de trier toute ' table d'objets qui héritent de ClComparer Public Sub Tri(ByVal Table As ClComparer(), _

Optional ByVal CleTri As Integer = 1, _ Optional ByVal Sens As Integer = 1)

Dim Tmp As Object ' CleTri désigne le membre de l'objet sur Dim i As Integer ' lequel doit s'effectuer le tri. C'est le Dim Permutation As Boolean = True ' premier par défaut While Permutation ' Sens vaut 1 par défaut et provoque un tri Permutation = False ' croissant, sa valeur doit être -1 pour un For i = 0 To Table.Length - 2 ' tri décroissant If Table(i).Compare(Table(i + 1), CleTri) = Sens Then Tmp = Table(i) Table(i) = Table(i + 1) Table(i + 1) = Tmp Permutation = True End If Next End While End Sub End Class Public Class ClEtudiant ' La classe ClEtudiant hérite de la Inherits ClComparer ' classe abstraite ClComparer et en Private Resultat As Single ' implémente les méthodes virtuelles Private Section As String Private NomLocal As String Public Sub New(ByVal ArgNom As String, ByVal ArgResultat As Single,

ByVal ArgSection As String) Nom = ArgNom : Resultat = ArgResultat : Section = ArgSection End Sub Public Property Nom() As String Get Return NomLocal End Get Set(ByVal UneValeur As String) NomLocal = UneValeur End Set End Property Public Overrides ReadOnly Property Information(ByVal Choix As Byte) As Object Get Select Case Choix Case 1 Return Nom Case 2 Return Resultat Case 3 Return Section End Select End Get End Property Public Overrides Function Compare(ByVal Objet As Object, ByVal Index As Byte)

As Integer Select Case Me.Information(Index) Case Is < Objet.Information(Index) Return -1 Case Is = Objet.Information(Index) Return 0 Case Is > Objet.Information(Index) Return 1 End Select End Function End Class

Page 221: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 30

Surdéfinition, surcharge et masquage La surdéfinition, ou redéfinition, est un mécanisme qui permet de récrire une méthode déjà définie ou simplement déclarée, comme cela se fait pour l'implémentation des méthodes virtuelles. Les signatures des méthodes redéfinies doivent toujours être identiques. Mots clés : MustOverride, NotOverridable, Overrides et Overridable. La surcharge, qui est un mécanisme très semblable à la redéfinition, mais différent dans ses effets, permet également de récrire des méthodes déjà définies. Il peut être utilisé pour offrir un choix de méthodes virtuelles dans une classe abstraite, mais ne peut servir à l'implémentation de ces méthodes pour réaliser le polymorphisme de spécialisation. Les signatures des méthodes surchargées doivent être différentes au sein d'une même classe, mais dans une classe dérivée, elles peuvent être identiques à leurs homologues de la classe mère. Mot clé : Overloads. Le masquage est équivalent à la surcharge avec la différence essentielle qu'il permet la redéclaration de variables publiques d'une classe mère. Le masquage réalise un effet de masque là où celui ci est inexistant par défaut. Au sein d'une même classe, il ne peut masquer des variables et il est synonyme de surcharge en ce qui concerne les méthodes, mais il ne peut surcharger des méthodes virtuelles. Il faut noter encore que la redéclaration de variables n'est pas une bonne pratique de la POO, ne serait-ce que par défaut de contrôle d’accès. Mot clé : Shadows. Quelques différences existant entre ces trois mécanismes sont illustrées dans l'exemple suivant. Public Class ClBase ' La classe de base contient 4 méthodes Public Overridable Function F1() As String ' redéfinissables Return "F1 en ClBase" End Function Public Overridable Function F2() As String Return "F2 en ClBase" End Function Public Overridable Function F3() As String Return "F3 en ClBase" End Function Public Overridable Function F4() As String Return "F4 en ClBase" End Function End Class Public Class ClDerivee ' La classe de ClDerivee hérite de ClBase, Inherits ClBase ' en redéfinit F1 et surcharge F2 en la ' spécifiant redéfinissable Public Overrides Function F1() As String Return "F1 en ClDerivee" End Function Public Overridable Overloads Function F2() As String Return "F2 en ClDerivee" End Function End Class Public Class ClSecondaire ' La classe de ClSecondaire hérite de Inherits ClDerivee ' ClDerivee, et donc aussi de ClBase,

' redéfinit F2 de ClDerivee, F3 de ClBase, Public Overrides Function F2() As String ' et masque F4 de ClBase Return "F2 en ClSecondaire " End Function Public Overrides Function F3() As String Return "F3 en ClSecondaire" End Function Public Shadows Function F4() As String Return "F4 en ClSecondaire" End Function End Class

Page 222: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 31

Module MonModule Sub Main() Dim RefBaseD As ClBase Dim RefBaseS As ClBase Dim RefDerive As ClDerivee Dim UnDerive As ClDerivee Dim UnSecond As ClSecondaire RefBaseD = New ClDerivee ' ClDerivee par référence à sa classe mère UnDerive = New ClDerivee ' ClDerivee par référence à sa classe RefBaseS = New ClSecondaire ' ClSecondaire par référence à sa grand-mère RefDerive = New ClSecondaire ' ClSecondaire par référence à sa classe mère UnSecond = New ClSecondaire ' ClSecondaire par référence à sa classe

' Accès une instance de ClDerive par une référence de sa classe mère ClBase Console.WriteLine("11 " & RefBaseD.F1) ' Affiche : 11 F1 en ClDerivee Console.WriteLine("12 " & RefBaseD.F2) ' Affiche : 12 F2 en ClBase Console.WriteLine("13 " & RefBaseD.F3) ' Affiche : 13 F3 en ClBase Console.WriteLine("14 " & RefBaseD.F4) ' Affiche : 14 F4 en ClBase

' Accès une instance de ClDerive par une référence de sa classe Console.WriteLine("21 " & UnDerive.F1) ' Affiche : 21 F1 en ClDerivee Console.WriteLine("22 " & UnDerive.F2) ' Affiche : 22 F2 en ClDerivee Console.WriteLine("23 " & UnDerive.F3) ' Affiche : 23 F3 en ClBase Console.WriteLine("24 " & UnDerive.F4) ' Affiche : 24 F4 en ClBase

' Accès une instance de ClSecondaire par une référence de sa classe "grand-mère" ClBase Console.WriteLine("31 " & RefBaseS.F1) ' Affiche : 31 F1 en ClDerivee Console.WriteLine("32 " & RefBaseS.F2) ' Affiche : 32 F2 en ClBase Console.WriteLine("33 " & RefBaseS.F3) ' Affiche : 33 F3 en ClSecondaire Console.WriteLine("34 " & RefBaseS.F4) ' Affiche : 34 F4 en ClBase

' Accès une instance de ClSecondaire par une référence de sa classe mère ClDerive Console.WriteLine("41 " & RefDerive.F1) ' Affiche : 41 F1 en ClDerivee Console.WriteLine("42 " & RefDerive.F2) ' Affiche : 42 F2 en ClSecondaire Console.WriteLine("43 " & RefDerive.F3) ' Affiche : 43 F3 en ClSecondaire Console.WriteLine("44 " & RefDerive.F4) ' Affiche : 44 F4 en ClBase

' Accès une instance de ClSecondaire par une référence de sa classe Console.WriteLine("51 " & UnSecond.F1) ' Affiche : 51 F1 en ClDerivee Console.WriteLine("52 " & UnSecond.F2) ' Affiche : 52 F2 en ClSecondaire Console.WriteLine("53 " & UnSecond.F3) ' Affiche : 53 F3 en ClSecondaire Console.WriteLine("54 " & UnSecond.F4) ' Affiche : 54 F4 en ClSecondaire End Sub End Module Le tableau de la page suivante explique les résultats constatés à l'exécution entre les différentes combinaisons testées. Il s’en dégage les généralités suivantes, donc certaines ont déjà été mises en évidence dans ces pages. Quand un objet est manipulé par une référence à sa classe, tous ses membres et tous ceux dont il hérite de ses classes ancêtres sont accessibles. Quand un objet est manipulé par une référence à une de ses classes ancêtres, seuls les membres de cette dernière et ceux dont elle a hérités sont accessibles. Toutefois, si cette classe ancêtre de la référence, ou une de ses ancêtres, possède des membres redéfinissables, ceux-ci sont recherchés par le compilateur depuis la classe instanciée et jusque la classe où ils sont spécifiés redéfinissables. C'est le membre redéfini le plus proche généalogiquement de la classe instanciée qui est invoqué. C'est la liaison dynamique. Il en résulte que toute méthode spécifiée redéfinissable provoque un travail de recherche et d'analyse par le compilateur. Ce travail ne pouvant se faire qu'au détriment de la rapidité d'exécution, il faut limiter cette pratique aux seuls cas dûment justifiés par une nécessité de polymorphisme. Ce dernier cas de figure s'illustre bien dans les affichages 43 et 44 de l'exemple. L'objet de type ClSecondaire est manipulé par une référence à sa classe mère ClDerivee. Celle-ci hérite de ClBase où toutes les méthodes sont spécifiées redéfinissables. En 43, la méthode F3 de ClSecondaire, qui est une redéfinition de F3 de ClBase, est trouvée par le compilateur et invoquée. En 44, la méthode F4 de ClSecondaire n'est pas une redéfinition. Elle n’est pas davantage redéfinie en ClDerivee. En remontant la généalogie de ces classes, le compilateur trouve F4 redéfinissable en ClBase et l'invoque.

Page 223: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 32

Numéro affichage

Type de la référence

Classe instanciée Répondant Explication

11

ClBase ClDerivee

F1 de ClDerivee

Polymorphisme. La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et dans ClDerivee, F1 redéfinit F1 de ClBase.

12 F2 de ClBase

La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et F2 n'est que surchargée en ClDerivee.

13 F3 de ClBase

La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et F3 n'existe pas en ClDerivee.

14 F4 de ClBase

La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et F4 n'existe pas en ClDerivee.

21

ClDerivee ClDerivee

F1 de ClDerivee

La manipulation d'un objet par une référence à sa propre classe permet d'atteindre tous ses membres, ainsi que ceux dont il hérite, et F1 est membre de ClDerive où il redéfinit un membre de ClBase.

22 F2 de

ClDerivee

La manipulation d'un objet par une référence à sa propre classe permet d'atteindre tous ses membres, ainsi que ceux dont il hérite, et F2 est membre de ClDerivee où il surcharge un membre de ClBase.

23 F3 de ClBase

Héritage simple. La manipulation d'un objet par une référence à sa propre classe permet d'atteindre tous ses membres, ainsi que ceux dont il hérite, et ClDerivee hérite F3 de ClBase.

24 F4 de ClBase

Héritage simple. La manipulation d'un objet par une référence à sa propre classe permet d'atteindre tous ses membres, ainsi que ceux dont il hérite, et ClDerivee hérite F4 de ClBase.

31

ClBase ClSecondaire

F1 de ClDerivee

Héritage et polymorphisme. La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et ClSecondaire hérite de ClDerivee qui redéfinit F1 de ClBase.

32 F2 de ClBase

Héritage. La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et F2 n'est que surchargée en ClDerivee.

33 F3 de

ClSecondaire

Polymorphisme. La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et dans ClSecondaire, F3 redéfinit F3 de ClBase.

34 F4 de ClBase

La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et en ClSecondaire, F4 n'est pas une redéfinition de F4 de ClBase.

41

ClDerivee ClSecondaire

F1 de ClDerivee

Héritage. La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et F1 n'existe pas en ClSecondaire.

42 F2 de

ClSecondaire

Polymorphisme. La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et dans ClSecondaire , F2 redéfinit F2 de ClDerivee (et non F2 de ClBase).

43 F3 de

ClSecondaire

Polymorphisme. La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et dans ClSecondaire, F3 redéfinit F3 de ClBase.

44 F4 de ClBase

La manipulation d'un objet par une référence à sa classe mère ne permet d'atteindre que les membres de cette dernière, sauf en cas de redéfinition, et en ClSecondaire, F4 n'est pas une redéfinition de F4 de ClBase.

51

ClSecondaire ClSecondaire

F1 de ClDerivee

Héritage simple. La manipulation d'un objet par une référence à sa propre classe permet d'atteindre tous ses membres, ainsi que ceux dont il hérite, et ClSecondaire hérite F1 de ClDerivee où il redéfinit un membre de ClBase.

52 F2 de

ClSecondaire

La manipulation d'un objet par une référence à sa propre classe permet d'atteindre tous ses membres, ainsi que ceux dont il hérite, et F2 est membre de ClSecondaire où il redéfinit un membre de ClDerivee.

53 F3 de

ClSecondaire

La manipulation d'un objet par une référence à sa propre classe permet d'atteindre tous ses membres, ainsi que ceux dont il hérite, et F3 est membre de ClSecondaire où il redéfinit un membre de ClBase.

54 F4 de

ClSecondaire

La manipulation d'un objet par une référence à sa propre classe permet d'atteindre tous ses membres, ainsi que ceux dont il hérite, et F4 est membre de ClSecondaire où il masque un membre de ClBase.

Page 224: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 33

La surcharge des opérateurs La surcharge des opérateurs (depuis VS2005 en VB.Net) permet au programmeur de l’application cliente d’une classe de réaliser des opérations entre les objets instanciés en utilisant les opérateurs naturels pourtant non conçus pour de tels opérandes. Par exemple, la surcharge l’opérateur de soustraction de sorte que "AZY" = "AZERTY" - "ERT".

Public Class Fraction Dim Numer, Denom As Integer Public Sub New (ByVal ArgNumer As Integer, ByVal ArgDenom As Integer) Numer = ArgNumer Denom = ArgDenom End Sub Public Shared Operator +(ByVal a As Fraction, ByVal b As Fraction) As Fraction Return New Fraction(a.Numer * b.Denom + b.Numer * a.Denom, a.Denom * b.Denom) End Operator

Public Shared Operator *(ByVal a As Fraction, ByVal b As Fraction) As Fraction Return New Fraction(a.Numer * b.Numer, a.Denom * b.Denom) End Operator Public Shared Narrowing Operator CType(ByVal Valeur As Fraction) As String Return Valeur.ToString End Operator Public Overrides Function ToString() As String Return Numer.ToString & "/" & Denom.ToString End Function End Class

Les opérateurs de conversion doivent être déclarés avec un attribut de restriction (Narrowing) ou d'élargissement (Widening) indiquant la manière dont la conversion doit avoir lieu. Une conversion Widening change une valeur vers un type de donnée qui peut s'adapter à n'importe quelle valeur possible de la donnée d'origine, par exemple : Integer vers Decimal. Une conversion Narrowing change une valeur vers un type de donnée qui peut ne pas être capable de contenir certaines valeurs possibles de la donnée d'origine, par exemple : Decimal vers Integer. L'application cliente de la classe Fraction ci-dessus pourrait être :

Module MonModule Sub Main() Dim x As New Fraction(4, 3) Dim y As New Fraction(5, 2) Dim z As New Fraction(3, 5) Console.WriteLine((x + (y * z)).ToString) ' Affiche : 85/30 Console.WriteLine(CType(x, String)) ' Affiche : 4/3 End Sub End Module

Les opérateurs susceptibles d'être surchargés sont : +, -, *, /, \, ^, &, Like, Mod, And, Or, Xor, Not, <<, >>, =, <>, >, >=, <, <=, CType, IsTrue, IsFalse.

L'interdiction de dériver A l'inverse de la classe déclarée abstraite par le mot clé MustInherit, qui doit obligatoirement être dérivée à moins d'être inutile, une classe peut être déclarée dérivation interdite, ou plus exactement NotInheritable. Aucune classe ne peut en hériter et toutes ses méthodes sont NotOverridable, et non surchargeables et non masquables, quels que soient leurs attributs. Il faut noter que ce mécanisme ne palie pas à l'absence de dérivation protégée et privée en VB. Sans doute doit-il exister de bonnes raisons justifiant cette pratique égoïste et contraire au principe de réutilisabilité de la POO, mais sans doute aussi sont-elles suffisament rares pour que ce mécanisme n'encombre pas davantage ces pages.

Page 225: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 34

Les événements Les méthodes permettent aux applications de communiquer des ordres aux objets et ceux-ci répondent en renvoyant l'information demandée le cas échéant. Mais la programmation des objets permet de les doter d'émetteurs de signaux à l'intention de tout autre objet qui veut bien être à l'écoute. Ces signaux sont les événements. Lorsque survient un événement, l'application consommatrice de l'objet, si elle est à l'écoute de cet événement, peut le capter et réagir en conséquence. C'est ainsi que lorsqu'un objet TextBox est installé sur un formulaire, le code de l'application peut réagir aux événements générés par cet objet. Le TextBox émet des événements. Le programmeur choisit parmi les nombreux signaux du TextBox celui ou ceux qui conviennent à son application. Il met son application à l'écoute de certains événements émis par le TextBox. Il peut choisir de réagir à l'événement Click, ou à KeyPress, ou à d'autres, ou à aucun. C'est la logique de la programmation événementielle qui guide le programmeur. Emission d'événements Un événement se déclare dans la classe émettrice comme une variable, ou mieux comme une méthode d'Interface, c'est-à-dire sans code ni fin de bloc, pour laquelle le mot clé Function ou Sub est remplacé par Event. Cette déclaration ne peut se faire à l'intérieur d'une procédure ou d'une fonction. Ses attributs de portée sont les mêmes que ceux des fonctions et procédures. Les arguments d'une procédure Event ne peuvent être Optional, ni ParamArray. Par convention, les événements sous DotNet ont une même signature contenant deux arguments. Le premier, qui de type Object, désigne l'objet émetteur et le second de type EventArgs contient la liste des arguments à passer. Le programmeur peut respecter cette convention ou pas. La forme conventionnelle d'une procédure Event est la suivante :

Public Event UnEvenement(ByVal Sender As Object, ByVal e As EventArgs)

La francisation du code, généralement adoptée dans ce cours, présente l'avantage pour le débutant de différencier clairement les mots du langage et ceux du programmeur. La déclaration précédente peut s'écrire comme ceci :

Public Event UnEvenement(ByVal Emetteur As Object, ByVal Arguments As EventArgs) Le programmeur peut conserver la forme conventionnelle même lorsqu'il n'a pas d'argument à transmettre. Dans ce cas, il doit utiliser l'argument EventArgs.Empty. Mais s'il doit transmettre une information en même temps que l'événement, il doit créer un objet de type EventArgs contenant cette information, ou bien abandonner la forme conventionnelle. L'émission de l'événement est déclenchée par l'ordre RaiseEvent.

Public Class UneClasse Public Event CetEvenement(ByVal Emetteur As Object,

ByVal Arguments As EventArgs) Public Sub UneSub() Dim MomentEmission As Boolean = True ' … … … If MomentEmission Then ' Dans cet exemple, c'est toujours le moment RaiseEvent CetEvenement(Me, EventArgs.Empty) End If End Sub End Class

Soit, pour l'exemple, un événement générant trois informations à l'intention de l'objet qui souhaite réagir à son avènement. La première information est la désignation de l'objet émetteur, la deuxième est un message et la troisième est le moment en date et heure de la survenance de l'événement. Voici sa programmation sous une forme quelconque d'abord et sous la forme conventionnelle ensuite. Public Class UneClasse ' Forme non conventionnelle de CetEvenement

Public Event CetEvenement(ByVal Emetteur As Object, ByVal Message As String, ByVal Moment As DateTime)

Public Sub UneSub() ' … … … RaiseEvent CetEvenement(Me, "Un message", DateTime.Now) ' … … … End Sub End Class

Page 226: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 35

Pour déclarer le même événement CetEvenement sous la forme conventionnelle, il faut créer un objet de type EventArgs capable de transmettre plusieurs informations. De plus, il est utile de conserver la possibilité de transmettre EvengArgs.Empty. Il est évident que le respect de la forme conventionnelle engendre un surcroît de programmation.

Public Class EvenementArgs ' L'objet EvenementArgs hérite de EventArgs Inherits EventArgs ' et dispose ainsi de sa propriété Empty Private UnMessage As String Private UnMoment As DateTime Public Sub New() ' Il peut être instancié sans paramètre, ce MyBase.New() ' qui est suffisant quand il n'y a aucune End Sub ' information à transmettre Public Sub New(ByVal Maintenant As DateTime) UnMoment = Maintenant ' Mais il doit recevoir le moment de son End Sub ' instanciation s'il faut le transmettre à ' une procédure événementielle Public Property Message() Get ' Les propriétés sont les diverses Return UnMessage ' informations à transmettre End Get Set(ByVal Valeur) UnMessage = Valeur End Set End Property Public ReadOnly Property Moment() Get Return UnMoment End Get End Property End Class Public Class UneClasse Public Event CetEvenement(ByVal Emetteur As Object,

ByVal Arguments As EvenementArgs) Public Sub UneSub() ' UneSub transmet les informations Dim e As New EvenementArgs(DateTime.Now()) e.Message = "Un Message" RaiseEvent CetEvenement(Me, e) End Sub Public Sub UneAutreSub() ' UneAutreSub ne transmet aucune information Dim e As New EvenementArgs RaiseEvent CetEvenement(Me, e.Empty) End Sub End Class

Réception et traitement d'événements La mise à l'écoute d'un événement par une instance de l'objet susceptible de le déclencher peut se réaliser de deux manières. Soit la variable de référence à l'objet est déclarée avec l'attribut WithEvents, soit un ordre AddHandler implique l'événement concerné. Dans un cas comme dans l'autre, le traitement de la réception d'un événement est pris en charge par une procédure événementielle dont les arguments sont identiques à ceux définis lors de la déclaration de l'événement dans la classe émettrice. Quand une référence d'objet est déclarée avec l'attribut WithEvents, l'objet est visible dans la liste déroulante de gauche de la fenêtre d'écriture du code et les événements de cet objet sont visibles dans la liste déroulante de droite. Exactement comme cela se passe avec les objets visuels déposés sur un Form, la sélection d'un événement dans cette liste provoque l'écriture automatique de la ligne d'entête de la procédure événementielle adéquate. Il ne reste plus qu'à y ajouter le code de réponse à l'événement. L’ordre AddHandler, n’accorde pas cette facilité, mais présente d'autres avantages. L'ordre AddHandler permet la mise à l'écoute dynamique d'un objet, à un moment choisi et pour une durée déterminée, ainsi que la désignation d'une même procédure pour répondre à plusieurs événements. L'écoute d'un objet est stoppée par l'ordre RemoveHandler.

Page 227: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 36

Pour illustrer ces mécanismes, la classe UneClasse de l'exemple précédent est reprise et le code d'écoute et de traitement de ses événements est programmé ci après de différentes manières.

Module MonModule ' Instanciation de UneClasse avec prise en compte de ses événements Dim WithEvents UnObjet As New UneClasse

' Les sélections appropriées dans les listes déroulantes facilitent la déclaration de la ' procédure événementielle. Le code Console.WriteLine(…) y est ajouté. Private Sub UnObjet_CetEvenement(ByVal Emetteur As Object, ByVal Arguments As

EvenementArgs) Handles UnObjet.CetEvenement Console.WriteLine("1 : " & Arguments.Message & ", " & Arguments.Moment) End Sub

' Une autre procédure événementielle, ayant les mêmes arguments que la précédente, est ' écrite sans référence à un événement (pas de Handles … ). Private Sub EvenementUnAutreObjet(ByVal Emetteur As Object, ByVal Arguments As

EvenementArgs) Console.WriteLine("2 : " & Arguments.Message & ", " & Arguments.Moment) End Sub Sub Main() ' La méthode UneSub de UnObjet émet un évenement UnObjet.UneSub() ' La procédure UnObjet_CetEvenement est ' activée et l'affichage est : ' 1 : Un Message, 16/05/2006 14:37:34 ' Instanciation de UneClasse sans prise en compte de ses événements Dim UnAutreObjet As New UneClasse UnAutreObjet.UneSub() ' Aucune procédure événementielle n'est ' désignée pour répondre à l'événement ' émis par UneSub ' La procédure EvenementUnAutreObjet est désignée pour répondre à un CetEvenement émis ' par UnAutreObjet AddHandler UnAutreObjet.CetEvenement, AddressOf EvenementUnAutreObjet UnAutreObjet.UneSub() ' La procédure EvenementUnAutreObjet est ' activée et l'affichage est : ' 2 : Un Message, 16/05/2006 14:37:35 ' La procédure UnObjet_CetEvenement est aussi désignée pour répondre à un CetEvenement ' émis par UnAutreObjet AddHandler UnAutreObjet.CetEvenement, AddressOf UnObjet_CetEvenement UnAutreObjet.UneSub() ' Les deux procédures sont activées dans ' l'ordre de leur désignation : ' 2 : Un Message, 16/05/2006 14:37:36 ' 1 : Un Message, 16/05/2006 14:37:36 ' La procédure EvenementUnAutreObjet est désignée pour répondre aussi à un CetEvenement ' émis par UnObjet AddHandler UnObjet.CetEvenement, AddressOf EvenementUnAutreObjet UnObjet.UneSub() ' Les deux procédures sont activées dans ' l'ordre de leur désignation : ' 1 : Un Message, 16/05/2006 14:37:37 ' 2 : Un Message, 16/05/2006 14:37:37 ' Arrêt des écoutes des événements émis par les objets UnObjet et UnAutreObjet RemoveHandler UnObjet.CetEvenement, AddressOf UnObjet_CetEvenement RemoveHandler UnObjet.CetEvenement, AddressOf EvenementUnAutreObjet RemoveHandler UnAutreObjet.CetEvenement, AddressOf UnObjet_CetEvenement RemoveHandler UnAutreObjet.CetEvenement, AddressOf EvenementUnAutreObjet UnObjet.UneSub() ' Aucune procédure activée UnAutreObjet.UneSub() ' Aucune procédure activée End Sub End Module

Page 228: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 37

Aspects avancés

La délégation La délégation est le mécanisme permettant de passer l'adresse d'une procédure ou d'une fonction en paramètre à une méthode. Il permet aussi bien le passage d'une méthode d'instance que d'une méthode statique (Shared). La classe Delegate du Framework est la classe de base de tous les types délégués et elle n'est pas dérivable. Une procédure ou une fonction qui est déclarée avec l'attribut Delegate à l'intérieur ou à l'extérieur d'une classe, constitue le prototype de plusieurs autres méthodes autorisées à être passées en paramètre par leur adresse. Cette dernière est donnée par l'opérateur AddressOf. Cette déclaration de prototype implique que toutes les méthodes concernées aient exactement la même signature. Public Class MesOperations ' Déclaration d'un prototype de fonction déléguée à l'intérieur d'une classe Public Delegate Function Operation(ByVal V1 As Object, ByVal V2 As Object) As Object ' Définition de deux fonctions statiques Addition et Division Public Shared Function Addition(ByVal V1 As Object, ByVal V2 As Object) As Object Return V1 + V2 End Function Public Shared Function Division(ByVal V1 As Object, ByVal V2 As Object) As Object If V2 <> 0 Then Return V1 / V2 Else Return 0 End If End Function ' Définition de deux fonctions non statiques Soustraction et Multiplication Public Function Soustraction(ByVal V1 As Object, ByVal V2 As Object) As Object Return V1 - V2 End Function Public Function Multiplication(ByVal V1 As Object, ByVal V2 As Object) As Object Return V1 * V2 End Function End Class Module MonModule Sub Main() ' Instanciation d'un délégué Dim Operateur As MesOperations.Operation ' Désignation au délégué de l'adresse de la fonction sans instanciation (car Shared) Operateur = AddressOf MesOperations.Division ' Exécution de la fonction concernée par délégation Console.WriteLine(Operateur(5, 3)) ' Affiche : 1.6666667 ' Désignation au délégué de l'adresse d'une autre fonction sans instanciation (car Shared) Operateur = AddressOf MesOperations.Addition ' Exécution de la fonction concernée par délégation Console.WriteLine(Operateur(5, 3)) ' Affiche : 8 ' Instanciation d'un objet MesOperations Dim AutreOperateur As New MesOperations ' Désignation au délégué de l'adresse de la fonction de l'instance (car non Shared) Operateur = AddressOf AutreOperateur.Multiplication ' Exécution de la fonction concernée par délégation Console.WriteLine(Operateur(5, 3)) ' Affiche : 15 ' Désignation au délégué de l'adresse d'une autre fonction de l'instance (car non Shared) Operateur = AddressOf AutreOperateur.Soustraction ' Exécution de la fonction concernée par délégation Console.WriteLine(Operateur(5, 3)) ' Affiche : 2 End Sub End Module

Page 229: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 38

Sûr qu'un délégué n’est pas nécessaire pour choisir une des quatre opérations arithmétiques élémentaires. Mais ce n'est qu'un exemple de la programmation du mécanisme. En voici un autre, un peu plus sérieux. Le sujet et le mécanisme de tri utilisés pour illustrer l'usage de l'Interface, sont repris ici avec pour principale différence que l'Interface est abandonné au profit d'un délégué. ' Déclaration d'un prototype de fonction déléguée à l'extérieur de toute classe. Par souci de ' regroupement d'outils associés, cette déclaration aurait pu être faite dans la classe ClTri, ' sans aucune autre modification de l'application. Public Delegate Function Compare(ByVal Objet1 As Object, ByVal Objet2 As Object)

As Integer Public Class ClEtudiant ' La classe ClEtudiant est l'objet de Private Nom As String ' base de cet exemple Private Resultat As Single Private Section As String Public ReadOnly Property Information(ByVal Choix As Byte) As Object Get Select Case Choix Case 1 Return Nom Case 2 Return Resultat Case 3 Return Section End Select End Get End Property Public Sub New(ByVal ArgNom As String, ByVal ArgResultat As Single,

ByVal ArgSection As String) Nom = ArgNom Resultat = ArgResultat Section = ArgSection End Sub ' Pour que ses instances puissent être triées sur certains de ses membres par l'algorithme ' de tri utilisé, il faut que la classe ClEtudiant implémente les méthodes de comparaisons ' adéquates en respectant la signature du prototype adopté par la méthode Tri. ' Pour la permettre la comparaison sur Nom. Public Shared Function CompareNom(ByVal Obj1 As Object, ByVal Obj2 As Object)

As Integer Return Comparaison(1, Obj1, Obj2) End Function ' Pour la permettre la comparaison sur Resultat. Public Shared Function CompareResultat(ByVal Obj1 As Object, ByVal Obj2 As Object)

As Integer Return Comparaison(2, Obj1, Obj2) End Function ' Pour éviter la redondance de codes, une seule fonction réalise vraiment les comparaisons. Private Shared Function Comparaison(ByVal Choix As Byte,

ByVal Obj1 As Object, ByVal Obj2 As Object) As Integer Select Case Obj1.Information(Choix) Case Is < Obj2.Information(Choix) Return -1 Case Is = Obj2.Information(Choix) Return 0 Case Is > Obj2.Information(Choix) Return 1 End Select End Function End Class

Page 230: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 39

Public Class ClTri Public Sub Tri(ByVal Table() As Object, ByVal Comparateur As Compare,

Optional ByVal Sens As Integer = 1) Dim Tmp As Object ' Sens vaut 1 par défaut et provoque un tri Dim i As Integer ' croissant, sa valeur doit être -1 pour un Dim Permutation As Boolean = True ' tri décroissant While Permutation Permutation = False For i = 0 To Table.Length - 2 If Comparateur(Table(i), Table(i + 1)) = Sens Then Tmp = Table(i) Table(i) = Table(i + 1) Table(i + 1) = Tmp Permutation = True End If Next End While End Sub End Class Module MonModule ' Une application cliente de la classe Sub Main() ' ClEtudiant et du tri polyvalent ClTri.Tri Dim MesEtudiant(5) As ClEtudiant Dim Tri As ClTri = New ClTri Dim i As Integer MesEtudiant(0) = New ClEtudiant("Michel", 10, "Linux") MesEtudiant(1) = New ClEtudiant("Marc", 15, "Windows") MesEtudiant(2) = New ClEtudiant("Pierre", 12, "Analyse") MesEtudiant(3) = New ClEtudiant("Albert", 17, "Windows") MesEtudiant(4) = New ClEtudiant("Christian", 16, "Analyse") MesEtudiant(5) = New ClEtudiant("Bruno", 19, "Linux") For i = 0 To MesEtudiant.Length - 1 ' Affichage de la table non triée With MesEtudiant(i) Console.Write(.Information(1) & " - ") Console.Write(.Information(2) & " - ")) Console.WriteLine(.Information(3)) End With Next ' C'est l'adresse de la méthode qui est passée à Tri, selon la définition de ses arguments Tri.Tri(MesEtudiant, AddressOf ClEtudiant.CompareNom, 1) For i = 0 To MesEtudiant.Length - 1 ' Affichage de la table triée par Nom With MesEtudiant(i) Console.Write(.Information(1) & " - ")) Console.Write(.Information(2) & " - ")) Console.WriteLine(.Information(3)) End With Next Tri.Tri(MesEtudiant, AddressOf ClEtudiant.CompareResultat, -1) For i = 0 To MesEtudiant.Length - 1 ' Affichage de la table triée par Resultat With MesEtudiant(i) Console.Write(.Information(1) & " - ")) Console.Write(.Information(2) & " - ")) Console.WriteLine(.Information(3)) End With Next End Sub End Module

Page 231: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 40

Programmation asynchrone Dans la plupart des cas lorsqu'une procédure ou une fonction en appelle une autre, elle doit attendre la fin de l'exécution de la méthode appelée pour continuer ses propres tâches. Il s'agit là du mécanisme de programmation synchrone. Il peut constituer une perte de temps, parfois considérable, pouvant laisser croire à l'utilisateur que son système ne répond plus. La programmation asynchrone rassemble des mécanismes permettant le lancement d'une tâche en arrière plan, et ceux permettant de s'assurer de la fin d'une tâche avant d'en commencer une autre. La commande asynchrone Shell Le mécanisme de la programmation asynchrone permet donc à une méthode de continuer ses tâches après l'appel d'une autre, sans en attendre la fin de l'exécution. Ce cas a déjà été rencontré précédemment avec la commande Shell (de la librairie Microsoft.VisualBasic) qui sert au lancement d'applications externes.

Sub Main() Shell("Sol.exe", AppWinStyle.NormalFocus) For i As Byte = 1 To 250 Console.WriteLine(i) Next ' … … …

Dans cet exemple, une commande Shell est utilisée pour lancer le jeu Solitaire de Windows. Cette commande transmet l'ordre au système et rend aussitôt la main à Sub Main. L'affichage des valeurs de 1 à 250 demandé dans ce Main sera probablement terminé avant même qu'apparaisse la fenêtre du jeu. L'inconvénient potentiel est que si la suite du traitement doit utiliser les résultats du traitement demandé par Shell, ceux-ci ne seront pas encore disponibles. Par exemple, si Shell lance l'exécution d'un fichier script dont le rôle est de créer un fichier texte d'informations, il est impossible que le code à la suite de la ligne Shell réalise la lecture de ce fichier sauf à implémenter une temporisation suffisante, ce qui est fastidieux et peu fiable. Par exemple le script MesFichiers.Bat doit créer un fichier MesFichiers.Txt contenant les noms de tous les fichiers de tous les dossiers de l'unité X:. Ce script est : DIR X:\*.* /S > MesFichiers.Txt. Le code VB suivant lance l'exécution de MesFichier.Bat par la commande Shell et tente ensuite d'ouvrir en lecture le fichier ainsi créé (par exemple, pour en afficher le contenu à l'écran).

Sub Main() Shell("X:\MesFichiers.Bat") Dim FichierEntree As New StreamReader("X:\MesFichiers.TXT") ' … … … utilisation du fichier FichierEntree.Close()

La tentative d'instanciation d'un StreamReader sur le fichier lève une exception avec le message suivant : Le processus ne peut pas accéder au fichier "X:\MesFichiers.TXT", car il est en cours d'utilisation par un autre processus. Lorsque l'asynchronisme de la commande Shell handicape l'exécution correcte du reste de l'application, il faut utiliser les moyens qu'elle offre pour s'assurer de la fin du processus qu'elle a enclenché. Ses moyens sont un argument booléen indiquant s'il faut attendre ou non la fin du processus activé, et un autre argument précisant la durée de cette attente. La valeur par défaut de ce dernier est -1 et signifie durée illimitée. En outre, Shell est une fonction et elle retourne l'entier 0 si le processus se termine endéans l'intervalle de temps spécifié ou l'identifiant du processus dans le cas contraire. Voici le prototype de la fonction Shell.

Public Function Shell( ByVal Commande As String, _ Optional ByVal StyleFenetre As AppWinStyle = AppWinStyle.MinimizedFocus, _ Optional ByVal Attente As Boolean = False, _ Optional ByVal Duree As Integer = -1 _

) As Integer La correction du Main précédent par le passage de la valeur True pour le paramètre d'attente a pour effet que l'instanciation du StreamReader se fait après l'exécution complète du processus enclenché par Shell. Plus aucune exception n'est levée.

Page 232: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 41

Sub Main() Shell("X:\MesFichiers.Bat",,True) Dim FichierEntree As New StreamReader("X:\MesFichiers.TXT") ' … … … utilisation du fichier FichierEntree.Close() ' … … …

En matière d'exécution de programmes externes, la méthode Start de la classe Process du framework est une bonne alternative à Shell. Toutefois c'est aussi une commande asynchrone et une vitamine additionnelle est nécessaire pour qu'elle attende la fin de l'exécution de la tâche demandée. L'Imports System.Diagnostics est utile.

Process.Start("x:\MesFichiers.Bat") ' Aucune attente Process.Start("x:\MesFichiers.Bat").WaitForExit(t) ' Attente de t millisecondes

ou illimitée si t absent Programmation de méthodes asynchrones Lorsqu'une méthode déléguée est appelée par l'usage d'une méthode BeginInvoke, l'appel est placé en file d'attente et l'appelant peut immédiatement poursuivre ses tâches. Imports System.Threading ' Pour disposer d'une temporisation Delegate Function UneMethode(ByVal Entier As Integer) As String Public Class UneClasse ' Implémente la conversion d'un entier Public Shared Function Conversion(ByVal N As Integer) As String Console.WriteLine("Conversion commencée") Thread.Sleep(1000) ' Temporisation d'une seconde Console.WriteLine("Conversion terminée") Return N.ToString End Function End Class Module MonModule Dim Convertir As New UneMethode(AddressOf UneClasse.Conversion) Sub Main() Console.WriteLine("Appel Conversion") Convertir(5) Console.WriteLine("Suite application …") End Sub End Module L'affichage produit par ce programme montre que son fonctionnement est synchrone. Le dernier message du Main n'apparaît qu'après l'exécution complète de la fonction Conversion. Pour réaliser un appel asynchrone de cette fonction, il faut l'invoquer par la méthode BeginInvoke. Cette méthode impose que deux paramètres supplémentaires soient ajoutés à ceux de la fonction invoquée. Pour l'instant, ils sont remplacés par la valeur Nothing. Module MonModule Dim Convertir As New UneMethode(AddressOf UneClasse.Conversion) Sub Main() Console.WriteLine("Appel Conversion") Convertir.BeginInvoke(5, Nothing, Nothing) Console.WriteLine("Suite application …") End Sub End Module Cette fois, l'affichage produit par ce programme montre un fonctionnement asynchrone. En effet, le dernier message du Main apparaît déjà alors que ceux de la fonction Conversion ne sont pas encore affichés. Le premier paramètre imposé par la méthode BeginInvoke est l'adresse de la méthode à exécuter à la fin du traitement asynchrone. Par ailleurs, la méthode BeginInvoke retourne une valeur de type IAsyncResult qui peut être récupérée par la méthode EndInvoke. La récupération de la valeur de retour de la méthode appelée et son traitement doivent être programmés dans une autre méthode qui accepte une valeur IAsyncResult et invoque la méthode EndInvoke.

Affichages : Appel Conversion Conversion commencée Conversion terminée Suite application …

Affichages : Appel Conversion Suite application … Conversion commencée Conversion terminée

Page 233: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 42

Module MonModule Dim Convertir As New UneMethode(AddressOf UneClasse.Conversion) Private Sub Affichage(ByVal Resultat As IAsyncResult) Console.WriteLine(Convertir.EndInvoke(Resultat)) End Sub Sub Main() Console.WriteLine("Appel Conversion") Convertir.BeginInvoke(5, AddressOf Affichage, Nothing) Console.WriteLine("Suite application …") End Sub End Module Une autre manière de faire dispense de l'écriture de cette méthode spécifique mais elle bloque l'exécution de la suite du programme car EndInvoke est une méthode synchrone. Mais alors, à quoi bon s'embarrasser de l'asynchrone …? Module MonModule Dim Convertir As New UneMethode(AddressOf UneClasse.Conversion) Sub Main() Dim Resultat As IAsyncResult Console.WriteLine("Appel Conversion") Resultat = Convertir.BeginInvoke(5, Nothing, Nothing) Console.WriteLine(Convertir.EndInvoke(Resultat)) Console.WriteLine("Suite application …") End Sub End Module Toutefois, cette manière de procéder peut se justifier. Elle permet de continuer certains traitements pendant que s'exécute la méthode asynchrone et de ne bloquer l'application que lorsque c'est vraiment nécessaire. Dans l'exemple ci-dessous, les blocages par EndInvoke sont effectués quand c'est nécessaire et d'autres traitements, dont un autre appel de la même méthode asynchrone, ont été réalisés entre temps. Module MonModule Sub Main() Dim Resultat1, Resultat2 As IAsyncResult Console.WriteLine("Appel 1 Conversion") Resultat1 = Convertir.BeginInvoke(11, Nothing, Nothing) Console.WriteLine("Suite 1 application ...") Console.WriteLine("Appel 2 Conversion") Resultat2 = Convertir.BeginInvoke(22, Nothing, Nothing) Console.WriteLine("Suite 2 application ...") Console.WriteLine("Resultat 2 nécessaire") Console.WriteLine(Convertir.EndInvoke(Resultat2)) Console.WriteLine("Suite 3 application ...") Console.WriteLine("Resultat 1 nécessaire") Console.WriteLine(Convertir.EndInvoke(Resultat1)) Console.WriteLine("Suite application ...") End Sub End Module L'affichage produit par ce dernier exemple montre aussi que chaque appel asynchrone génère un processus d'exécution distinct. C'est ainsi que le deuxième appel asynchrone provoque le message d'entrée dans la méthode alors que le premier appel n'a pas encore produit le message de sortie. La méthode est donc bien exécutée une deuxième fois avant même avoir terminé les tâches de la première exécution. Cela ne peut se faire que dans des processus distincts. Il faut encore remarquer que l'arrêt d'une application par EndInvoke peut parfois durer plus longtemps que prévu. Il est prudent de se prémunir d'un arrêt infini par un test de la propriété IsCompleted de l'objet IAsyncResult.

If Resultat2.IsCompleted Then Console.WriteLine(Convertir.EndInvoke(Resultat2)) Else Console.WriteLine("Resultat 2 pas encore disponible") ' Agir en conséquence ... End If

Affichages : Appel Conversion Suite application … Conversion commencée Conversion terminée 5

Affichages : Appel Conversion Conversion commencée Conversion terminée 5 Suite application …

Affichages : Appel 1 Conversion Suite 1 application ... Appel 2 Conversion Suite 2 application ... Resultat 2 nécessaire Conversion commencée Conversion commencée Conversion terminée Conversion terminée 22 Suite 3 application ... Resultat 1 nécessaire 11 Suite application ...

Page 234: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 43

Une autre technique d'attente de la fin d'un processus asynchrone consiste à obtenir le signal de fin de processus par l'appel de la méthode AsyncWaitHandle.WaitOne de l'objet IAsyncResult. Cette technique est équivalente à l'usage de EndInvoke seul.

Console.WriteLine("Resultat 2 nécessaire") Resultat2.AsyncWaitHandle.WaitOne() Console.WriteLine(Convertir.EndInvoke(Resultat2))

Enfin, il faut encore noter que le deuxième paramètre supplémentaire imposé par la méthode BeginInvoke est de type Object et qu'il peut donc contenir n'importe quoi. Cet objet est destiné à être récupéré dans la méthode dont l'adresse est passée à BeginInvoke, par la propriété AsyncState de l'objet IAsyncResult. Module MonModule Dim Convertir As New UneMethode(AddressOf UneClasse.Conversion) Private Sub Affichage(ByVal Resultat As IAsyncResult) Console.Write(Resultat.AsyncState) Console.WriteLine(Convertir.EndInvoke(Resultat)) End Sub Sub Main() Dim UneChaine As String UneChaine = "Résultat = " Console.WriteLine("Appel Conversion") Convertir.BeginInvoke(5, AddressOf Affichage, UneChaine) Console.WriteLine("Suite application ...") Console.ReadLine() End Sub End Module

Le multithreading Une application est exécutée dans le contexte d'un processus global dont le rôle principal est la gestion de la zone mémoire nécessaire au stockage des données de cette application. Ce processus global est constitué d'un ensemble de petits processus appelés threads (processus légers ou fils d'exécution). Ces threads, pour l'exécution de leurs traitements, utilisent le processeur à tour de rôle pendant un court laps de temps alloué par le système. Si les traitements d'un thread ne sont pas terminés dans le bref délai imparti, ils sont continués quand ce thread obtient de nouveau la main. Il en découle que la simultanéité d'exécution d'applications n'est qu'une apparence, tout au moins sur un système mono processeur. Très nombreuses sont les applications qui ne nécessitent pas la gestion explicite des threads par le programmeur. Ce dernier peut user des méthodes asynchrones pour faire exécuter plusieurs tâches simultanément par le système sans se préoccuper des threads sous jacents. Toutefois, la gestion de threads par le programmeur est intéressante parce qu'elle offre des possibilités de contrôles supérieures à celles de la programmation asynchrone. De plus, elle permet l'exécution et la gestion de tâches en parallèle sur les systèmes multi processeurs où la simultanéité n'est plus une illusion. Les processus actifs La classe Process de l'espace de noms System.Diagnostics propose une vaste panoplie de moyens de gestion des processus. Il n'est pas utile ici de les étudier en détail, mais voici un exemple d'identification et d'arrêt d'un processus, qui correspond aux besoins les plus fréquents en la matière. Le code suivant affiche l'identifiant et le nom de chaque processus actif et, pour autant que Excel ait été lancé au préalable, l'expérimentation montre l'arrêt immédiat de ce logiciel par l'exécution de la méthode Kill. Module MonModule Sub Main() Dim P As System.Diagnostics.Process For Each P In System.Diagnostics.Process.GetProcesses Console.WriteLine(P.Id & " " & P.ProcessName) If P.ProcessName = "EXCEL" Then P.Kill() Next End Sub End Module

Affichages : Appel Conversion Suite application ... Conversion commencée Conversion terminée Résultat = 5

Page 235: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 44

Synchrone, asynchrone et multithreading La comparaison des modes de programmation synchrone, asynchrone et multithreads illustre des différences de comportement et apporte une justification à l'emploi du multithreading sur un système mono processeur. Ci-dessous, une classe Traitements offre deux méthodes Traitement1 et Traitement2 réalisant presque les mêmes tâches. Pour différencier ces méthodes lors de l'exécution, la première affiche les entiers de 10 à 15 et la deuxième, les entiers de 20 à 25. Une pause entre chaque affichage allonge la durée de chaque traitement pour permettre le dépassement du temps processeur alloué. Des affichages du temps avec Now.TimeOfDay ont été réalisés afin de produire une comparaison des performances. Ils ne sont pas programmés ci dessous, mais les positions qu'ils occupaient sont signalées en commentaires. Bien entendu, outre les temporisations, les durées calculées sont dépendantes des performances du processeur et non guère de significations en elles mêmes. C'est leur comparaison qui est porteuse d'enseignement. Dans les exemples suivants et dans tous ceux qui illustrent des traitements asynchrones ou multithreads, il convient de placer un ReadKey ou un ReadLine avant la fin de la procédure principale pour que les traitements parallèles puissent afficher leurs résultats avant l’arrêt du programme et le retour au système. Imports System.Threading Public Class Traitements ' Implémente deux traitements semblables ' mais de rapidités différentes Public Sub Traitement1() For i As Byte = 10 To 15 ' Affichage du temps avant For Console.Write(i & " ") ' Doit afficher 10 11 12 13 14 15 Thread.Sleep(400) ' Temporisation 400 millisecondes Next ' Affichage du temps après Next End Sub Public Sub Traitement2() For i As Byte = 20 To 25 ' Affichage du temps avant For Console.Write(i & " ") ' Doit afficher 20 21 22 23 24 25 Thread.Sleep(800) ' Temporisation 800 millisecondes Next ' Affichage du temps après Next End Sub End Class Module MonModule ' Programmation synchrone Dim T As New Traitements Sub Main() ' Affichage du temps avant appel Traitement1 T.Traitement1() ' Affichage du temps avant appel Traitement2 T.Traitement2() ' Affichage du temps après appel Traitement2 End Sub ' Affichage : End Module ' 10 11 12 13 14 15 20 21 22 23 24 25 Module MonModule ' Programmation Asynchrone Delegate Sub Traitement() Dim T As New Traitements Dim T1 As New Traitement(AddressOf T.Traitement1) Dim T2 As New Traitement(AddressOf T.Traitement2) Sub Main() ' Affichage du temps avant appel Traitement1 T1.BeginInvoke(Nothing, Nothing) ' Affichage du temps avant appel Traitement2 T2.BeginInvoke(Nothing, Nothing) ' Affichage du temps après appel Traitement2 End Sub ' Affichage : End Module ' 10 11 20 12 13 21 14 15 22 23 24 25 Module MonModule ' Programmation MultiThreads Dim T As New Traitements Dim T1 As New Thread(AddressOf T.Traitement1) Dim T2 As New Thread(AddressOf T.Traitement2) Sub Main() ' Affichage du temps avant appel Traitement1 T1.Start() ' Affichage du temps avant appel Traitement2 T2.Start() ' Affichage du temps après appel Traitement2 End Sub ' Affichage : End Module ' 10 20 11 21 12 13 22 14 15 23 24 25

Page 236: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 45

Synchrone Mise en exécution de T1 0 millisecondes

Mise en exécution de T2 0 millisecondes

Délai de reprise par l'appelant à l'issue des 2 appels 7.220,3824 millisecondes

Durée totale des traitements (T1 et T2 achevés) 7.220,3824 millisecondes

Affichages 10 11 12 13 14 15 20 21 22 23 24 25

Asynchrone

Mise en exécution de T1 50,0720 millisecondes

Mise en exécution de T2 0 millisecondes

Délai de reprise par l'appelant à l'issue des 2 appels 40,0576 millisecondes

Durée totale des traitements (T1 et T2 achevés) 5.497,9056 millisecondes

Affichages 10 11 20 12 13 21 14 15 22 23 24 25

Multithreads

Mise en exécution de T1 30,0432 millisecondes

Mise en exécution de T2 30,0432 millisecondes

Délai de reprise par l'appelant à l'issue des 2 appels 10,0144 millisecondes

Durée totale des traitements (T1 et T2 achevés) 4.987,1712 millisecondes

Affichages 10 20 11 21 12 13 22 14 15 23 24 25

L'examen des relevés ci-dessus montre clairement qu'en programmation synchrone, bien que la mise en exécution d'un traitement soit extrêmement plus rapide que dans les autres modes, la durée totale des traitements est un peu supérieure à la somme des durées de chacun des traitements (6 x 400 + 6 x 800 = 7200). Par ailleurs, la méthode appelante doit attendre la fin de tous les traitements pour continuer ses tâches. En programmation asynchrone, bien que le temps de mise à l'exécution du premier traitement soit conséquent, la durée totale des traitements est inférieure à la somme des durées de chacun des traitements. Ceci est bien normal car chaque traitement profite de la pause de l'autre pour effectuer une partie de ses tâches. Cela se voit à l'examen de l'affichage : Traitement2 réalise une itération pendant la pause Traitement1, lequel réalise ensuite deux itérations pendant la pause de l'autre, et ainsi de suite. Par ailleurs, la méthode appelante peut continuer la suite de ses tâches après un délai inférieur même à l'activation du premier traitement. En programmation par gestion des threads, chaque mise en exécution a une même durée qui est moins importante qu'en programmation asynchrone. Mais ce qui extrêmement plus rapide, ce sont la durée totale des traitements et le délai de continuation de ses propres tâches par la méthode appelante. Ceci s'explique par le fait que les traitements ainsi lancés sont exécutés comme des processus strictement distincts. Ils ne sont donc plus dépendants des temporisations de leurs concurrents, mais seulement des laps de temps accordés par le système. L'affichage montre d'ailleurs que Traitement2 réalise ses deux premières itérations (affichage de 20 et 21) avant même que Traitement1 termine sa troisième (affichage de 12). Les deux traitements sont quasi simultanés. Programmation et gestion du multithreading L'espace de nom System.Threading propose plusieurs classes utiles pour la programmation et la gestion des threads, dont notamment Thread et Mutex. Les outils de gestions des threads, dont seuls les plus fréquemment utilisés sont étudiés ici, sont nécessaires pour éviter les dangers de ce mode de programmation. Ces dangers et leur gestion, qui ont diverses incidences sur le bon fonctionnement des applications, sont illustrés dans les exemples suivants. Accès à une ressource partagée Module MonModule ' Programmation MultiThreads Dim T As New Traitements Dim T1 As New Thread(AddressOf T.Traitement1) Sub Main() T1.Start() ' Démarrage de Traitement1 sur un thread T.Traitement2() ' Appel de Traitement2 en mode synchrone End Sub ' Affichage : End Module ' 10 20 11 21 12 13 22 14 15 23 24 25

Page 237: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 46

L'affichage produit par Traitement2 est parasité par l'affichage de Traitement1. L'usage des méthodes Suspend et Resume de la classe Thread permet qu'une tâche comme Traitement2 dans cet exemple, soit exécutée sans perturbation, prioritairement au thread T1. Mais la suspension de Traitement1 risque fort de bloquer l’accès à Console, la ressource commune de l’exemple, et d’empêcher du même coup l’exécution de Traitement2. Le risque est évité ci-dessous par la reprise de la ressource par l’application principale avant la suspension de Traitement1. Module MonModule ' Programmation MultiThreads Dim T As New Traitements Dim T1 As New Thread(AddressOf T.Traitement1) Sub Main() T1.Start() ' Démarrage de Traitement1 sur un thread Console.Write(String.Empty) ' Reprendre la main sur la Console T1.Suspend() ' Interrompt l'exécution du thread T1 T.Traitement2() ' Appel de Traitement2 en mode synchrone T1.Resume() ' Continue l'exécution du thread interrompu End Sub ' Affichage : End Module ' 10 20 21 22 23 24 25 11 12 13 14 15

Par ailleurs, l'usage de Suspend ou de Resume sur un thread qui n'est pas en service provoque la levée d'une exception. Les propriétés IsAlive et ThreadSate fournissent l'information sur l'état d'un thread donné. Module MonModule ' Programmation MultiThreads Dim T As New Traitements Dim T1 As New Thread(AddressOf T.Traitement1) Sub Main() T1.Start() ' Démarrage de Traitement1 sur un thread If T1.IsAlive Then ' Interruption si IsAlive Console.Write(String.Empty) ' Reprendre la main sur la Console T1.Suspend() End If T.Traitement2() ' Appel de Traitement2 en mode synchrone If T1.ThreadState Then ' Reprise si ThreadState <> 0 T1.Resume() ' ThreadState a plusieurs valeurs possibles End If ' et la décision de reprise peut être évaluée End Sub ' de manière plus pertinente End Module Les exemples précédents illustrent comment exécuter une méthode prioritairement sur le thread actif, mais montre aussi que la suspension temporaire d’un thread peut provoquer des problèmes d’exécution par le blocage de ressources partagées. Il faut noter encore qu’il n’est sans doute pas judicieux d’invoquer différemment des méthodes d’une même instance comme dans ces exemples en multithreading et en mode synchrone, mais procéder autrement n’illimine pas les risques de blocages. Il faut éviter de suspendre un thread au profit d’un autre qui utilise les mêmes ressources. Plutôt qu’exécuter une méthode prioritairement sur un thread actif, il peut être souhaitable d'attendre la fin de l'exécution du thread avant d’en lancer un autre. La classe Thread offre la méthode bloquante Join pour réaliser cette manoeuvre. Module MonModule ' Programmation MultiThreads Dim T As New Traitements Dim T1 As New Thread(AddressOf T.Traitement1) Dim T2 As New Thread(AddressOf T.Traitement2) Sub Main() T1.Start() ' Démarrage de Traitement1 sur un thread If T1.IsAlive Then T1.Join() ' Attendre la fin de T1 si IsAlive End If T2.Start() ' Démarrage de Traitement2 sur un thread End Sub ' Affichage : End Module ' 10 11 12 13 14 15 20 21 22 23 24 25 La méthode Join permet également de s'assurer de l'exécution complète de tous les threads lancés par l'application avant l'arrêt de celle-ci. Cela peut se produire lorsque l'application utilise l'ordre explicite de retour au système System.Environment.Exit. Dans l'exemple suivant, des lignes T1.Join() et T2.Join() devraient précéder l'ordre de sortie pour que les affichages réalisés par les threads soient complets.

Page 238: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 47

Module MonModule ' Programmation MultiThreads Dim T As New Traitements Dim T1 As New Thread(AddressOf T.Traitement1) Dim T2 As New Thread(AddressOf T.Traitement2) Sub Main() T1.Start() T2.Start() System.Environment.Exit(0) End Sub ' Affichage : End Module ' 10 20

Accès exclusif à une ressource C'est la classe Mutex qui permet de réserver les ressources d'un thread. La méthode WaitOne réserve toutes les ressources utilisées dans les lignes de codes qui la suivent jusqu'à la rencontre de la méthode ReleaseMutex qui les libère pour d'autres threads. L'instance de Mutex doit être unique dans un contexte donné. Avec ce procédé, le premier thread à exécuter WaitOne garde la main sur ses ressources jusqu'à la fin de ses traitements. La classe Traitements utilisée précédemment est modifiée de sorte que la ressource ne soit plus la Console, mais une méthode Traitement3. C'est dans la méthode appelante qu'il faut régler la priorité souhaitée en choisissant le thread à démarrer d'abord. Imports System.Threading Public Class Traitements Dim M As New Mutex Public Sub Traitement1() M.WaitOne() ' Réserve les ressources pour le thread For i As Byte = 10 To 15 Traitement3(i) Thread.Sleep(400) Next M.ReleaseMutex() ' Libère les ressources End Sub Public Sub Traitement2() M.WaitOne() ' Réserve les ressources pour le thread For i As Byte = 20 To 25 Traitement3(i) Thread.Sleep(800) Next M.ReleaseMutex() ' Libère les ressources End Sub Public Sub Traitement3(ByVal N As Integer) Console.Write(N & " ") End Sub End Class La classe Mutex offre donc un outil de synchronisation des tâches démarrées sur des threads différents. Mais, attention au contenu des zones de codes protégées par Mutex. Si une méthode contient une zone protégée par laquelle elle réalise l'appel d'une autre méthode qui contient aussi une zone protégée dans laquelle est effectué l'appel de la méthode appelante, il y a un inter blocage qui, sous DotNet, génère la levée d'une exception. Il faut donc éviter que des méthodes ainsi exécutées s'appellent entre elles. Module MonModule ' Programmation MultiThreads Dim T As New Traitements Dim T1 As New Thread(AddressOf T.Traitement1) Dim T2 As New Thread(AddressOf T.Traitement2) Sub Main() T1.Start() T2.Start() End Sub ' Affichage : End Module ' 10 11 12 13 14 15 20 21 22 23 24 25

Page 239: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 48

Instanciation d'un thread par délégation Deux écritures différentes du code d'instanciation d'un thread se rencontrent dans ce cours, mais aussi dans la littérature et dans la documentation en ligne de Visual Studio.

Dim T1 As New Thread(AddressOf T.Traitement1)

Dim T1 As New Thread(New ThreadStart(AddressOf T.Traitement1)) La seconde utilise explicitement une méthode déléguée ThreadStart. En réalité, le constructeur de la classe Thread reçoit comme seul paramètre le délégué ThreadStart et le compilateur remplace automatiquement la première écriture d'instanciation par la deuxième. Ces deux écritures sont donc strictement équivalentes. Echange d'informations avec un thread Le thread est instancié avec un délégué qui ne tolère aucun argument et ne retourne aucune valeur. Il est donc impossible d'utiliser les modes ordinaires de communication de données que sont les arguments et les valeurs de retour des fonctions. Il existe toutefois plusieurs moyens de fournir des valeurs aux threads et d'en récupérer des informations. Il convient que toutes les méthodes exécutables sur des threads distincts soient regroupées au sein d'une ou plusieurs classes, même si cela n'est pas toujours nécessaire. Ainsi dans l'exemple suivant, une chaîne de caractères est traitée par un thread du seul fait que procédure et variable sont situées dans la même portée. Imports System.Threading Public Module MonModule Private Chaine As String Public Sub UneProc() Console.WriteLine(Chaine) ' Affiche la variable de la portée Chaîne = "Chaîne modifiée" ' et change son contenu End Sub Public Sub Main() Dim Traitement As New Thread(AddressOf UneProc) Chaine = "Une chaîne" Traitement.Start() Console.WriteLine("Appel terminé") Traitement.Join() Console.WriteLine("Traitement terminé.") Console.WriteLine(Chaine) End Sub End Module En enfermant la procédure dans une classe distincte, le code appelant, qui peut utiliser les méthodes Property pour passer et récupérer des valeurs, dispose aussi d'un constructeur permettant le passage de valeurs initiales. Public Class MesProcedures Private ChaineLocale As String Public Sub New(ByVal Chaine As String) ChaineLocale = Chaine End Sub Public ReadOnly Property Chaine() Get Return ChaineLocale End Get End Property Public Sub UneProc() Console.WriteLine(ChaineLocale) ' Affiche la variable locale et ChaineLocale = "Chaîne modifiée" ' et change son contenu End Sub End Class

Affichages : Appel terminé Une chaîne Traitement terminé Chaîne modifiée

Page 240: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 49

Public Module MonModule Public Sub Main() Dim Procedure As New MesProcedures("Une Chaîne") Dim Traitement As New Thread(AddressOf Procedure.UneProc) Traitement.Start() Console.WriteLine("Appel terminé") Traitement.Join() Console.WriteLine("Traitement terminé.") Console.WriteLine(Procedure.Chaine) ' Lecture de la propriété modifiée End Sub End Module Si l'usage du constructeur et des méthodes Property permet l'échange d'informations avec un thread, ce procédé n'est guère satisfaisant dans la mesure où il faut absolument attendre (Join) la fin de l'exécution du thread, c'est-à-dire renoncer à l'avantage du multithreading, pour récupérer la valeur souhaitée. En effet, dans le code suivant, la valeur récupérée est la valeur initiale et non celle modifiée par le thread parce que la reprise de la main par le programme appelant est plus rapide que l'exécution du thread.

Public Sub Main() Dim Procedure As New MesProcedures("Une Chaîne") Dim Traitement As New Thread(AddressOf Procedure.UneProc) Traitement.Start() ' Traitement.Join() Console.WriteLine(Procedure.Chaine) ' Affiche : Une Chaîne End Sub

C'est la programmation judicieuse d'événements qui apporte la meilleure solution en matière de communication entre une application exécutée sur un thread principal et les threads qu'elle active. Voici, reprogrammé en conséquence, les codes de l'exemple précédent. Imports System.Threading Public Class MesProcedures Public Event ResultatComplet(ByVal Valeur As String) Private ChaineLocale As String Public Sub New(ByVal Chaine As String) ChaineLocale = Chaine End Sub Public Sub UneProc() Console.WriteLine(ChaineLocale) Thread.Sleep(400) ChaineLocale = "Chaîne modifiée" RaiseEvent ResultatComplet(ChaineLocale) End Sub End Class Public Module MonModule Private Sub ResultatDisponible(ByVal Chaine As String) Console.WriteLine(Chaine) End Sub Public Sub Main() Dim Procedure As New MesProcedures("Une Chaîne") Dim Traitement As New Thread(AddressOf Procedure.UneProc) AddHandler Procedure.ResultatComplet, AddressOf ResultatDisponible Traitement.Start() Console.WriteLine("Appel terminé") ' Traitement.Join() Console.WriteLine("Traitement terminé.") Console.ReadLine() End Sub End Module

Page 241: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 50

Dans cet exemple, l'arrêt du programme a été suspendu par une ligne Console.ReadLine pour laisser à l'événement le temps de survenir. Dans une application, cette ligne est remplacée par le code qui peut être exécuté sans attendre le résultat de l'exécution du thread. Lorsque plusieurs instances d'un même thread ou plusieurs threads accèdent à une même ressource, cela pose des problèmes d'interférences, voire des blocages. C'est pourquoi les mécanismes de synchronisation tels que l'emploi de la méthode Join ou l'usage de la classe Mutex ont été étudiés. Quand la ressource critique est une expression manipulant un objet de type référence, comme une classe, un module, un tableau ou un délégué, VB.Net offre un outil permettant de geler l'accès à cet objet tout le temps nécessaire à son traitement. L'instruction SyncLock veille à ce que plusieurs threads n'exécutent pas les mêmes instructions en même temps. Quand l'exécution d'un thread atteint le bloc SyncLock, il évalue l'objet ou l'expression et s'en réserve l'exclusivité. Ceci empêche une expression de modifier des valeurs pendant l'exécution de plusieurs threads, ce qui peut donner des résultats inattendus. La levée de cette exclusivité s'effectue à la rencontre de la fin de bloc End SyncLock. La même protection peut être obtenue avec un Mutex, mais l'action de SyncLock est plus rapide. L'exemple suivant illustre le problème et son remède. Imports System.Threading Public Class Traitements Public Event ResultatComplet(ByRef T() As Integer, ByVal Taille As Integer) Dim TLocal() As Integer Dim TailleLocale As Integer Public Sub New(ByVal Taille As Integer) ReDim TLocal(Taille) TailleLocale = Taille End Sub Public Sub Traitement1() SyncLock TLocal ' Verrouillage de TLocal For i As Integer = 0 To TailleLocale Thread.Sleep(100) TLocal(i) = 10 + i Next i RaiseEvent ResultatComplet(TLocal, TailleLocale) End SyncLock ' Libération (après expédition du résultat) End Sub Public Sub Traitement2() SyncLock TLocal ' Verrouillage de TLocal For i As Integer = 0 To TailleLocale Thread.Sleep(200) TLocal(i) = 20 + i Next i RaiseEvent ResultatComplet(TLocal, TailleLocale) End SyncLock ' Libération (après expédition du résultat) End Sub End Class Public Module MonModule Private Sub ResultatDisponible(ByRef T() As Integer, ByVal Longueur As Integer) For i As Integer = 0 To Longueur Console.Write(T(i).ToString & " ") Next End Sub Public Sub Main() Dim T As New Traitements(5) Dim T1 As New Thread(AddressOf T.Traitement1) Dim T2 As New Thread(AddressOf T.Traitement2) AddHandler T.ResultatComplet, AddressOf ResultatDisponible T1.Start() ' Doit fournir les valeurs de 10 à 15 T2.Start() ' Doit fournir les valeurs de 20 à 25 Console.ReadLine() ' Attendre la fin des affichages End Sub End Module

Affichage sans les protections SyncLock : 20 21 22 13 14 15 20 21 22 23 24 25 Affichage avec les protections SyncLock : 10 11 12 13 14 15 20 21 22 23 24 25

Page 242: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 51

Le marshaling Plusieurs significations du mot marshaling (ou marshalling), telles conduite cérémonieuse de personnes, organisation et rangement de groupes de personnes, triage de wagons, suggèrent sa signification en informatique. C'est un mécanisme qui, lors de l'appel d'une procédure ou d'une fonction à distance (autre réseau, autre machine, autre processus), consiste à trier et à convertir des données d'une structure particulière vers un flux d'octets, en vue de leur transfert vers un site de réception où sont rétablies les valeurs dans une forme équivalente. Cette opération inverse porte le nom de unmarshaling. En DotNet, le concept s'applique aussi aux communications entre processus managés et non managés, et aux communications entre threads. Le marshaling est recommandé lorsque des méthodes doivent être activées sur le thread principal en réponse à des sollicitations de threads secondaires. La mise en place du marshaling consiste à programmer l'appel de ces méthodes en mode asynchrone. Cette situation arrive notamment quand des threads secondaires manipulent des contrôles d'une application Windows, ces contrôles appartenant toujours au thread d'exécution principal. Il s’agit en fin de compte, d’empêcher un thread secondaire de déclencher l’exécution d’une méthode synchrone qui perturberait les traitements en cours d’un thread principal. A titre d'illustration, la mise en place du marshaling dans l'exemple précédent consiste à modifier le module MonModule de sorte à sous-traiter le travail de la procédure événementielle ResultatDisponible , qui est activée à partir des threads, à la procédure TraiteResultatDisponible exécutée en mode asynchrone par une méthode BeginInvoke. Public Module MonModule Public Delegate Sub ResultatPresent(ByRef T() As Integer, ByVal L As Integer) Dim TraitementResultat As New ResultatPresent(AddressOf TraiteResultatDisponible) Private Sub TraiteResultatDisponible(ByRef T() As Integer, ByVal Longueur As Integer) For i As Integer = 0 To Longueur Console.Write(T(i).ToString & " ") Next End Sub Private Sub ResultatDisponible(ByRef T() As Integer, ByVal Longueur As Integer) ' TraiteResultatDisponible(T, Longueur) ' Pour test avec appel synchrone TraitementResultat.BeginInvoke(T, Longueur, Nothing, Nothing) End Sub Public Sub Main() Dim T As New Traitements(5) Dim T1 As New Thread(AddressOf T.Traitement1) Dim T2 As New Thread(AddressOf T.Traitement2) AddHandler T.ResultatComplet, AddressOf ResultatDisponible T1.Start() ' Doit fournir les valeurs de 10 à 15 T2.Start() ' Doit fournir les valeurs de 20 à 25 Console.ReadLine() ' Attendre la fin des affichages End Sub End Module Le marshaling mobilise beaucoup de ressources et dans l'exemple ci-dessus, la durée séparant le début du Main de la fin du dernier affichage est plus importante pour l'appel de TraiteResultatDisponible en mode asynchrone que pour l'appel en mode synchrone. Dans l'exemple suivant, la mise en œuvre du marshaling ne modifie guère les performances. Cet exemple est une application Windows qui exécute trois traitements, un lent, un moyen et un rapide. L'application est d'abord réalisée en programmation événementielle ordinaire, elle est modifiée ensuite en mode multithreads et finalement, elle implémente le marshaling. L'application est constituée d'un formulaire unique et d'une classe qui contient les différents traitements. Ceux-ci exécutent les calculs sollicitant le processeur et indiquent au formulaire la fin de ces calculs. En programmation événementielle ordinaire, l'utilisateur encode une valeur et lance le traitement souhaité. Il doit alors attendre que le formulaire reçoive le résultat et l'affiche dans l'étiquette appropriée pour procéder à l'encodage de la valeur suivante. En programmation multithreads, l'utilisateur peut encoder la valeur suivante aussitôt un traitement lancé. Cette disponibilité de son application est plus rapide encore lorsque le marshaling est implémenté. C'est un des bénéfices déjà constatés de la programmation asynchrone.

Page 243: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 52

Le formulaire, qui est nommé FBase, est muni de quatre étiquettes (EtiDouble, EtiFactorielle, EtiIteration, EtiNbCalcul), de trois boutons (BDouble, BFactorielle, BIteration) et d'une boîte de texte (TValeur). Ces différents objets sont flanqués d'étiquettes de libellés lorsque c'est nécessaire. La propriété BackColor des étiquettes destinées aux résultats a été réglée de sorte à les mettre en évidence. L'encodage s'effectue dans la boîte de texte et le clic d'un bouton lance le traitement correspondant. Le traitement Double consiste à doubler la valeur encodée. C'est le plus rapide. Le traitement Factorielle, plus lent, calcule la factorielle de la valeur encodée. Enfin, le traitement Iteration, qui est d'autant plus lent qu'il contient une pause de 500 millisecondes, effectue le nombre d'itérations indiqué par la valeur encodée. Le premier traitement compte pour un calcul et les deux autres en comptabilisent un par itération. La classe, qui est nommée MesProcedures, contient les variables locales nécessaires à l'exécution de ses calculs (vDouble, vFactorielle, vIteration, vNbCalcul), les événements (DoubleComplete, FactorielleComplete, IterationComplete) qui retournent les résultats, les méthodes Property (ValeurDouble, ValeurFactorielle, ValeurIteration) permettant le passage de la valeur encodée dans FBase vers la variable locale appropriée, et les trois procédures de traitements (CalculeDouble, CalculeFactorielle, ExecuteIteration) qui effectuent les calculs demandés et émettent les événements. Chaque traitement dispose de ses propres variables locales pour stocker les valeurs à retourner par l'événement. Voici le code de cette classe. Imports System.Threading Public Class MesProcedures Private vDouble As Integer Private vFactorielle As Integer Private vIteration As Integer Private vNbCalcul As Double = 0 ' En version multithreads, c'est ici que sont insérées les lignes de déclaration des threads Public Event IterationComplete(ByVal Nombre As Integer, ByVal TotalCalculs As Double) Public Event DoubleComplete(ByVal Result As Integer, ByVal TotalCalculs As Double) Public Event FactorielleComplete(ByVal Factorial As Double,

ByVal TotalCalculs As Double) Public WriteOnly Property ValeurDouble() Set(ByVal Valeur) vDouble = Valeur End Set End Property Public WriteOnly Property ValeurFactorielle() Set(ByVal Valeur) vFactorielle = Valeur End Set End Property Public WriteOnly Property ValeurIteration() Set(ByVal Valeur) vIteration = Valeur End Set End Property Public Sub CalculeDouble() Dim vResultat As Integer Dim vTotalActuel As Double vResultat = vDouble * 2 vNbCalcul += 1 vTotalActuel = vNbCalcul RaiseEvent DoubleComplete(vResultat, vTotalActuel) End Sub

Page 244: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 53

Public Sub CalculeFactorielle() Dim i As Integer Dim vResultat As Double = 1 Dim vTotalActuel As Double For i = 1 To vFactorielle vResultat *= i vNbCalcul += 1 Next i vTotalActuel = vNbCalcul RaiseEvent FactorielleComplete(vResultat, vTotalActuel) End Sub Public Sub ExecuteIteration() Dim i As Integer Dim vTotalActuel As Double For i = 1 To vIteration Thread.Sleep(500) ' Pause de 500 millisecondes vNbCalcul += 1 Next i vTotalActuel = vNbCalcul RaiseEvent IterationComplete(i - 1, vTotalActuel) End Sub End Class Cette fois, le sacro saint Code généré par le Concepteur Windows Form et stocké dans le fichier FBase.Designer.vb ne suffit plus à l’instanciation correcte du formulaire. Puisqu’il ne convient pas de modifier ce code dans ce fichier, le constructeur de FBase est récrit pour contenir les compléments nécessaires, à savoir l'instanciation d'un objet Procedures de type MesProcedures et les désignations des procédures événementielles.

Public Sub New() InitializeComponent() ' Constructeur contenu dans le fichier ' … compléments ajoutés … Procedures = New MesProcedures AddHandler Procedures.FactorielleComplete, AddressOf EvFinFactorielle AddHandler Procedures.DoubleComplete, AddressOf EvFinDouble AddHandler Procedures.IterationComplete, AddressOf EvFinIteration End Sub

Voici le code complet de FBase dans sa version événementielle simple. Public Class FBase Inherits System.Windows.Forms.Form Dim Procedures As MesProcedures Public Sub New() ' Reproduire ici le code de la procédure New écrite ci dessus End Sub Private Sub BFactorielle_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BFactorielle.Click Procedures.ValeurFactorielle = CInt(TValeur.Text) ' Transmettre la valeur à l'instance BFactorielle.Enabled = False ' Geler le bouton Procedures.CalculeFactorielle() ' Lancer le calcul sur le thread ' Procedures.ChoixThreads(1) ' Lancer le calcul sur un autre thread End Sub Private Sub BDouble_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles BDouble.Click Procedures.ValeurDouble = CInt(TValeur.Text) ' Transmettre la valeur à l'instance BDouble.Enabled = False ' Geler le bouton Procedures.CalculeDouble() ' Lancer le calcul sur le thread ' Procedures.ChoixThreads(2) ' Lancer le calcul sur un autre thread End Sub

Page 245: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 54

Private Sub BIteration_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BIteration.Click

EtiIteration.Text = "... ... ..." ' Faire patienter l'utilisateur EtiIteration.Refresh() Procedures.ValeurIteration = CInt(TValeur.Text) ' Transmettre la valeur à l'instance BIteration.Enabled = False ' Geler le bouton Procedures.ExecuteIteration() ' Lancer le calcul sur le thread ' Procedures.ChoixThreads(3) ' Lancer le calcul sur un autre thread End Sub Private Sub EvFinFactorielle(ByVal Valeur As Double, ByVal NbCalculs As Double) EtiFactorielle.Text = Valeur.ToString ' Afficher le résultat BFactorielle.Enabled = True ' Libérer le bouton EtiNbCalcul.Text = NbCalculs.ToString ' Mettre à jour le nombre de calculs End Sub Private Sub EvFinDouble(ByVal Valeur As Integer, ByVal NbCalculs As Double) EtiDouble.Text = Valeur.ToString ' Afficher le résultat BDouble.Enabled = True ' Libérer le bouton EtiNbCalcul.Text = NbCalculs.ToString ' Mettre à jour le nombre de calculs End Sub Private Sub EvFinIteration(ByVal Nombre As Integer, ByVal NbCalculs As Double) EtiIteration.Text = Nombre.ToString ' Afficher le résultat BIteration.Enabled = True ' Libérer le bouton EtiNbCalcul.Text = NbCalculs.ToString ' Mettre à jour le nombre de calculs End Sub End Class Pour le test de l'exécution de toute l'application sur son thread courant, les lignes Procedures.ChoixThreads() doivent être maintenues en commentaires. Voici un test démonstratif. L'encodage de la valeur 10 et le clic de BIteration provoque un traitement qui doit durer au moins 5 secondes. Pendant ce temps, il est impossible d'encoder une autre valeur ni de cliquer un autre bouton. En réalité, le clic d'un autre bouton est mémorisé dans le buffer et il est pris en compte quand le traitement en cours se termine. Ce décalage entre une action de l'utilisateur et le feedback que lui renvoie l'application est source de confusions, d'erreurs, et aussi d'inconfort pour l'utilisateur. Il s'agit d'un problème fréquent qui se produit quand le processeur est accaparé par un traitement. Tous les autres traitements exécutés sur le même thread sont en souffrances. En plus du gain de performances déjà constaté, le multithreading apporte la solution à ce problème. Peu de modifications sont nécessaires pour que les traitements de cette application soient exécutés sur des threads différents. Dans la classe MesProcedures, il faut ajouter les trois lignes suivantes de déclaration des threads à la suite des déclarations des variables de la classe, ainsi que la procédure ChoixThread qui facilite la programmation des appels de threads.

Public ThrFactorielle As Thread Public ThrDouble As Thread Public ThrIteration As Thread

Public Sub ChoixThreads(ByVal ThreadChoisi As Integer) Select Case ThreadChoisi Case 1 ThrFactorielle = New Thread(AddressOf CalculeFactorielle) ThrFactorielle.Start() Case 2 ThrDouble = New Thread(AddressOf CalculeDouble) ThrDouble.Start() Case 3 ThrIteration = New Thread(AddressOf ExecuteIteration) ThrIteration.Start() End Select End Sub

Page 246: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 55

Dans la classe FBase, trois procédures doivent être modifiées comme indiqué précédemment. Ce sont les procédures BDouble_Click, BFactorielle_Click et BIteration_Click dans lesquelles les lignes d'appels des procédures de traitements et commentées Lancer le calcul sur le thread doivent être placées en commentaire, tandis que les lignes Procedures.ChoixThreads() doivent être rendues actives. Par exemple, voici la procédure BDouble_Click ainsi modifiée.

Private Sub BDouble_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BDouble.Click

Procedures.ValeurDouble = CInt(TValeur.Text) BDouble.Enabled = False ' Procedures.CalculeDouble() ' Lancer le calcul sur le thread Procedures.ChoixThreads(2) ' Lancer le calcul sur un autre thread End Sub

Une fois ces modifications réalisées, le test précédent offre une toute autre disponibilité de l'application. Il est possible d'encoder d'autres valeurs avant même que le traitement initial ait terminé ses itérations. Mais au moment où le résultat d'un autre traitement devrait être affiché dans l’étiquette qui le concerne, le débogueur suspend l’application et produit un message Opération inter-threads non valide : le contrôle '…' a fait l'objet d'un accès à partir d'un thread autre que celui sur lequel il a été créé. Cette intervention du débogueur, qui ne se produit d’ailleurs pas en exploitation réelle, peut être désactivée en attribuant la valeur False à la propriété CheckForIllegalCrossThreadCalls du composant. Toutefois, il s’agit là de l’indication que le Framework n’offre aucune sécurité pour cette communication entre threads et il est donc prudent d’implémenter le marshaling. Ce contrôle de la communication entre threads ne s’effectuait pas sous VS 2003. Il faut remarquer que dans cet exemple, le nombre de calculs présenté en cours de traitement n'est pas significatif. Sur l'illustration ci contre, le nombre de calculs est 13. Il n'en faut pourtant pas autant pour établir le double et la factorielle de la valeur 10. La différence s'explique du fait que pendant ces calculs, le traitement des itérations a également modifié la variable vNbCalcul. Bien sûr, il y a des outils de verrouillage de l'accès aux données (Mutex, SyncLock). Mais ces outils sont bloquants et leur usage dans cet exemple anéantirait tout l'avantage du multithreading. L'implémentation du marshaling ne nécessite aucune modification de la classe MesProcedures. Par contre plusieurs modifications s'imposent dans FBase. Il faut ajouter les trois lignes suivantes de déclaration des délégués à la suite des déclarations des variables de la classe et écrire les trois procédures déléguées.

Public Delegate Sub FinFactorielle(ByVal Valeur As Double, ByVal Calculs As Double) Public Delegate Sub FinDouble(ByVal Valeur As Integer, ByVal Calculs As Double) Public Delegate Sub FinIteration(ByVal Nombre As Integer, ByVal Calculs As Double)

Public Sub TraiteFinFactorielle(ByVal Valeur As Double, ByVal NbCalculs As Double) EtiFactorielle.Text = Valeur.ToString ' Afficher le résultat BFactorielle.Enabled = True ' Libérer le bouton EtiNbCalcul.Text = NbCalculs.ToString ' Mettre à jour le nombre de calculs End Sub Public Sub TraiteFinDouble(ByVal Valeur As Integer, ByVal NbCalculs As Double) EtiDouble.Text = Valeur.ToString ' Afficher le résultat BDouble.Enabled = True ' Libérer le bouton EtiNbCalcul.Text = NbCalculs.ToString ' Mettre à jour le nombre de calculs End Sub Public Sub TraiteFinIteration(ByVal Nombre As Integer, ByVal NbCalculs As Double) EtiIteration.Text = Nombre.ToString ' Afficher le résultat BIteration.Enabled = True ' Libérer le bouton EtiNbCalcul.Text = NbCalculs.ToString ' Mettre à jour le nombre de calculs End Sub

Les lignes de codes de ces trois procédures ci dessus sont exactement celles des procédures événementielles correspondantes des versions précédentes. Il faut donc maintenant modifier chacune de ces procédures en y remplaçant

Page 247: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 56

le code recopié ici par l'appel asynchrone des procédures déléguées. Voici les procédures événementielles ainsi modifiées.

Private Sub EvFinFactorielle(ByVal Valeur As Double, ByVal NbCalculs As Double) Me.BeginInvoke(New FinFactorielle(AddressOf TraiteFinFactorielle), New Object() _ {Valeur, NbCalculs}) End Sub Private Sub EvFinDouble(ByVal Valeur As Integer, ByVal NbCalculs As Double) Me.BeginInvoke(New FinDouble(AddressOf TraiteFinDouble), New Object() _ {Valeur, NbCalculs}) End Sub Private Sub EvFinIteration(ByVal Nombre As Integer, ByVal NbCalculs As Double) Me.BeginInvoke(New FinIteration(AddressOf TraiteFinIteration), New Object() _ {Nombre, NbCalculs}) End Sub

L’implémentation du marshaling réduit un peu les performances des applications. Les mesures relevées sur l’exemple précédent sont de 10,194 secondes pour une programmation sans marshaling et de 10,234 avec marshaling, soit un écart de moins de 0.004% du temps d’exécution. Programmation asynchrone et multithreading : une synthèse La démonstration de la supériorité du multithreading en matière de performances a été montrée dans les pages précédentes. La programmation asynchrone apporte elle aussi un appréciable gain de rapidité, mais elle impose l'usage de délégués. Toutefois, cette contrainte offre l'avantage de son inconvénient puisque le délégué peut être une fonction avec plusieurs arguments et retournant un résultat. En programmation multithreads, les méthodes exécutées ne peuvent être que des Sub sans arguments. L'usage des méthodes Property et des événements s'imposent comme étant les meilleurs moyens de communications avec les threads. Ceci implique, idéalement, la création de classes spécifiques pour les traitements et événements du multithreads. L'un comme l'autre, ces modes de programmation nécessitent des précautions quand à l'interprétation des résultats lorsque plusieurs méthodes ont traité des données communes. Toutefois, ils acceptent tous deux des méthodes de verrouillages et de synchronisations de l'accès aux données et aux ressources qu'ils manipulent. Ces méthodes, qui ont été illustrées dans les exemples concernant le multithreading, sont aussi applicables aux méthodes asynchrones. Ainsi, dans l'exemple de programmation asynchrone où Traitement1 (qui affiche les entiers de 10 à 15) et Traitement2 (qui affiche les entiers de 20 à 25) se partagent la Console pour afficher leurs valeurs presque à tour de rôle, l'emploi d'un Mutex aurait permis à celui qui a commencé l'affichage de terminer son travail avant de laisser la main à l'autre. Mais les dispositifs de verrouillages et de synchronisations réduisent fortement le bénéfice du travail simultané de plusieurs méthodes, qu'elles soient asynchrones ou multithreads. En résumé, au plus s'élève le gain en performance, au plus la communication de données devient ardue, et au plus la protection de données communes réduit presque à néant les efforts du programmeur. La programmation asynchrone convient à l'exécution de petites procédures ou fonctions, et elle offre quelques facilités pour le transfert de peu de données, ainsi que pour la récupération d'un résultat unique. La programmation multithread convient à l'exécution de toutes les procédures, et surtout celles dont les traitements accaparent fortement le processeur. Dans un mode comme dans l'autre, le programmeur doit concevoir son développement de sorte à éviter l'exécution simultanée de méthodes utilisant les mêmes données. En programmation multithreads, il faut empêcher un thread secondaire de déclencher l’exécution d’une méthode synchrone sur le thread qui l’a instancié par l’implémentation du marshaling, c'est-à-dire en délégant les traitements de réponses aux événements émis par un thread secondaire à des procédures asynchrones.

Page 248: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 57

La persistance La persistance est la mémorisation des valeurs des propriétés d'un objet en vue d'une réutilisation future. Les différents objets manipulés possèdent des propriétés dont les valeurs peuvent être modifiées en cours d'exécution. Mais à chaque nouvelle utilisation, ce sont les valeurs par défaut, ou celles prévues dans le code par le programmeur, qui affectent les objets lors de leur instanciation. La persistance d'un objet se réalise par l'enregistrement des valeurs de toutes ses propriétés, généralement dans un fichier binaire. La solution idéale consiste à doter les objets des moyens de persistance qui leur sont nécessaires ainsi que d'un constructeur capable d'effectuer la récupération des valeurs sauvées précédemment. L'exemple suivant illustre cette façon de faire. La classe concernée par la persistance possède un constructeur adapté et les méthodes de traitements des fichiers les plus appropriés à ses données. L'application cliente doit désigner le nom du fichier, avec son chemin complet, en le passant par paramètre à la méthode appropriée de l'instance. Imports System.IO Public Class UneClasse ' Une classe quelconque exposant une valeur Dim UneChaine As String ' de type String Public Sub New(ByVal ArgValeur As String) ' Constructeur permettant l'initialisation UneChaine = ArgValeur ' de la valeur UneChaine End Sub Public Property Chaine() As String ' Property permettant l'accès à UneChaine Get Return UneChaine End Get Set(ByVal Valeur As String) UneChaine = Valeur End Set End Property End Class Public Class UneClasseSpecialisee ' Une autre classe dérivée de la précédente Inherits UneClasse ' expose une valeur de type Integer Dim UnEntier As Integer ' Son constructeur permet l'initialisation

' de la valeur UnEntier et de celle héritée Public Sub New(ByVal V As Integer, ByVal S As String) MyBase.New(S) UnEntier = V End Sub Public Property Entier() As Integer ' Property permettant l'accès à UnEntier Get Return UnEntier End Get Set(ByVal Valeur As Integer) UnEntier = Valeur End Set End Property End Class Public Class UneAutreClasse ' Une classe client expose un vecteur de 5 Dim MesValeurs(4) As UneClasseSpecialisee ' objets de type UneClasseSpecialisee Public Sub New() ' Initialisation par défaut MesValeurs(0) = New UneClasseSpecialisee(0, "Zéro") MesValeurs(1) = New UneClasseSpecialisee(1, "Un") MesValeurs(2) = New UneClasseSpecialisee(2, "Deux") MesValeurs(3) = New UneClasseSpecialisee(3, "Trois") MesValeurs(4) = New UneClasseSpecialisee(4, "Quatre") End Sub

Page 249: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 58

Public Sub New(ByVal Fichier As String) ' Initialisation par lecture d'un fichier Charge(Fichier) End Sub ' Remplacement d'un élément du vecteur Public Sub New(ByVal Indice As Integer, ByVal ArgValeur As Integer,

ByVal ArgChaine As String) MesValeurs(Indice) = New UneClasseSpecialisee(ArgValeur, ArgChaine) End Sub ' Accès à un élément donné Public Property UneValeur(ByVal Indice As Integer) As UneClasseSpecialisee Get Return MesValeurs(Indice) End Get Set(ByVal Value As UneClasseSpecialisee) MesValeurs(Indice).Entier = Value.Entier MesValeurs(Indice).Chaine = Value.Chaine End Set End Property Public Sub Sauve(ByVal Fichier As String) ' Ecriture du vecteur dans un fichier Dim FichierSortie As New BinaryWriter(File.Open(Fichier, FileMode.Create,

FileAccess.Write)) For i As Integer = 0 To 4 FichierSortie.Write(MesValeurs(i).Entier) FichierSortie.Write(MesValeurs(i).Chaine) Next FichierSortie.Close() End Sub Public Sub Charge(ByVal Fichier As String) ' Instanciation des éléments du vecteur Dim i As Integer ' par lecture des valeurs du fichier Dim FichierEntree As New BinaryReader(File.Open(Fichier, FileMode.Open,

FileAccess.Read)) For i = 0 To 4 MesValeurs(i) = New UneClasseSpecialisee(FichierEntree.ReadInt32,

FichierEntree.ReadString) Next FichierEntree.Close End Sub End Class Module MonModule Private Sub Affiche(ByVal Obj As UneAutreClasse) For i As Integer = 0 To 4 Console.WriteLine(Obj.UneValeur(i).Entier) Console.WriteLine(Obj.UneValeur(i).Chaine) Next End Sub Sub Main() Dim UnObjet As New UneAutreClasse Dim UnAutre As New UneClasseSpecialisee(7, "Sept") Dim NomFichier As String = "X:\MesDonnees\MesObjets.txt" ' Affiche les éléments suite à l'instanciation avec le constructeur par défaut Affiche(UnObjet) ' Remplace l'élément d'indice 3 UnObjet.UneValeur(3) = UnAutre ' Affiche les éléments suite à la modification Affiche(UnObjet) ' Appel de la méthode de sauvegarde de l'instance UnObjet.Sauve(NomFichier)

Page 250: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBOBJ - 59

' Instanciation avec le constructeur "lecteur de fichier" Dim UnAutreObjet As New UneAutreClasse(NomFichier) ' Affiche les éléments suite à l'instanciation par lecture du fichier Affiche(UnAutreObjet) End Sub End Module

Page 251: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 1

Programmation orientée bases de données

en VB.Net

Page 252: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 2

Tables des matières des pages VBDB L'ADO.Net .......................................................................................................................................................................... 4

L'accès aux sources de données................................................................................................................................ 4 Les modes d'accès aux bases de données ................................................................................................................. 4

Le mode connecté .......................................................................................................................................... 4 Le mode déconnecté ....................................................................................................................................... 5

Les bases de données Access .............................................................................................................................................. 5 Création d'une base Access ...................................................................................................................................... 5 Compactage d'une base Access ................................................................................................................................ 6 Réglage d'un pilote ODBC pour Access .................................................................................................................. 7

Le mode connecté ............................................................................................................................................................... 8 La connexion ............................................................................................................................................................ 8

Fonctionnalités de l'objet Connection ............................................................................................................ 8 Propriétés ............................................................................................................................................. 8 Méthodes .............................................................................................................................................. 9 Evénements .......................................................................................................................................... 9

L'objet Command ................................................................................................................................................... 10 Propriétés ........................................................................................................................................... 10 Méthodes ............................................................................................................................................ 10 Exemple ............................................................................................................................................. 11

L'objet DataReader ................................................................................................................................................. 11 Propriétés ........................................................................................................................................... 11 Méthodes ............................................................................................................................................ 12 Exemples ............................................................................................................................................ 12

L'objet Transaction ................................................................................................................................................. 13 Exemple ............................................................................................................................................. 13

Connexion polyvalente ........................................................................................................................................... 14 Obtention de la chaîne de connexion ..................................................................................................................... 15

Le mode déconnecté ......................................................................................................................................................... 16 La connexion en lecture ......................................................................................................................................... 16

L'objet DataAdapter ..................................................................................................................................... 16 Deux méthodes de lecture ............................................................................................................................ 16 Les objets DataSet et DataTable .................................................................................................................. 16

Propriété du DataSet .......................................................................................................................... 16 Propriété du DataSet et du DataTable ................................................................................................ 16 Propriétés du DataTable ..................................................................................................................... 16 Méthodes du DataSet et du DataTable............................................................................................... 17 Usage simple du DataSet ................................................................................................................... 17 Usage simple du DataTable ............................................................................................................... 18

Création d'une base de données en mémoire .......................................................................................................... 19 Sauvegarde de la base mémoire dans une base Access ................................................................................ 22

Utilisation des relations et contraintes d'intégrité ................................................................................................... 23 Lectures des données ................................................................................................................................... 23

Lecture linéaire .................................................................................................................................. 23 Lectures hiérarchiques ....................................................................................................................... 23

Les contraintes d'intégrités ........................................................................................................................... 24 Quelques cas ...................................................................................................................................... 24

Mise à jour de la base source et gestion des conflits .............................................................................................. 25 Bases de données et composants visuels .......................................................................................................................... 29

La liaison à une base de données ............................................................................................................................ 29 La présentation des données ................................................................................................................................... 29

Lecture et affichage d'une simple table ........................................................................................................ 31 Lecture et affichage de tables liées par une relation .................................................................................... 31 La recherche d'un enregistrement................................................................................................................. 33

La manipulation des données ................................................................................................................................. 34 L'édition contrôlée d'un enregistrement ....................................................................................................... 34 L'ajout d'un enregistrement .......................................................................................................................... 36 La suppression d'un enregistrement ............................................................................................................. 37

La mise à jour de la base ........................................................................................................................................ 38

Page 253: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 3

Le composant DataGridView ................................................................................................................................. 40 L’impression des données par CrystalReport ......................................................................................................... 41

Page 254: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 4

L'ADO.Net

L'accès aux sources de données L'accès et le traitement des sources de données sous Visual Studio .Net repose exclusivement sur la technologie ADO.Net. Bien qu'inspirée d'une technologie ADO antérieure (ActiveX Data Object), l'ADO.Net est très différente par sa simplicité d'emploi et par ses fonctionnalités. Elle permet l'accès à diverses sources de données par l'intermédiaire de fournisseurs d'accès OLEDB (Object Linking and Embedding DataBase) et de pilotes ODBC (Open DataBase Connectivity). Les fournisseurs OLEDB permettent l'accès à des sources de données aussi différentes que fichiers à plat, messageries, annuaires, bases de données, …, tandis que les pilotes ODBC permettent seulement (ou presque) l'accès à des bases de données. Mais les technologies OLE et ODBC ne sont pas neuves et la puissance de l'ADO.Net réside ailleurs, notamment dans le fait que peu d'objets soient nécessaires à l'établissement de la connexion à une source de données et à son exploitation. Ces objets sont dépendants du fournisseur OLEDB ou du pilote ODBC et une application peut accéder à n'importe quelle source de données sans modification de son code hormis l'instanciation et le réglage éventuel de ces objets. Des fournisseurs OLEDB et pilotes ODBC sont disponibles par défaut sous DotNet et gérés par le Framework (code managé). Pour d'autres sources de données il appartient à leur producteur de livrer les fournisseurs d'accès adéquats. Les anciennes technologies de connexions aux sources de données, telle que DAO (Data Access Object) utilisée dans ce cours pour permettre la création d'une base de type Access, peuvent toujours être utilisées mais ne sont pas gérées par le Framework (code non managé). Lorsqu'un fournisseur OLEDB et un pilote ODBC sont tous deux disponibles, c'est l'utilisation de l'OLEDB qui présente les meilleures performances et fiabilités. Les fournisseurs d'accès de ADO.Net sont livrés dans les espaces de noms :

System.Data.ODBC Contient les objets associés aux pilotes ODBC System.Data.OleDB Contient les objets associés aux fournisseurs OLEDB.Net System.Data.SqlClient Contient les objets associés au fournisseur Sql Server System.Data.OracleClient Contient les objets associés au fournisseur Oracle

Les modes d'accès aux bases de données La technologie DotNet permet l'exploitation d'une base de données sous deux modes différents, chacun présentant ses avantages et ses inconvénients. Le mode connecté En mode connecté, la connexion avec la base de données est maintenue tout le temps nécessaire, même si le traitement consiste en de l'encodage qui peut durer plusieurs heures. Toute modification validée est immédiatement répercutée sur la base et, en cas de panne du système ou du réseau, seules les données en cours de dactylographie sont perdues. La mise à jour simultanée de données par plusieurs utilisateurs ne pose ordinairement pas de problème. C'est le moteur de gestion de la base de données qui gère les mises à jour simultanées. Par exemple, Oracle verrouille une ligne de la base de données dès qu'un utilisateur la modifie. Elle reste verrouillée, et donc inaccessible aux autres utilisateurs, jusqu'à ce que celui qui l'a modifiée valide sa modification ou l'abandonne. Les phases de travail avec la source de données sont évidentes en mode connecté. Ce sont les suivantes :

1. Ouvrir une connexion avec la source de données 2. Travailler avec la source de données en lecture et en écriture 3. Fermer la connexion

Les principaux objets ADO.Net concernés par ce mode d'accès sont Connection, Command et DataReader.

Page 255: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 5

Le mode déconnecté En mode déconnecté, la connexion avec la base de données n'est établie que le temps nécessaire à l'acquisition d'une copie mémoire des informations nécessaires au traitement. Elle est éventuellement rétablie ultérieurement si une mise à jour de la base s'impose. Les modifications sont ainsi répercutées en une seule opération quand tous les traitements sont terminés et un encodage de plusieurs heures peut être perdu si une panne électrique survient avant la mise à jour. La mise à jour de données modifiées simultanément par plusieurs utilisateurs est plus délicate. En effet, si deux utilisateurs disposent d'une même copie initiale d'un jeu de données, le dernier des deux à commander la mise à jour provoque l'écrasement de celle commandée par l'autre. Ce mode de travail permet de réduire au strict minimum la charge occasionnée sur la base de données par de nombreuses connexions comme c'est généralement le cas sous Internet ou sur de grands réseaux. Le mode déconnecté offre un avantage tout particulier lorsqu'il s'agit d'échanger l'information sur un réseau hétérogène. Les données stockées localement sont converties de façon tout à fait transparente au format XML (eXtensible Markup Language) et elles peuvent être communiquées à tout autre système disposant de ce langage. Les phases de travail avec la base de données en mode déconnecté sont les suivantes :

1. Ouvrir une connexion avec la source de données 2. Obtenir une copie mémoire de tout ou partie des données de la source 3. Fermer la connexion 4. Travailler avec la copie mémoire des données en lecture et en écriture 5. Si mise à jour nécessaire :

1. Ouvrir une connexion avec la source de données 2. Envoyer les données modifiées à la source 3. Fermer la connexion

Les principaux objets ADO.Net concernés par ce mode d'accès sont Connection, DataSet et DataAdapter.

Les bases de données Access L'accès aux bases de données Access et leurs traitements s'opèrent ordinairement sous la technologie ADO.Net par l'usage du fournisseur OLEDB proposé par Microsoft et de préférence à l'ODBC également disponible sous Windows. Mais la technologie ADO.Net, qui est destinée au travail sur des sources de données existantes, ne permet pas la création de bases de données. Toutefois, le programmeur qui développe une application dont les données doivent être stockées dans une base Access, peut souhaiter y intégrer la création de la base initiale afin de se dispenser de l'emploi du logiciel Access lors de l'installation de son produit. Le programmeur peut également souhaiter intégrer dans son application un compactage automatique de la base de données. Ces opérations de création et de compactage se commandent par des méthodes de l'objet DBEngine de l'espace de noms DAO qui contient les objets DAO du modèle COM (Component Object Model), ancienne technologie qui a été essentielle à la programmation sous Windows, mais qui n'est plus maintenue sous DotNet qu'à des fins de transitions. Les codes et données du modèle COM ne sont pas gérés par le Framework de DotNet. L'emploi des objets COM produit donc du code non managé et il convient d'en user aussi peu que possible. C'est pourquoi l'emploi des objets DAO est limité dans ce cours à l'illustration de ces deux opérations particulières du monde Access. Pour utiliser l’objet DBEngine, l’application doit référencer le composant Microsoft DAO Object Library.

Création d'une base Access La création d'une base Access avec DBEngine produit un fichier .mdb qui ne contient aucune table. Il convient donc de programmer ensuite l'ajout des tables en utilisant la technologie ADO.Net. La fonction CreationDBAccess ci après réalise la création d'une base de données Access dont le chemin et le nom lui sont passés par un paramètre de type chaîne de caractères. Elle retourne la valeur True en cas de succès et False dans le cas contraire. La méthode de création, qui est CreateDatabase, génère une exception si la base de données existe déjà ou si le chemin est inexistant. Pour remplacer une base existante, il faut d'abord la supprimer par la méthode Delete de l'objet File. La seule gestion d'erreur faite ici consiste à attribuer False comme valeur de retour de la fonction.

Page 256: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 6

La méthode CreateDatabase nécessite deux paramètres : le nom complet de la base à créer et une constante de l'espace DAO qui concerne la façon dont les tris de chaînes doivent être exécutés en fonction de la langue (avec ou sans caractères accentués, par exemple).

Public Function CreationDBAccess(ByVal NomComplet As String) As Boolean Dim dbe As New DAO.DBEngine Dim db As DAO.DataBase Dim Reussite As Boolean = True Try db = dbe.CreateDatabase(NomComplet, DAO.LanguageConstants.dbLangGeneral) Catch ex As Exception Reussite = False Finally

If Reussite Then db.Close ' Libère le fichier pour permettre son accès par d'autres codes End if db = Nothing dbe = Nothing

End Try Return Reussite End Function

L'usage de cette fonction peut être le suivant :

Sub Main() Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" If CreationDBAccess(NomDB) Then ' Tout va bien, on continue … Else ' Traitement de l'erreur End If End Sub

Compactage d'une base Access Le compactage est une opération qui consiste à débarrasser le gestionnaire de la base de données de toutes ses informations inutiles comme par exemple, les enregistrements supprimés par l'application mais encore physiquement présents dans le fichier de la base. Cette opération réduit son volume et améliore l'accès aux données. Le compactage est réalisé par la méthode CompactDatabase qui produit une copie de la base traitée. Cette méthode nécessite deux paramètres : le nom complet de la base à traiter et celui du fichier à produire. La méthode génère une exception si le fichier cible existe déjà ou si le compactage ne peut être mené à bien pour n'importe quelle autre raison. Dans la procédure CompacteDBAccess ci après, le nom du fichier cible est celui de la base elle-même auquel les caractères ~TMP sont ajoutés. Quand le compactage s'est bien déroulé, le fichier cible est renommé avec le nom de la base de données d'origine après suppression de cette dernière. Cette façon de faire présente l'avantage de conserver intacte la base d'origine en cas d'accident de compactage.

Public Sub CompacteDBAccess(ByVal NomComplet As String) Dim dbe As New DAO.DBEngine Dim Reussite As Boolean = True Try dbe.CompactDatabase(NomComplet, NomComplet & "~TMP") Catch ex As Exception Reussite = False Finally If Reussite Then IO.File.Delete(NomComplet) IO.File.Move(NomComplet & "~TMP", NomComplet) End If dbe = Nothing End Try End Sub

Page 257: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 7

Réglage d'un pilote ODBC pour Access Bien que l'usage du fournisseur OLEDB soit préférable au pilote ODBC, le réglage de l'ODBC pour Access est l'occasion d'illustrer ce processus par ailleurs absolument nécessaire pour d'autres types de bases comme MySQL, par exemple. Le réglage de l'ODBC s'effectue par l'outil "Sources de données (ODBC)" du système d'exploitation. Cet outil se trouve dans les "Outils d'administration" du panneau de configuration.

Un fois chargé, l'outil présente une fenêtre munie de divers onglets. Pour les besoins d'une application particulière, il convient de sélectionner l'onglet Sources de données utilisateur et de cliquer le bouton Ajouter…

Il faut alors choisir le pilote ODBC souhaité, soit Microsoft Access Driver dans ce cas, et cliquer sur le bouton Terminer. La boîte de dialogue présentée ci contre s'ouvre alors. Elle permet le réglage du pilote. L'indication d'un nom identifiant le pilote et la sélection de la base par le bouton Sélectionner sont les réglages ordinairement suffisants. Cette boîte de dialogue est différente d'un pilote à l'autre car elle expose des fonctionnalités et propriétés spécifiques au type de sources de données choisi. Par exemple, celle présentée ci-dessous pour le réglage d'un ODBC MySql est bien différente de celle utilisée pour Access.

A l'issue de ce réglage, le nom de la source de données se retrouve dans la liste de l'onglet Sources de données utilisateur. Le bouton Configurer… de cet onglet ouvre à nouveau la boîte de dialogue de réglage d'un pilote sélectionné dans la liste pour en permettre d'éventuelles modifications. Le réglage d'un pilote ODBC dédié à une base de données est terminé et son nom est stocké parmi tous les DSN (Data Source Name) connus du système. Si la base de données X:\MesDonnees\MaBaseAccess.mdb créée précédemment au moyen de l'objet DAO a été sélectionnée pour le réglage de cet ODBC TestAccess, alors la chaîne de connexion DSN=TestAccess; permet l'accès cette base.

Page 258: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 8

Le mode connecté

La connexion La connexion à une base de données se définit par la paramétrisation d'un objet Connection livré par un des fournisseurs d'accès disponibles. Il est commode de réaliser les Imports adéquats, à savoir :

Imports System.Data Imports System.Data.Common

et un ou plusieurs des suivants selon la ou les base(s) de données utilisée(s) :

Imports System.Data.Odbc ' Pour utiliser un pilote ODBC quelconque Imports System.Data.OleDb Imports System.Data.SqlClient Imports System.Data.OracleClient ' A télécharger chez Microsoft (cf. MDAC …) ' …

Le programmeur dispose après cela de l'objet de Connection dont il a besoin et il peut l'instancier comme ci-dessous. Il est intéressant de remarquer que le nom du type de l'objet de connexion rappelle celui de son fournisseur.

Dim MaConnexion As New OdbcConnection Dim MaConnexion As New OleDBConnection Dim MaConnexion As New SqlConnection Dim MaConnexion As New OracleConnection ' …

La liaison à la base de données n'est pas établie à l'instanciation d'un objet Connection. Il faut encore définir la propriété ConnectionString de l'objet et invoquer sa méthode Open. La connexion se termine par l'exécution de la méthode Close. Par exemple, la base de données X:\MesDonnees\MaBaseAccess.mdb créée précédemment au moyen de l'objet DAO, pour laquelle un pilote ODBC TestAccess a été réglé, peut être accédée par le code suivant :

Dim MaConnexion As New OdbcConnection MaConnexion.ConnectionString = "DSN=TestAccess;" MaConnexion.Open() ' Connexion effective ' Travail sur la base de données … MaConnexion.Close() ' Fermeture de la connexion

Mais puisqu'il existe un fournisseur OLEDB pour le traitement des bases Access, il est préférable d'abandonner l'ODBC et de reprogrammer la connexion précédente comme ceci :

Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" Dim MaConnexion As New OleDBConnection MaConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;Data Source=" &

NomDB & ";" MaConnexion.Open() ' Connexion effective ' Travail sur la base de données … MaConnexion.Close() ' Fermeture de la connexion

Fonctionnalités de l'objet Connection Outre la propriété ConnectionString, qui est aussi accessible en lecture, et les méthodes Open et Close, l'objet Connection offre quelques fonctionnalités intéressantes. Propriétés Toutes les propriétés suivantes sont uniquement accessibles en lecture.

ConnectionTimeOut Délai maximum autorisé pour une tentative de connexion.

Page 259: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 9

DataSource Nom complet de la base de donnée. Provider Fournisseur d'accès à la base. State Etat de la connexion

En VB.Net, cette propriété peut livrer une des valeurs suivantes : Closed Valeur 0. La connexion est fermée. Open Valeur 1. La connexion est ouverte. Dans une version ultérieure, peut-être, la propriété pourra livrer aussi une ou

plusieurs des valeurs suivantes : Connecting Valeur 2. La connexion est entrain de se faire. Executing Valeur 4. La connexion est entrain d'exécuter une commande. Fetching Valeur 8. La connexion est entrain de récupérer des données. Broken Valeur 16. La connexion a été interrompue. Cela ne peut se produire que sur une

connexion ouverte. La connexion dans cet état peut être fermée et, éventuellement, ouverte à nouveau.

Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" Dim MaConnexion As New OleDbConnection MaConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;Data Source=" &

NomDB & ";" Console.WriteLine(MaConnexion.ConnectionString) ' Affichage de la chaîne complète Console.WriteLine(MaConnexion.DataSource) ' Affichage du contenu de NomDB Console.WriteLine(MaConnexion.Provider) ' Affiche : Microsoft.JET.OLEDB.4.0 Console.WriteLine(MaConnexion.ConnectionTimeout)' Affiche : 15 Console.WriteLine(MaConnexion.State.ToString) ' Affiche : Closed MaConnexion.Open() Console.WriteLine(MaConnexion.State.ToString) ' Affiche : Open MaConnexion.Close() Console.WriteLine(MaConnexion.State.ToString) ' Affiche : Closed

Méthodes

CreateCommand Crée un objet Command sur la connexion. BeginTransaction Retourne la référence de l'objet Transaction associé à la connexion. Cet objet

fournit les méthodes de validation (Commit) et d'annulation (Rollback) des opérations exécutées sur la connexion. Cette méthode accepte un paramètre de type IsolationLevel qui détermine le niveau de sécurité souhaité pour la transaction. Voici les valeurs possibles de ce niveau de sécurité, aussi appelé niveau d'isolation :

Chaos Valeur 16. Les modifications en attente, provenant des transactions les plus isolées, ne peuvent pas être remplacées.

ReadCommitted Valeur 4096. Les verrous partagés sont conservés pendant la lecture des données afin d'éviter tout défaut de lecture, mais les données peuvent être modifiées avant la fin de la transaction, entraînant ainsi des données fantômes ou des lectures qui ne peuvent pas être répétées.

ReadUncommitted Valeur 256. Un défaut de lecture est possible, ce qui signifie qu'aucun verrou partagé n'est émis et qu'aucun verrou exclusif n'est respecté.

RepeatableRead Valeur 65536. Des verrous sont placés sur toutes les données utilisées dans une requête afin d'empêcher d'autres utilisateurs de mettre à jour les données. Cette valeur empêche les lectures qui ne peuvent pas être répétées, mais des lignes fantômes peuvent toujours exister.

Serializable Valeur 1048576. Un verrou de plage est placé sur DataSet afin d'empêcher les autres utilisateurs de mettre à jour ou de modifier les lignes du groupe de données avant la fin de la transaction.

Unspecified Valeur -1. Un niveau d'isolation différent de celui spécifié est utilisé actuellement, mais il est impossible de le déterminer.

Evénements

StateChange Est émis à chaque changement d'état de la connexion. InfoMessage Est émis lorsque le système gestionnaire de la base de données envoie un message à

l'application.

Page 260: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 10

' Déclaration globale du module Dim WithEvents MaConnexion As New OleDbConnection

Private Sub MaConnexion_StateChange(ByVal sender As Object,

ByVal e As System.Data.StateChangeEventArgs) Handles MaConnexion.StateChange Console.WriteLine(e.OriginalState.ToString & " => " & e.CurrentState.ToString) End Sub

' Quelque part, le code de connexion et déconnexion … ' … MaConnexion.Open() ' Changement d'état ' Travail sur la base de données … MaConnexion.Close() ' Changement d'état

Pendant l'exécution de ce code, la procédure événementielle MaConnexion_StateChange est sollicitée deux fois et affiche successivement :

Closed => Open Open => Closed

L'objet Command L'objet Command créé par la méthode CreateCommand d'un objet Connection est indispensable au travail avec une base de données. C'est par ses propriétés et ses méthodes que sont définis, puis transmis, les ordres que le programmeur souhaite faire exécuter sur la base. Propriétés

CommandType La commande peut être de trois types à préciser : Text Le texte de la commande doit être un ordre SQL. TableDirect Le texte de la commande doit être le nom d'une table de la base. StoredProcedure Le texte de la commande doit être le nom d'une procédure stockée dans la base.

CommandText Chaîne de caractères contenant la commande conformément à ce qui est stipulé dans la propriété CommandType.

CommandTimeOut Délai maximum autorisé pour l'exécution de la commande. Connection Connexion sur laquelle doit s'exécuter la commande. C'est la connexion qui l'a

créée ou une autre du même fournisseur d'accès. Transaction Objet Transaction de la connexion qui doit contrôler cette commande.

Méthodes

Cancel Annule la commande qui vient d'être exécutée. ExecuteNonQuery Exécute une commande qui n'est pas une requête de sélection. ExecuteScalar Exécute une commande qui est une requête ne retournant qu'une seule valeur. Le

lancement d'une requête de sélection par cette méthode retourne la valeur de la première colonne de la première ligne trouvée par cette requête.

ExecuteReader Exécute une commande qui est une requête de sélection et retourne le résultat dans un objet DataReader. Cette méthode peut recevoir un paramètre qui modifie son comportement comme, par exemple :

CommandBehavior.CloseConnection Provoque la fermeture de la connexion à la fin de la commande de sélection.

CommandBehavior.SingleRow Le DataReader reçoit seulement la première ligne du jeu d'enregistrements.

CommandBehavior.SchemaOnly Le DataReader reçoit seulement les noms des champs.

Page 261: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 11

Exemple Imports System.Data.OleDb Module UnModule Public Function CreationDBAccess(ByVal NomComplet As String) As Boolean ' Code de cette fonction déjà écrit précédemment End Function Sub Main() Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" Dim MaConnexion As New OleDbConnection ' Instanciation d'un objet Connection Dim MaCommande As New OleDbCommand ' Instanciation d'un objet Command Dim TLocalePers(4) As String ' Vecteur de chaînes pour 5 noms Dim i As Integer TLocalePers(0) = "Azerty" ' Stockage des noms dans le vecteur TLocalePers(1) = "Qwerty" ' TLocalePers(2) = "Panzani" ' TLocalePers(3) = "Ranzani" ' TLocalePers(4) = "Barety" ' If Not CreationDBAccess(NomDB) Then End ' Création d'une base Access MaConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;Data Source="

& NomDB & ";" MaConnexion.Open() ' Connexion effective via fournisseur OLEDB Console.WriteLine(MaConnexion.State.ToString) ' Affiche : Open MaCommande = MaConnexion.CreateCommand() ' Création effective de Command MaCommande.CommandType = CommandType.Text ' Désignation du type de commande MaCommande.CommandText = "CREATE TABLE TPers (IdPers Int, Nom Text (40) )" MaCommande.ExecuteNonQuery() ' La commande CREATE TABLE n'est pas une For i = 0 To 4 ' requête. La commande INSERT non plus. MaCommande.CommandText = "INSERT INTO TPers (IdPers, Nom) VALUES (" &

i + 1 & ", '" & TLocalePers(i) & "')" MaCommande.ExecuteNonQuery() ' Stockage des valeurs du vecteur dans TPers Next MaCommande.CommandText = "SELECT COUNT(*) FROM TPers" i = MaCommande.ExecuteScalar() ' La requête SELECT COUNT retourne UNE valeur Console.WriteLine(i & " enregistrement(s)") ' Affiche : 5 enregistrement(s) Dim MesPersonnes As OleDbDataReader ' Déclaration d'un objet DataReader MaCommande.CommandText = "SELECT * FROM TPers" MesPersonnes = MaCommande.ExecuteReader() ' La requête SELECT * retourne DES valeurs Do While MesPersonnes.Read ' qui sont stockées dans l'objet DataReader Console.WriteLine(MesPersonnes.Item(0) & " " & MesPersonnes.Item(1)) Loop ' Le DataReader est parcouru ligne par ligne MesPersonnes.Close() ' par la méthode Read et chaque Item contient MaConnexion.Close() ' la valeur du champ désigné par l'indice Console.WriteLine(MaConnexion.State.ToString) ' Affiche : Closed End Sub End Module

L'objet DataReader Comme le montre clairement l'exemple précédent, l'objet DataReader reçoit un jeu d'enregistrements livré par la méthode ExecuteReader de l'objet Command. La définition du type de DataReader est dépendant du fournisseur d'accès choisi et, comme pour l'objet Connection et l'objet Command, le nom de son type rappelle celui de son fournisseur. Attention, le jeu d'enregistrements est livré en lecture seule et ne peut être parcouru que du début vers la fin. Un DataReader doit être fermé (méthode Close) avant que puisse être exécutée une autre commande ExecuteNonQuery, ExecuteScalar ou ExecuteReader. Propriétés

FieldCount Nombre de colonnes d'un enregistrement. HasRows Contient False si le DataReader ne contient aucune ligne, sinon True. Item(i) Contenu du champ désigné par l'indice i de sa colonne dans la ligne de 0 à

FieldCount - 1.

Page 262: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 12

Méthodes

Read Positionne sur l'enregistrement suivant du DataReader et retourne True tant qu'il y a encore des enregistrements disponibles.

NextResult Passe au jeu d'enregistrements suivant du DataReader et retourne True tant qu'il y a des jeux d'enregistrements disponibles. Cette méthode permet la lecture de jeux d'enregistrements issus d'une batterie de sélections. Ce mode de sélections, qui permet plusieurs sélections en une seule connexion à la base, n'est pas accepté par tous les SGBD : Access ne l'accepte pas, SQL Server l'accepte.

GetValue(i) Retourne le contenu du champ désigné par l'indice i de sa colonne dans la ligne, de 0 à FieldCount - 1.

GetName(i) Retourne le nom du champ désigné par l'indice i de sa colonne dans la ligne, de 0 à FieldCount - 1.

Item(i).GetType Retourne le type du champ désigné par l'indice i de sa colonne dans la ligne, de 0 à FieldCount - 1.

Exemples Sur base de l'exemple précédent :

' … Dim MesPersonnes As OleDbDataReader MaCommande.CommandText = "SELECT * FROM TPers" MesPersonnes = MaCommande.ExecuteReader() Console.WriteLine(MesPersonnes.FieldCount) ' Affiche : 2 Console.WriteLine(MesPersonnes.HasRows.ToString) ' Affiche : True If MesPersonnes.Read() Then ' Affichage des noms des champs d'indice 0 et 1 : IdPers Nom Console.WriteLine(MesPersonnes.GetName(0) & " " & MesPersonnes.GetName(1)) ' Affichage des types de valeurs des champs d'indice 0 et 1 : Int32 String Console.WriteLine(MesPersonnes.Item(0).GetType.ToString & " " &

MesPersonnes.Item(1).GetType.ToString) ' Affichage des contenus des champs d'indice 0 et 1 de la ligne courante : 1 Azerty Console.WriteLine(MesPersonnes.GetValue(0) & " " & MesPersonnes.GetValue(1)) End If ' Positionnement à la ligne suivante et affichage des contenus des champs Do While (MesPersonnes.Read()) Console.WriteLine(MesPersonnes.GetValue(0) & " " & MesPersonnes.GetValue(1)) Loop MesPersonnes.Close() MaConnexion.Close() ' …

Reprogrammation de l'exemple complet précédent pour SQL Server : Imports System.Data.SqlClient Module MonModule Sub Main() Dim MaConnexion As New SqlConnection ' Instanciation d'un objet Connection Dim MaCommande As New SqlCommand ' Instanciation d'un objet Command Dim MesPersonnes As SqlDataReader ' Déclaration d'un objet DataReader Dim TLocalePers(4) As String ' Vecteur de chaînes pour 5 noms Dim i As Integer TLocalePers(0) = "Azerty" ' Stockage des noms dans le vecteur TLocalePers(1) = "Qwerty" ' TLocalePers(2) = "Panzani" ' TLocalePers(3) = "Ranzani" ' TLocalePers(4) = "Barety" ' ' Chaîne de connexion pour MaBaseSQL gérée par mon serveur SQL MonServeur MaConnexion.ConnectionString = "Data Source=MonServeur;

Initial Catalog=MaBaseSQL;Integrated Security=True;" MaConnexion.Open() ' Connexion effective via fournisseur SQL MaCommande = MaConnexion.CreateCommand() ' Création effective de Command MaCommande.CommandType = CommandType.Text ' Désignation du type de commande

Page 263: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 13

MaCommande.CommandText = "CREATE TABLE TPers (IdPers Int, Nom Text (40))" MaCommande.ExecuteNonQuery() ' La commande CREATE TABLE n'est pas une For i = 0 To 4 ' requête. La commande INSERT non plus. MaCommande.CommandText = "INSERT INTO TPers (IdPers, Nom) VALUES (" &

i + 1 & ", '" & TLocalePers(i) & "')" MaCommande.ExecuteNonQuery() ' Stockage des valeurs du vecteur dans TPers Next ' Batterie de deux sélections : les requêtes SELECT sont concaténées (point virgule nécessaire) MaCommande.CommandText = "SELECT * FROM TPers;" &

"SELECT * FROM TPers WHERE (IdPers > 2);" MesPersonnes = MaCommande.ExecuteReader() Do ' Balayage des différents jeux de résultats Do While (MesPersonnes.Read()) ' Balayage des lignes d'un jeu Console.WriteLine(MesPersonnes.GetValue(0) & " " & MesPersonnes.GetValue(1)) Loop Loop Until Not MesPersonnes.NextResult ' Passage au jeu de réultats suivant MesPersonnes.Close() MaConnexion.Close() End Sub End Module

L'objet Transaction La transaction est un mécanisme fondamental du travail sur les bases de données qui est utilisé pour préserver l'intégrité des données lorsqu'il faut mettre à jour des enregistrements dans une ou plusieurs tables en ayant la certitude que les modifications sont effectuées (entérinées) ou annulées (révoquées) dans toutes les tables. Le principe d’utilisation est le suivant :

Initier la transaction (BeginTransaction) Tant qu’il y a des enregistrements à mettre à jour et qu’il ne survient pas d’erreur

Mettre à jour l’enregistrement Passer au suivant

Si une erreur survient Révoquer les mises à jour (RollBack)

Sinon Entériner les mises à jour (Commit)

Exemple

' … Dim MaTransaction As OleDbTransaction ' Déclaration d'un objet Transaction Dim Reussite As Boolean = True MaConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;Data Source=" &

NomDB & ";" MaConnexion.Open() MaTransaction = MaConnexion.BeginTransaction ' Démarrage d'une transaction MaCommande = MaConnexion.CreateCommand() MaCommande.CommandType = CommandType.Text MaCommande.Transaction = MaTransaction ' Affectation de la transaction Try For i = 0 To 4 MaCommande.CommandText = "INSERT INTO TPers (IdPers, Nom) VALUES (" &

i + 1 & ", '" & TLocalePers(i) & "')" MaCommande.ExecuteNonQuery() Next MaTransaction.Commit() ' Entérine les transactions Catch ex As Exception MaTransaction.Rollback() ' Révoque les transactions Reussite = False Finally Console.WriteLine("Transaction terminée. Réussite = " & Reussite.ToString) End Try ' …

Page 264: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 14

Connexion polyvalente Il est relativement aisé d'offrir à une application, la possibilité de travailler avec divers types de base de données. Il convient toutefois que les fonctionnalités utilisées soient compatibles avec ces bases. L'exemple suivant illustre le plus simple moyen de traiter indifféremment une base Access, ou SQL Server, ou Oracle. Mais à ce niveau de simplicité, il n'est pas possible d'exécuter une batterie de sélections car une telle tentative sur une base Access génère une erreur lors de l'exécution. La simplicité du procédé provient de l'usage d'Interface (IDbConnection, IDbCommand, IDataReader, IDbTransaction) implémentés dans les outils (Connection, Command, DataReader, Transaction) de chaque fournisseur d'accès.

Imports System.Data.Odbc Imports System.Data.OleDb Imports System.Data.SqlClient Imports System.Data.OracleClient ' … Dim MaConnexion As IDbConnection Dim MaCommande As IDbCommand Dim MaTransaction As IDbTransaction Dim MesPersonnes As IDataReader Dim Reussite As Boolean = True Dim TypeDB As Integer Console.Write("Type de base (1, 2, 3 ou 4) ? ") TypeDB = Console.ReadLine() Select Case TypeDB Case 1 ' ODBC MaConnexion = New OdbcConnection MaCommande = New OdbcCommand MaConnexion.ConnectionString = "DSN=TestAccess;" Case 2 ' OLEDB Access MaConnexion = New OleDbConnection MaCommande = New OleDbCommand Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" MaConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;

Data Source=" & NomDB & ";" Case 3 ' SQL Server MaConnexion = New SqlConnection MaCommande = New SqlCommand MaConnexion.ConnectionString = "Data Source=MonServeur;

Initial Catalog=MaBaseSQL;Integrated Security=True;" Case 4 ' Oracle MaConnexion = New OracleConnection MaCommande = New OracleCommand ' MaConnexion.ConnectionString = … End Select Dim TLocalePers(4) As String Dim i As Integer TLocalePers(0) = "Azerty" ' … MaConnexion.Open() MaTransaction = MaConnexion.BeginTransaction() MaCommande = MaConnexion.CreateCommand() MaCommande.CommandType = CommandType.Text MaCommande.Transaction = MaTransaction Try For i = 0 To 4 MaCommande.CommandText = "INSERT INTO TPers (IdPers, Nom) VALUES (" &

i + 1 & ", '" & TLocalePers(i) & "')" MaCommande.ExecuteNonQuery() Next MaTransaction.Commit() Catch ex As Exception ' …

Page 265: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 15

Obtention de la chaîne de connexion Dans les exemples précédents, plusieurs chaînes de connexion ont été définies. Chacune possède un format dépendant du fournisseur d'accès utilisé et un contenu représentant une base de données particulière sur un SGBD donné. Il est évident que la création d'une chaîne de connexion est une mission impossible sans une documentation adéquate. Or cette documentation fait souvent défaut … Mais Visual Studio offre un assistant de connexion à une source de données qui est capable de créer la chaîne idéale pour une connexion envisagée. Dès lors, la meilleure méthode d'élaboration d'une chaîne de connexion consiste à réaliser une connexion avec cet assistant et à récupérer la chaîne souhaitée dans les propriétés de la connexion. Il suffit d'un copier-coller pour placer la chaîne ainsi produite dans le code. L'assistant de connexion à une source de données est accessible par l'explorateur de serveurs (menu Affichage/Explorateur de serveurs) qui présente la fenêtre ci contre. Le menu contextuel obtenu à partir du libellé Connexions de données propose l'ajout d'une connexion et même la création d'une base SQL Server. L'ajout d'une connexion ouvre l'assistant.

Le menu contextuel obtenu à partir du libellé d'une connexion propose des fonctionnalités qui diffèrent selon le fournisseur d'accès de la connexion, ainsi que d'autres toujours présentes, dont notamment Propriétés qui fournit la chaîne de connexion convoitée.

L'explorateur de serveurs permet aussi de visualiser les objets des bases de données connectées et d'en obtenir les propriétés. Il offre également un assistant de mise au point des requêtes SQL. C'est la proposition Nouvelle requête du menu contextuel ci-dessus qui ouvre cet assistant.

Page 266: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 16

Le mode déconnecté Seul l'essentiel du mode déconnecté est abordé dans ces pages. Le lecteur désireux d'approfondir les techniques de ce mode et d'étudier les outils non exposés ici, tels que CommandBuilder, DataView ou encore le mappage des données, peut se référer à la documentation de son Visual Studio et aux pages du site de MicroSoft qui concernent l'espace de noms System.Data.

La connexion en lecture L'objet DataAdapter Comme pour le mode connecté, c'est la paramétrisation d'un objet Connection livré par un des fournisseurs d'accès disponibles qui permet l'accès à une base de données choisie. Mais cet objet n'est pas utilisé explicitement comme dans le mode connecté. Une fois sa chaîne de connexion définie, il est passé en paramètre à un DataAdapter qui se charge de la gestion des connexions et des liaisons avec la base de données via ses méthodes. De nombreux objets, tels que DataSet, DataTable ou encore DataRelation, sont utiles au travail en mode déconnecté. Les deux premiers, DataSet et DataTable, sont directement liés au DataAdapter et il n'est guère possible d'illustrer l'usage de ce dernier sans intégrer les deux autres. Deux méthodes de lecture

Fill Lecture de données pour le remplissage d'un DataSet ou d'un DataTable. FillSchema Lecture des informations de schéma pour leur affectation à un DataSet ou à un

DataTable. Les objets DataSet et DataTable Le DataSet est une représentation en mémoire d'une base de données relationnelle complète et cohérente, comprenant les tables avec leurs contraintes et relations. Le DataSet est indépendant de toute source de données et, même quand il est la copie d'une base réelle, les manipulations réalisées sur ses données ne sont pas répercutées dans la base d'origine, sauf commande explicite de mise à jour. Le DataTable est une table du DataSet. Il contient les colonnes avec leurs noms, propriétés et données. Un DataTable peut être construit de toutes pièces ou être la réplique d'une table existant dans une base réelle. Propriété du DataSet

DataSetName Nom du DataSet. HasChanges Retourne True si le DataSet contient des tables dont les enregistrements ont été

modifiés, supprimés, ajoutés. Relations Collection des relations établies entre les tables du DataSet. Tables Collection des DataTable du DataSet.

Propriété du DataSet et du DataTable

CaseSensitive Indique si les comparaisons de chaînes doivent tenir compte de la casse. Propriétés du DataTable

TableName Nom du DataTable. Columns Collection des colonnes de la table. Une colonne est un objet DataColumns. Rows Collection des lignes de la table. Une ligne est un objet DataRow. MinimumCapacity Taille initiale pour les enregistrements. PrimaryKey Tableau de colonnes faisant partie de la clé primaire. Constraints Collection des contraintes de la table. ChildRelations Collection des relations enfants. ParentRelations Collection des relations parents.

Page 267: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 17

Méthodes du DataSet et du DataTable

AcceptChanges Valide toutes les modifications effectuées. RejectChanges Refuse toutes les modifications effectuées depuis le dernier appel à

AcceptChanges. GetChanges Retourne une copie de toutes les modifications effectuées depuis le dernier appel à

AcceptChanges. Clear Efface toutes les données. Clone Copie de la structure sans copier les données. Copy Copie de la structure et des données. Reset Efface les données et les noms des champs, ne conserve que les noms des tables.

Usage simple du DataSet Pour les exemples suivants, une table des localités TLoc composée de IdLoc et Localite a été ajoutée à la base de données X:\MesDonnees\MaBaseAccess.mdb précédemment utilisée pour illustrer le mode connecté. La procédure AfficheDonnees ci-dessous permet l'affichage des noms et contenus de tous les champs de la première table d'un DataSet. Elle sera complétée plus tard pour permettre le balayage de toutes les tables du DataSet.

Private Sub AfficheDonnees(ByVal DTS As DataSet) ' Affichage des noms des colonnes For Each C As DataColumn In DTS.Tables(0).Columns Console.Write(C.ColumnName & " ") Next ' Balayage des lignes de la table et affichage du contenu de chaque colonne For Each R As DataRow In DTS.Tables(0).Rows Console.WriteLine() For Each C As DataColumn In DTS.Tables(0).Columns Console.Write(R.Item(C.ColumnName) & " ") Next Next Console.WriteLine() End Sub

' … Dim MaConnexion As New OleDbConnection Dim MaConnexionTmp As OleDbDataAdapter Dim MesDonnees As New DataSet Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" MaConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;Data Source=" &

NomDB & ";" ' La connexion est réalisée par la méthode Fill de l'objet DataAdapter qui reçoit en ' paramètres la chaîne de sélection et la connexion à utiliser MaConnexionTmp = New OleDbDataAdapter("SELECT * FROM TPers", MaConnexion) ' La méthode Fill ouvre la connexion, remplit le DataSet et ferme la connexion ' La table d'indice 0 est créée dans le DataSet ' La méthode Fill ne fournit pas le nom de la table MaConnexionTmp.Fill(MesDonnees) ' Affichage des données AfficheDonnees(MesDonnees) ' Pour obtenir une autre lecture de la base, il faut exécuter la méthode Reset du DataSet, ' faute de quoi son contenu reste inchangé MesDonnees.Reset() MaConnexionTmp = New OleDbDataAdapter("SELECT * FROM TLoc", MaConnexion) MaConnexionTmp.Fill(MesDonnees) ' Affichage des données AfficheDonnees(MesDonnees)

La solution adoptée dans l'exemple ci-dessus pour permettre la lecture et l'affichage des données de la table TLoc n'est guère satisfaisante dans la mesure où les données de la table TPers ont été perdues. Le DataSet contient une collection Tables qui ne contient qu'une seule table par défaut. Mais cette collection propose une méthode Add bien utile.

Page 268: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 18

C'est ainsi qu'au lieu de faire un Reset du DataSet pour lire les localités, il est possible d'ajouter une table et de conserver ensuite les données de TPers en même temps que celles de TLoc. Il suffit d'utiliser la table d'indice 0 ou celle d'indice 1 selon les données souhaitées.

' … ' MesDonnees.Reset() ' Inutile maintenant MesDonnees.Tables.Add() ' Ou bien : … .Add("TLocalites") MaConnexionTmp = New OleDbDataAdapter("SELECT * FROM TLoc", MaConnexion) ' Si la table ajoutée est nommée, son nom est disponible et il peut remplacer son indice ' Console.WriteLine(MesDonnees.Tables(1).TableName) MaConnexionTmp.Fill(MesDonnees.Tables(1)) ' Ou bien : … .Tables("TLocalites") AfficheDonnees(MesDonnees) ' Affichage des données

Comme illustré en commentaires dans l'exemple ci-dessus, une table du DataSet peut être nommée à la création, mais elle peut aussi être nommée et renommée par la suite :

MesDonnees.Tables(0).TableName = "TPersonnes" Usage simple du DataTable Dans le cadre d'une application aussi simple que celle de l'exemple précédent, il n'y a pas de différence à l'usage entre le DataSet et le DataTable, sauf la possibilité de constituer le DataSet par l'ajout de DataTable. Cette possibilité facilite la reconstitution fidèle en mémoire de tables existantes.

Dim MaConnexion As New OleDbConnection Dim MaConnexionTmp As OleDbDataAdapter Dim UneTable As New DataTable Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" MaConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;Data Source=" &

NomDB & ";" MaConnexionTmp = New OleDbDataAdapter("SELECT * FROM TPers", MaConnexion) ' La méthode FillSchema ne récupère pas les données MaConnexionTmp.FillSchema(UneTable, SchemaType.Source) ' Affichage du nom de la table Console.WriteLine(UneTable.TableName) ' Affichage des noms des colonnes For Each C As DataColumn In UneTable.Columns Console.Write(C.ColumnName & " ") Next Console.WriteLine() ' Affichage des types de données For Each C As DataColumn In UneTable.Columns Console.Write(C.DataType.ToString & " ") Next Console.WriteLine() ' Récupération des données MaConnexionTmp.Fill(UneTable) ' Balayage des lignes de la table et affichage du contenu de chaque colonne For Each R As DataRow In UneTable.Rows For Each C As DataColumn In UneTable.Columns Console.Write(R.Item(C.ColumnName) & " ") Next Console.WriteLine() Next

L'exemple suivant illustre la constitution d'un DataSet par la lecture de deux tables d'une base existante et l'affichage de toutes les informations ainsi regroupées. C'est l'occasion d'améliorer la procédure AfficheDonnees écrite précédemment.

Private Sub AfficheDonnees(ByVal DTS As DataSet) For Each T As DataTable In DTS.Tables Console.WriteLine(T.TableName) ' Affichage du nom de la table For Each C As DataColumn In T.Columns Console.Write(C.DataType.ToString & " ") ' Affichage des types de champs Next

Page 269: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 19

Console.WriteLine() For Each C As DataColumn In T.Columns Console.Write(C.ColumnName & " ") ' Affichage des noms de champs Next For Each R As DataRow In T.Rows If R.RowState <> DataRowState.Deleted then ' La tentative d'accès à une Console.WriteLine() ' ligne supprimée provoque une

For Each C As DataColumn In T.Columns ' erreur Console.Write(R.Item(C.ColumnName) & " ") ' Affichage du contenu Next End If Next Console.WriteLine() Next End Sub

' … Dim MaConnexion As New OleDbConnection Dim MaConnexionTmp As OleDbDataAdapter Dim MesDonnees As New DataSet Dim UneTable As DataTable Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" MaConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;Data Source="

& NomDB & ";" ' Récupération de la table TPers MaConnexionTmp = New OleDbDataAdapter("SELECT * FROM TPers", MaConnexion) UneTable = New DataTable MaConnexionTmp.FillSchema(UneTable, SchemaType.Source) MaConnexionTmp.Fill(UneTable) MesDonnees.Tables.Add(UneTable) ' Récupération de la table TLoc MaConnexionTmp = New OleDbDataAdapter("SELECT * FROM TLoc", MaConnexion) UneTable = New DataTable MaConnexionTmp.FillSchema(UneTable, SchemaType.Source) MaConnexionTmp.Fill(UneTable) MesDonnees.Tables.Add(UneTable) ' Balayage de toutes les tables du DataSet AfficheDonnees(MesDonnees)

Création d'une base de données en mémoire Le mode déconnecté peut effectivement être utile au traitement en mémoire de lots d'informations provenant d'une base de données réelle. La création d'une base de données en mémoire, autrement que par la lecture d'une base réelle, est une technique d'usage exceptionnel qu'il n'est pas opportun d'approfondir dans ce cours. C'est pourquoi elle n'est présentée ci après que sous la forme d'un exemple. Pour cet exemple, la base précédemment utilisée va être recréée et améliorée. La table des personnes, nommée TPers, est constituée des champs IdPers, NomPers et XIdLoc, ce dernier étant une copie de la clé de l'identifiant de la localité de la personne. La table des localités, nommée TLoc, est constituée des champs IdLoc et Localite. Une relation Domicile lie les tables TPers et TLoc et une localité ne peut être supprimée de la base tant qu'une personne y habite. La base de données est contenue dans un DataSet nommé MesDonnees. Aucun Imports autre que System.Data n'est nécessaire tant que la programmation ne concerne que la base en mémoire. La procédure AfficheDonnees déjà écrite précédemment est utilisée chaque fois qu'un affichage de tout le DataSet est souhaité.

Page 270: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 20

Afin de permettre des expérimentations successives sans répétition d'une fastidieuse phase d'encodage, une procédure EncodeJeuDeTest est écrite ci après. Les enregistrements y sont définis dans des tableaux en mémoire et sont transférés dans les tables de la base données lors de l'exécution de la procédure.

Private Sub EncodeJeuDeTest(ByRef DTS As DataSet) ' Uniquement pour tests Dim i As Integer Dim TLocalePers(4) As String ' Vecteur de chaînes pour 5 personnes Dim TLocaleLoc(4) As String ' Vecteur de chaînes pour 5 localités Dim TLocaleDom(4, 1) As Integer ' Matrice des relations Personnes - Localités ' Stockage des Personnes dans le vecteur TLocalePers(0) = "Azerty" TLocalePers(1) = "Qwerty" TLocalePers(2) = "Panzani" TLocalePers(3) = "Ranzani" TLocalePers(4) = "Barety" ' Stockage des Localités dans le vecteur TLocaleLoc(0) = "Loc 1" TLocaleLoc(1) = "Loc 2" TLocaleLoc(2) = "Loc 3" TLocaleLoc(3) = "Loc 4" TLocaleLoc(4) = "Loc 5" ' Associations Personnes - Localité TLocaleDom(0, 0) = 0 ' Azerty TLocaleDom(0, 1) = 3 ' Loc 4 TLocaleDom(1, 0) = 1 ' Qwerty TLocaleDom(1, 1) = 0 ' Loc 1 TLocaleDom(2, 0) = 2 ' Panzani TLocaleDom(2, 1) = 1 ' Loc 2 TLocaleDom(3, 0) = 3 ' Ranzani TLocaleDom(3, 1) = 0 ' Loc 1 TLocaleDom(4, 0) = 4 ' Barety TLocaleDom(4, 1) = 3 ' Loc 4 ' Insertion des localités dans la base For i = 0 To 4 DTS.Tables("TLoc").Rows.Add(i) DTS.Tables("TLoc").Rows(i).Item("Localite") = TLocaleLoc(i) Next ' Insertion des personnes dans la base For i = 0 To 4 DTS.Tables("TPers").Rows.Add(i) DTS.Tables("TPers").Rows(i).Item("NomPers") = TLocalePers(TLocaleDom(i, 0)) DTS.Tables("TPers").Rows(i).Item("XIdLoc") = TLocaleDom(i, 1) Next End Sub

' … ' Création de la base de données en mémoire ' Instanciation d'un DataSet nommé MesDonnees (c'est aussi le nom sa référence) Dim MesDonnees As New DataSet("MesDonnees")

' Création d'un DataTable nommé TPers (c'est aussi le nom sa référence) Dim TPers As New DataTable("TPers")

' Réglage du DataTable TPers.CaseSensitive = False TPers.MinimumCapacity = 10

' Création d'un DataColumn nommé IdPers Dim IdPers As New DataColumn("IdPers", GetType(Integer)) ' Réglage de la colonne IdPers IdPers.AutoIncrement = True

' Ajout de la colonne IdPers à la table TPers TPers.Columns.Add(IdPers) TPers.PrimaryKey = New DataColumn() {IdPers}

GetType fournit un objet Type pour le type spécifié. Cet objet contient des informations sur le type telles que ses propriétés, méthodes et événements.

Page 271: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 21

' Création d'un DataColumn nommé NomPers Dim NomPers As New DataColumn("NomPers", GetType(String)) ' Réglage de la colonne NomPers NomPers.Unique = True NomPers.MaxLength = 50

' Ajout de la colonne NomPers à la table TPers TPers.Columns.Add(NomPers)

' Création d'un DataColumn nommé XIdLoc Dim XIdLoc As New DataColumn("XIdLoc", GetType(Integer)) ' Réglage de la colonne XIdLoc XIdLoc.AutoIncrement = False XIdLoc.Unique = False XIdLoc.DefaultValue = 0

' Ajout de la colonne XIdloc à la table TPers TPers.Columns.Add(XIdLoc)

' Ajout de la table TPers à la base MesDonnees MesDonnees.Tables.Add(TPers) ' Création d'un DataTable nommé TLoc (c'est aussi le nom sa référence) Dim TLoc As New DataTable("TLoc")

' Réglage du DataTable TLoc.CaseSensitive = False TLoc.MinimumCapacity = 10

' Création d'un DataColumn nommé IdLoc Dim IdLoc As New DataColumn("IdLoc", GetType(Integer)) ' Réglage de la colonne IdLoc IdLoc.AutoIncrement = True

' Ajout de la colonne IdLoc à la table TLoc TLoc.Columns.Add(IdLoc) TLoc.PrimaryKey = New DataColumn() {IdLoc}

' Création d'un DataColumn nommé Localite Dim Localite As New DataColumn("Localite", GetType(String)) ' Réglage de la colonne Localite Localite.Unique = True Localite.MaxLength = 40

' Ajout de la colonne Localite à la table TLoc TLoc.Columns.Add(Localite)

' Ajout de la table TLoc à la base MesDonnees MesDonnees.Tables.Add(TLoc) ' Création d'une relation Domicile entre TPers et TLoc Dim Domicile As New DataRelation("Domicile", _ MesDonnees.Tables("TLoc").Columns("IdLoc"), _ MesDonnees.Tables("TPers").Columns("XIdLoc")) ' Ajout de la relation Domicile à la base MesDonnees MesDonnees.Relations.Add(Domicile) ' Ajout de la contrainte d'intégrité : ne pas supprimer la ligne, ni modifier la clé Dim Contrainte As ForeignKeyConstraint = Domicile.ChildKeyConstraint Contrainte.DeleteRule = Rule.None ' Aucune action sur les lignes liées Contrainte.UpdateRule = Rule.None ' Autres valeurs : ' Cascade : répercute l’action en cascade EncodeJeuDeTest(MesDonnees) ' SetNull : place DBNull dans les lignes liées AfficheDonnees(MesDonnees) ' SetDefault : place la valeur stipulée par la ' propriété DefaultValue du DataColumn

Page 272: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 22

Les deux dernières tâches réalisées ci-dessus, l'établissement des relations et la définition des contraintes, sont utiles à programmer pour établir au niveau d'un DataSet une sécurité équivalente à celle qui existe dans une base de données réelle. Sauvegarde de la base mémoire dans une base Access Les opérations nécessaires à la sauvegarde de la base mémoire dans une base de données réelle relèvent essentiellement des possibilités offertes par le mode connecté. Il faut d'abord procéder à la création des tables, de leurs relations et contraintes, ensuite il faut y insérer toutes les données obtenues par le balayage de toutes les lignes de chaque table. Dans l'exemple suivant, ces opérations sont réalisées dans une procédure CreeDBAccessEtStockeDataSet qui reçoit par paramètres, le nom complet de la base Access à créer et la référence du DataSet à enregistrer.

' … à la suite de l'exemple précédent … Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" CreeDBAccessEtStockeDataSet(NomDB, MesDonnees) Private Function CreationDBAccess(ByVal NomComplet As String) As Boolean ' Code de cette fonction déjà écrit précédemment End Function Private Sub CreeDBAccessEtStockeDataSet(ByVal NomComplet As String,

ByVal DTS As DataSet) Dim MaConnexion As New OleDbConnection Dim MaCommande As New OleDbCommand CreationDBAccess(NomComplet) MaConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;Data Source="

& NomComplet & ";" MaConnexion.Open() MaCommande.CommandType = CommandType.Text MaCommande.Connection = MaConnexion MaCommande.CommandText =

"CREATE TABLE TPers (IdPers Int, NomPers Text (40), XIdLoc Int )" MaCommande.ExecuteNonQuery() MaCommande.CommandText =

"CREATE TABLE TLoc (IdLoc Int, Localite Text (35))" MaCommande.ExecuteNonQuery() MaCommande.CommandText =

"ALTER TABLE TPers ADD PRIMARY KEY (IdPers)" MaCommande.ExecuteNonQuery() MaCommande.CommandText =

"ALTER TABLE TLoc ADD PRIMARY KEY (IdLoc)" MaCommande.ExecuteNonQuery() MaCommande.CommandText =

"ALTER TABLE TPers ADD FOREIGN KEY (XIdLoc) REFERENCES TLoc (IdLoc)" MaCommande.ExecuteNonQuery() For Each R As DataRow In DTS.Tables("TLoc").Rows If R.RowState <> DataRowState.Deleted then ' La tentative d'accès à une ' ligne supprimée provoque une

' erreur MaCommande.CommandText = "INSERT INTO TLoc (IdLoc, Localite)

VALUES (" & R.Item("IdLoc").ToString & ", '" & R.Item("Localite").ToString & "')"

MaCommande.ExecuteNonQuery() End If Next For Each R As DataRow In DTS.Tables("TPers").Rows If R.RowState <> DataRowState.Deleted then MaCommande.CommandText = "INSERT INTO TPers (IdPers, NomPers, XIdLoc)

VALUES (" & R.Item("IdPers").ToString & ", '" & R.Item("NomPers").ToString & "', "

& R.Item("XIdLoc").ToString & ")" MaCommande.ExecuteNonQuery() End If Next MaConnexion.Close() End Sub

Page 273: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 23

Utilisation des relations et contraintes d'intégrité Pour illustrer l'effet des relations et des contraintes d'intégrités, un DataSet est créé par lecture de la base de données issue de l'exemple précédent, par l'utilisation du code déjà étudié et repris ici pour mémoire.

Dim MaConnexion As New OleDbConnection Dim MaConnexionTmp As OleDbDataAdapter Dim MesDonnees As New DataSet Dim UneTable As DataTable Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" MaConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;Data Source=" &

NomDB & ";" ' Récupération de la table TLoc (TLoc avant TPers car table "parent") MaConnexionTmp = New OleDbDataAdapter("SELECT * FROM TLoc", MaConnexion) UneTable = New DataTable MaConnexionTmp.FillSchema(UneTable, SchemaType.Source) MaConnexionTmp.Fill(UneTable) MesDonnees.Tables.Add(UneTable) ' Récupération de la table TPers (TPers après TLoc car table "enfant") MaConnexionTmp = New OleDbDataAdapter("SELECT * FROM TPers", MaConnexion) UneTable = New DataTable MaConnexionTmp.FillSchema(UneTable, SchemaType.Source) MaConnexionTmp.Fill(UneTable) MesDonnees.Tables.Add(UneTable)

Lectures des données Lecture linéaire La lecture linéaire est celle qui consiste à lire les données d'une table à la fois. C'est la lecture réalisée par la procédure AffichesDonnees écrite précédemment. Cette procédure parcourt tout le DataSet qui lui est passé par paramètre, affiche des informations sur les tables et leurs champs et pour chacune, affiche tout son contenu. Lectures hiérarchiques La lecture hiérarchique est celle qui consiste à lire les données en fonction des relations qui existent entre les tables. Alors qu'une lecture linéaire de la table TPers de l'exemple précédent donne l'identifiant de chaque personne, son nom et l'identifiant de sa localité, une lecture hiérarchique de cette même table en fonction de la relation qui lie TPers à TLoc donne également l'identifiant et le nom de chaque personne, mais pour la localité, c'est son libellé qui est fourni. Il y a deux sortes de lectures hiérarchiques : la lecture de la relation "parent" et la lecture de la relation "enfant". La lecture de la relation "parent" est celle qui consiste à rechercher dans une autre table le parent d'un enregistrement de la table lue, c'est-à-dire rechercher l'enregistrement désigné par la clé étrangère. Lors de la lecture de la table TPers, la lecture de la relation "parent" consiste à lire TPers et pour chacun de ses XIdLoc, rechercher le IdLoc correspondant dans TLoc. Elle consiste à dire où habite la personne. Il s'agit de la lecture ordinaire d'une relation. Inversement, la lecture de la relation "enfant" est celle qui consiste à rechercher dans une autre table tous les enfants d'un enregistrement de la table lue, c'est-à-dire rechercher tous les enregistrements dont la clé étrangère est une copie de l'identifiant de l'enregistrement en cours. Dans le cas de la relation entre TPers et TLoc, la lecture de la relation "enfant" consiste à lire TLoc et pour chacun de ses IdLoc, rechercher tous les XIdLoc correspondant dans TPers. Elle consiste à énumérer tous les habitants d'une localité. La récupération des données par le code ci-dessus ne restitue pas les relations existant dans la base de données. C'est une lacune du fournisseur d'accès et cela peut être différent avec d'autres, comme SQL Server par exemple. Il faut donc récréer les relations nécessaires compte tenu des tables récupérées et du travail à réaliser. Il n'y qu'une seule relation à établir dans le cadre de cet exemple et le code nécessaire est identique à celui utilisé lors de la création de la base en mémoire. Il suffit de le placer à la suite du code précédent.

Dim Domicile As New DataRelation("Domicile", _ MesDonnees.Tables("TLoc").Columns("IdLoc"), _ MesDonnees.Tables("TPers").Columns("XIdLoc")) MesDonnees.Relations.Add(Domicile)

Page 274: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 24

Affichage des données par lecture de la relation "parent".

For Each R As DataRow In MesDonnees.Tables("TPers").Rows If R.RowState <> DataRowState.Deleted then Console.WriteLine(R.Item("IdPers") & " " & _ R.Item("NomPers") & " " & _ R.GetParentRow("Domicile").Item("Localite")) End If Next

Affichage des données par lecture de la relation "enfant".

For Each R As DataRow In MesDonnees.Tables("TLoc").Rows If R.RowState <> DataRowState.Deleted then Console.WriteLine(R.Item("Localite")) For Each Habitant As DataRow In R.GetChildRows("Domicile") Console.WriteLine(" " & Habitant.Item("NomPers")) Next End If Next

Les contraintes d'intégrités De même que les relations, les contraintes d'intégrités ne sont pas restituées (faute au fournisseur d'accès) par le code de récupération de la base de données et il faut également les redéfinir au niveau du DataSet. Ici aussi le code à ajouter est identique à celui utilisé lors de la création de la base en mémoire et il suffit de le placer à la suite du code précédent.

Dim Contrainte As ForeignKeyConstraint = Domicile.ChildKeyConstraint Contrainte.DeleteRule = Rule.None Contrainte.UpdateRule = Rule.None

Quelques cas La suppression de l'enregistrement de la localité Loc 3 est permise car cette localité n'est pas habitée. Mais une tentative de suppression de la localité Loc 2 générerait une exception car cela produirait une ligne orpheline dans la table TPers.

For Each R As DataRow In MesDonnees.Tables("TLoc").Rows If R.RowState <> DataRowState.Deleted then If R.Item("Localite") = "Loc 3" Then R.Delete() Exit For End If End If Next AfficheDonnees(MesDonnees)

La modification du libellé d'une localité est toujours permise car la contrainte ne concerne que les clés. Par contre, la modification de l'identifiant d'une localité habitée produirait aussi des lignes orphelines dans la table TPers.

For Each R As DataRow In MesDonnees.Tables("TLoc").Rows If R.RowState <> DataRowState.Deleted then If R.Item("Localite") = "Loc 4" Then R.Item("Localite") = "Loc 4 bis" Exit For End If End If Next AfficheDonnees(MesDonnees)

A propos des modifications de données, la programmation suivante est meilleure. L'usage de la méthode BeginEdit permet également l'usage de EndEdit et de CancelEdit et donc, de pouvoir confirmer ou annuler une modification.

Affichages : 0 Azerty Loc 4 1 Qwerty Loc 1 2 Panzani Loc 2 3 Ranzani Loc 1 4 Barety Loc 4

Affichages : Loc 1 Qwerty Ranzani Loc 2 Panzani Loc 3 Loc 4 Azerty Barety Loc 5

Page 275: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 25

For Each R As DataRow In MesDonnees.Tables("TLoc").Rows If R.RowState <> DataRowState.Deleted then If R.Item("Localite") = "Loc 4" Then R.BeginEdit() R.Item("Localite") = "Loc 4 bis"

Console.Write("Confirmation de la modification (O/N) ? ") If Console.ReadLine().ToUpper = "O" Then R.EndEdit() Else R.CancelEdit() End If End If End If Next AfficheDonnees(MesDonnees)

L'ajout d'une personne impose que le champ XIdLoc reçoive une valeur et il faut que cette valeur existe. La contrainte sur la clé étrangère de TPers impose que l'identifiant correspondant existe dans TLoc.

With MesDonnees.Tables("TPers") .Rows.Add(MesDonnees.Tables("TPers").Rows.Count) Console.Write("Nom de la personne à ajouter ? ") .Rows(MesDonnees.Tables("TPers").Rows.Count - 1).Item("NomPers") =

Console.ReadLine() Console.Write("Localité de la personne (IdLoc) ? ") .Rows(MesDonnees.Tables("TPers").Rows.Count - 1).Item("XIdLoc") =

CType(Console.ReadLine(), Integer) End With AfficheDonnees(MesDonnees)

Mise à jour de la base source et gestion des conflits La mise à jour de la base de données source se réalise par la méthode UpDate de l'objet DataAdapter. La méthode UpDate exécute une commande SQL adaptée à la mise à jour envisagée et référencée selon le cas, dans une des propriétés InsertCommand, DeleteCommand ou UpdateCommand du DataAdapter. La commande SQL contient des inconnues, représentées par des points d'interrogations, qui sont remplacées en cours d'exécution par les valeurs utiles de chaque enregistrement à traiter grâce à une collection de paramètres préalablement définis. Cette commande traite tout un lot de lignes du DataSet en une fois et retourne le nombre d'enregistrements réellement affectés dans la base de données. Cette valeur de retour est sans conteste le plus simple moyen de savoir si des opérations ont été refusées par le système de gestion de la base de données. Cela se produit quand une opération tente d'agir sur la base en infractions de ses règles d'intégrité, lorsqu'il y a conflit. Bien que toutes les contraintes d'intégrités soient respectées dans la base de données réelle et que chaque application qui traite en DataSet des extraits de cette base redéfinisse les contraintes nécessaires à la bonne gestion de ses données, les conflits peuvent survenir à la mise à jour et ils ont toujours la même origine. Ils sont dus à l'utilisation de la base de données source par d'autres applications entre le moment où se termine le chargement de la base en mémoire et le moment où se fait la mise à jour. Ce partage de la base de données entre plusieurs applications en mode déconnecté n'implique pas obligatoirement la survenance de conflits lors de la mise à jour, mais réunit toutes les conditions qui leur sont favorables. Un conflit se produit par exemple, lors d'une mise à jour pour modification d'une donnée qui a été supprimée depuis le chargement, ou lors de la tentative d'insertion d'une donnée "enfant" alors que la donnée "parent" nécessaire a été supprimée, ou encore lors de la tentative de suppression d'un enregistrement qui l'a déjà été par ailleurs. Toute base de données gérée par une application est susceptible d'être également manipulée dans le même temps par une autre, ne serait-ce que par une autre instance de la même application, ou encore par le système gestionnaire de la base (le logiciel Access pour une base de ce type, par exemple). La programmation de la mise à jour d'une base réelle à partir d'un DataSet doit donc impérativement intégrer des traitements appropriés ou tout au moins, recenser les enregistrements rejetés et en informer l'utilisateur. C'est pourquoi dans l'exemple suivant, qui peut être expérimenté à la suite de ce qui a déjà été fait, la gestion des enregistrements rejetés n'est pas dissociée de la mise à jour elle-même.

Page 276: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 26

If MesDonnees.GetChanges() Is Nothing Then ' Pas de mise à jour nécessaire s'il Console.WriteLine("Aucun changement") ' n'y a eu aucune modification des Else ' données initialement lues. MaConnexionTmp.ContinueUpdateOnError = True ' La propriété ContinueUpDateOnError

' du DataAdapter est fixée à True afin Dim MaCommande As OleDbCommand ' que la mise à jour ne soit pas Dim Param As OleDbParameter ' interrompue dès le premier rejet. Dim MesModifs As DataTable Dim LignesATraiter As Integer Dim LignesTraitees As Integer ' Mise à jour : Ajout des localités (TLoc avant TPers car table "parent") ' Constitution d'un DataTable contenant tous les enregistrements dont le RowState est ' "Ajouté". C'est la méthode GetChanges(DataRowState.Added) qui fournit ce DataTable. MesModifs = MesDonnees.Tables("TLoc").GetChanges(DataRowState.Added) ' Si le DataTable est vide, il n'y a donc pas d'enregistrement à ajouter. ' Dans le cas contraire, le nombre de lignes à traiter est mémorisé. If Not MesModifs Is Nothing Then LignesATraiter = MesModifs.Rows.Count ' Ecriture de la commande d'ajout avec des ? pour les valeurs inconnues. MaCommande = New OleDbCommand("INSERT INTO TLoc (IdLoc, Localite)

VALUES (?, ?)", MaConnexion)

' Un paramètre se définit par de nombreuses propriétés, mais la seule indispensable ici ' est le nom du champ qu'il représente. Une boucle For Each est utilisable quand l'ordre ' et nombre d'inconnues correspondent à l'ordre et au nombre de colonnes du DataTable. ' Chaque paramètre doit être ajouté à la collection Parameters de la commande. For Each C As DataColumn In MesModifs.Columns Param = New OleDbParameter Param.SourceColumn = C.ColumnName MaCommande.Parameters.Add(Param) Next ' Quand la commande est écrite et ses paramètres définis, il faut l'affecter à la ' propriété adéquate (InsertCommand dans ce cas) du DataAdapter. MaConnexionTmp.InsertCommand = MaCommande ' Il n'y plus qu'à ordonner la mise à jour et mémoriser le nombre de lignes traitées. LignesTraitees = MaConnexionTmp.Update(MesModifs) ' Traitement des rejets (Affichage) ' S'il y a moins de lignes traitées qu'il y en avait à traiter, c'est qu'il y a eu des ' rejets. Il faut parcourir le DataSet pour les retrouver et les traiter. If LignesTraitees < LignesATraiter Then For Each R As DataRow In MesModifs.Rows ' Toute ligne correctement mise à jour reçoit la valeur Unchanged pour sa propriété ' RowState. If R.RowState <> DataRowState.Unchanged Then Console.WriteLine(R.Item(0) & " " & R.Item(1) & " non ajouté") End If Next End If MesModifs.Reset() End If

Page 277: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 27

' La même séquence d'opérations est à répéter pour chaque table et pour chaque type de ' mise à jour. ' Mise à jour : Ajout des personnes (TPers après TLoc car table "enfant") MesModifs = MesDonnees.Tables("TPers").GetChanges(DataRowState.Added) If Not MesModifs Is Nothing Then LignesATraiter = MesModifs.Rows.Count MaCommande = New OleDbCommand("INSERT INTO TPers (IdPers, NomPers, XIdLoc)

VALUES (?, ?, ?)", MaConnexion) For Each C As DataColumn In MesModifs.Columns Param = New OleDbParameter Param.SourceColumn = C.ColumnName MaCommande.Parameters.Add(Param) Next MaConnexionTmp.InsertCommand = MaCommande LignesTraitees = MaConnexionTmp.Update(MesModifs) ' Traitement des rejets (Affichage) If LignesTraitees < LignesATraiter Then For Each R As DataRow In MesModifs.Rows If R.RowState <> DataRowState.Unchanged Then Console.WriteLine(R.Item(0) & " " & R.Item(1) & " " & R.Item(2) &

" non ajouté") End If Next End If MesModifs.Reset() End If ' Mise à jour : Suppression des localités MesModifs = MesDonnees.Tables("TLoc").GetChanges(DataRowState.Deleted) If Not MesModifs Is Nothing Then LignesATraiter = MesModifs.Rows.Count MaCommande = New OleDbCommand("DELETE FROM TLOC WHERE (IdLoc = ?)",

MaConnexion) Param = New OleDbParameter Param.SourceColumn = "IdLoc" MaCommande.Parameters.Add(Param) MaConnexionTmp.DeleteCommand = MaCommande LignesTraitees = MaConnexionTmp.Update(MesModifs) ' Traitement des rejets (Affichage) If LignesTraitees < LignesATraiter Then ' Dans le cas des suppressions refusées, il faut d'abord annuler l'opération pour ' pouvoir afficher les informations. For Each R As DataRow In MesModifs.Rows If R.RowState <> DataRowState.Unchanged Then R.RejectChanges() Console.WriteLine(R.Item(0) & " " & R.Item(1) & " non supprimé") End If Next End If MesModifs.Reset() End If ' Mise à jour : Suppression des personnes MesModifs = MesDonnees.Tables("TPers").GetChanges(DataRowState.Deleted) If Not MesModifs Is Nothing Then LignesATraiter = MesModifs.Rows.Count MaCommande = New OleDbCommand("DELETE FROM TPers WHERE (IdPers = ?)",

MaConnexion)

Page 278: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 28

Param = New OleDbParameter Param.SourceColumn = "IdPers" MaCommande.Parameters.Add(Param) MaConnexionTmp.DeleteCommand = MaCommande LignesTraitees = MaConnexionTmp.Update(MesModifs) ' Traitement des rejets (Affichage) If LignesTraitees < LignesATraiter Then For Each R As DataRow In MesModifs.Rows If R.RowState <> DataRowState.Unchanged Then R.RejectChanges() Console.WriteLine(R.Item(0) & " " & R.Item(1) & " " & R.Item(2) &

" non supprimé") End If Next End If MesModifs.Reset() End If ' Mise à jour : Modification des localités MesModifs = MesDonnees.Tables("TLoc").GetChanges(DataRowState.Modified) If Not MesModifs Is Nothing Then LignesATraiter = MesModifs.Rows.Count MaCommande = New OleDbCommand("UPDATE TLOC SET Localite = ? WHERE

(IdLoc = ?)", MaConnexion) Param = New OleDbParameter Param.SourceColumn = "Localite" MaCommande.Parameters.Add(Param) Param = New OleDbParameter Param.SourceColumn = "IdLoc" MaCommande.Parameters.Add(Param) MaConnexionTmp.UpdateCommand = MaCommande LignesTraitees = MaConnexionTmp.Update(MesModifs) ' Traitement des rejets (Affichage) If LignesTraitees < LignesATraiter Then For Each R As DataRow In MesModifs.Rows If R.RowState <> DataRowState.Unchanged Then Console.WriteLine(R.Item(0) & " " & R.Item(1) & " non modifié") End If Next End If MesModifs.Reset() End If ' Mise à jour : Modification des personnes MesModifs = MesDonnees.Tables("TPers").GetChanges(DataRowState.Modified) If Not MesModifs Is Nothing Then LignesATraiter = MesModifs.Rows.Count MaCommande = New OleDbCommand("UPDATE TPers SET NomPers = ?, XIdLoc = ?

WHERE (IdPers = ?)", MaConnexion) Param = New OleDbParameter Param.SourceColumn = "NomPers" MaCommande.Parameters.Add(Param) Param = New OleDbParameter Param.SourceColumn = "XIdLoc" MaCommande.Parameters.Add(Param) Param = New OleDbParameter Param.SourceColumn = "IdPers" MaCommande.Parameters.Add(Param) MaConnexionTmp.UpdateCommand = MaCommande LignesTraitees = MaConnexionTmp.Update(MesModifs)

Page 279: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 29

' Traitement des rejets (Affichage) If LignesTraitees < LignesATraiter Then For Each R As DataRow In MesModifs.Rows If R.RowState <> DataRowState.Unchanged Then Console.WriteLine(R.Item(0) & " " & R.Item(1) & " " & R.Item(2) &

" non modifié") End If Next End If MesModifs.Reset() End If ' Le meilleur moyen de rétablir un DataSet cohérent avec la base de données ' après mise à jour est de le recharger MesDonnees.Reset() ' … rappeler le code de chargement du DataSet et redéfinir les relations et ' contraintes d'intégrité si nécessaire … End If ' …

Bases de données et composants visuels Toutes les expérimentations réalisées jusqu'à présent sur les bases de données l'ont été en mode Console, mais de nombreuses applications requièrent l'interface graphique de Windows. Il s'agit donc à présent de s'intéresser aux outils qu'offre la programmation visuelle.

La liaison à une base de données Lorsqu'une application est chargée et ses données initialisées, c'est le plus souvent pour permettre de longues consultations d'informations et les modifications de la base de données ne représentent qu'une petite part du temps de fonctionnement. C'est pourquoi, autant que possible, la recherche des informations et leur présentation à l'écran se feront idéalement en mode déconnecté, tandis que les actions sur base de données se feront en mode connecté et au fur et à mesure des modifications et ajouts demandés par l'utilisateur. Le mode déconnecté permet l'obtention d'un lot important d'informations en une seule connexion à la base et réduit ainsi le nombre d'accès et surtout, de connexions. Le mode connecté pour la mise à jour en temps réel évite les lots de rejets, les enregistrements erronés étant détectés immédiatement. Ce sont donc les outils de liaisons aux bases de données Connection, Command, DataReader et DataAdapter qui sont utilisés en programmation Windows comme en programmation Console.

La présentation des données Les composants visuels qui permettent l'affichage d'informations sont nombreux et la façon d'y placer les données a été étudiée précédemment. Dans les pages suivantes, l'essentiel de la présentation des données se réalise par l'usage des composants de base TextBox, ListBox et ComboBox, avec l'aide de certaines de leurs fonctionnalités ignorées jusqu'ici. Deux autres outils, le DataGridView et le CrystalReport sont abordés à la fin de cette partie du cours. Aucune nouvelle fonctionnalité n'est nécessaire pour remplir un ListBox à partir d'un DataReader : il suffit d'ajouter l'information ligne par ligne dans le ListBox au fur et à mesure qu'elle est obtenue du DataReader.

Dim MaConnexion As New OleDbConnection Dim MaCommande As New OleDbCommand Dim MesPersonnes As OleDbDataReader ' … MaCommande.CommandText = "SELECT * FROM TPers" MesPersonnes = MaCommande.ExecuteReader() Do While MesPersonnes.Read ' Balayage du DataReader MesPersonnes LBPersonnes.Items.Add(MesPersonnes.Item(1)) ' et insertion dans la ListeBox Loop

Page 280: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 30

L'usage d'un DataSet facilite l'affichage, surtout lorsque plusieurs composants visuels se partagent la charge de présenter diverses informations d'un même enregistrement, comme par exemple l'affichage du nom d'une personne dans une ComboBox, son prénom dans un TextBox et sa localité dans un autre. La facilité d'emploi du DataSet réside dans les possibilités qui sont offertes de lier les composants visuels aux DataSet. Certains composants tels que le ListBox et le ComboBox, possèdent des propriétés DataSource et DisplayMember adaptées à la liaison avec un DataSet. Les liaisons définies par ces propriétés sont dites complexes dans le sens où elles peuvent concerner plusieurs enregistrements. D'autres composants inadaptés à l'affichage de plusieurs enregistrements, comme le Label ou le TextBox, n'ont pas ces propriétés, mais disposent d'une propriété DataBindings qui est une collection de liaisons avec un DataSet. Les liaisons définies dans un DataBindings sont dites simples dans le sens où elles permettent l'affichage d'un seul enregistrement à la fois. Pour illustrer l'usage de ces propriétés, l'exemple précédent est repris dans une application Windows et quelques opérations sont reprogrammées dans les fonctions suivantes.

Private Function OuvreOleDBConnexion(ByVal NomComplet As String , Optional ByVal Ouvrir As Boolean = True) As OleDbConnection

Dim UneConnexion As New OleDbConnection UneConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;Data Source="

& NomComplet & ";" If Ouvrir Then UneConnexion.Open() ' Ouverture effective non nécessaire pour Return UneConnexion ' l'utilisation d'un DataAdapter End Function

Private Function CreeUneTable(ByRef UneConnexion As OleDbConnection,

ByVal Selection As String) As DataTable Dim UneTable As New DataTable Dim UneConnexionTmp As New OleDbDataAdapter(Selection, UneConnexion) UneConnexionTmp.FillSchema(UneTable, SchemaType.Source) UneConnexionTmp.Fill(UneTable) Return UneTable End Function

L'interface utilisateur ci contre présente un ListBox nommé LBPersonnes, un ComboBox nommé CBPersonnes, quatre TextBox nommés TIdPers, TNomPers, TXIdLoc et TLocalite. Un bouton BOK lance la recherche des données et leur affichage. L'illustration montre en outre que tous les composants visuels présentent la même information. La sélection d'une ligne dans le ListBox provoque aussi la sélection dans le ComboBox et vice versa. Les TextBox affichent également l'information sélectionnnée. Cette synchronisation des données dans les composants est due au fait qu'ils sont tous issus de la même table du DataSet. Imports System.Data.OleDb Public Class FBase Dim MaConnexion As OleDbConnection Dim MesDonnees As New DataSet Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" Dim BaseOuverte As Boolean = False Dim CR As Char = Char.ConvertFromUtf32(13) ' Carriage return, utilisé dans certains

' messages lors des mises à jour de la base ' … codes des fonctions et procédures nécessaires … Private Sub FBase_Load(ByVal sender As Object, ByVal e As System.EventArgs)

Handles Me.Load BOK_Click(Me, Nothing) ' Pour éviter, dans cet exemple, les erreurs BaseOuverte = True ' dues à l’absence de données. End Sub Private Sub BOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles BOK.Click If BaseOuverte Then Initialisation() ' Rompre les liaisons aux composants de ChargeDonnees() ' présentation des données et réinitialiser AfficheDonnees() ' le DataSet. End Sub

Page 281: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 31

Private Sub Initialisation() For Each C As Control In Me.Controls() ' Balayage de tous les contrôles pour trouver Try ' et rompre leurs liaisons dans un Try car C.DataBindings.Clear() ' tous, tels les boutons, n’en possèdent pas. Catch End Try Next MesDonnees.Reset() End Sub End Class Lecture et affichage d'une simple table Les procédures ChargeDonnees et AfficheDonnees suivantes sont adaptées à la lecture de tables indépendantes. Il en résulte que dans l'interface utilisateur présentée ci-dessus, la boîte de texte TLocalite présente le libellé de la première localité de la table, sans aucune possiblité d'affichage des autres et sans rapport avec les autres données affichées, notamment avec le contenu de la boîte de texte TXIdLoc.

Private Sub ChargeDonnees() ' Etablissement d'une connexion en vue de son utilisation avec un DataAdapter MaConnexion = OuvreOleDBConnexion(NomDB, False) ' Chargement de la table des personnes dans le DataSet MesDonnees.Tables.Add(CreeUneTable(MaConnexion, "SELECT * FROM TPers")) ' Chargement de la table des localités dans le DataSet MesDonnees.Tables.Add(CreeUneTable(MaConnexion, "SELECT * FROM TLoc")) End Sub Private Sub AfficheDonnees() ' Liaison du ListBox au DataSet par la propriété DataSource et désignation du champ à ' présenter par la propriété DisplayMember. LBPersonnes.DataSource = MesDonnees.Tables("TPers") LBPersonnes.DisplayMember = "NomPers" ' Liaison du ComboBox au DataSet par la propriété DataSource et désignation du champ à ' présenter par la propriété DisplayMember. CBPersonnes.DataSource = MesDonnees.Tables("TPers") CBPersonnes.DisplayMember = "NomPers" ' Le TextBox ne dispose pas des propriétés DataSource et DisplayMember, mais bien de la ' propriété DataBindings qui permet la liaison d’une propriété du composant à un champ. TIdPers.DataBindings.Add("Text", MesDonnees.Tables("TPers"), "IdPers") TNomPers.DataBindings.Add("Text", MesDonnees.Tables("TPers"), "NomPers") TXIdLoc.DataBindings.Add("Text", MesDonnees.Tables("TPers"), "XIdLoc") ' Le TextBox TLocalite n'est pas lié à la même table du DataSet TLocalite.DataBindings.Add("Text", MesDonnees.Tables("TLoc"), "Localite") End Sub

Lecture et affichage de tables liées par une relation Les procédures ChargeDonnees et AfficheDonnees suivantes sont adaptées à la lecture de tables liées. Il en résulte que dans l'interface utilisateur présentée ci-dessus, la boîte de texte TLocalite présente le libellé de la localité de la personne sélectionnée dans les listes et affichée dans les autres TextBox.

Private Sub ChargeDonnees() ' Version 1 ' Etablissement d'une connexion en vue de son utilisation avec un DataAdapter MaConnexion = OuvreOleDBConnexion(NomDB, False) ' Chargement de la table des personnes avec les localités nécessaires dans le DataSet. La ' requête de sélection appropriée à une telle lecture est une jointure. L'usage du ' générateur de requêtes est une aide précieuse pour la mise au point de telles requêtes. MesDonnees.Tables.Add(CreeUneTable(MaConnexion,

"SELECT IdPers, XIdLoc, NomPers, Localite FROM (TLoc INNER JOIN TPers ON TLoc.IdLoc = TPers.XIdLoc)"))

' Nommer la table ainsi créée pour une programmation plus lisible. MesDonnees.Tables(0).TableName = "PersEtLoc" End Sub

Page 282: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 32

Private Sub ChargeDonnees() ' Version 2 MaConnexion = OuvreOleDBConnexion(NomDB, False) MesDonnees.Tables.Add(CreeUneTable(MaConnexion,

"SELECT IdPers, XIdLoc, NomPers, Localite FROM (TLoc INNER JOIN TPers ON TLoc.IdLoc = TPers.XIdLoc)"))

MesDonnees.Tables.Add(CreeUneTable(MaConnexion, "SELECT * FROM TLoc")) MesDonnees.Tables(0).TableName = "PersEtLoc" ' Créer des relations et contraintes équivalentes à celle de la base pour interdire des ' modifications qui seraient rejetées en cas de mise à jour Dim Domicile As New DataRelation("Domicile", _ MesDonnees.Tables("TLoc").Columns("IdLoc"), _ MesDonnees.Tables("PersEtLoc").Columns("XIdLoc")) MesDonnees.Relations.Add(Domicile) Dim Contrainte As ForeignKeyConstraint = Domicile.ChildKeyConstraint Contrainte.DeleteRule = Rule.None Contrainte.UpdateRule = Rule.None

End Sub Les deux versions de la procédure ChargeDonnees reconstituent complètement la table des personnes en mémoire. Pour ce qui concerne les localités, la première version ne charge que les localités parentes d'enregistrements de personnes, tandis que la deuxième version charge toute la table. De plus, la deuxième version établit une contrainte équivalente à celle existant dans la base afin de réduire les risques de rejets lors des mises à jour. C'est cette dernière version qui est utilisée pour les exemples suivants.

Private Sub AfficheDonnees() ' Les Liaisons des composants au DataSet s'effectue de la même façon que dans la procédure ' AfficheDonnees précédente. Seul le nom de la table a changé. LBPersonnes.DataSource = MesDonnees.Tables("PersEtLoc") LBPersonnes.DisplayMember = "NomPers" CBPersonnes.DataSource = MesDonnees.Tables("PersEtLoc") CBPersonnes.DisplayMember = "NomPers" TIdPers.DataBindings.Add("Text", MesDonnees.Tables("PersEtLoc"), "IdPers") TNomPers.DataBindings.Add("Text", MesDonnees.Tables("PersEtLoc"), "NomPers") TXIdLoc.DataBindings.Add("Text", MesDonnees.Tables("PersEtLoc"), "XIdLoc") ' Cette fois, le TextBox TLocalite est lié à la même table du DataSet que les autres. TLocalite.DataBindings.Add("Text", MesDonnees.Tables("PersEtLoc"), "Localite") End Sub

Comme déjà annoncé, l'expérimentation de ces codes montre que la sélection d'une personne dans LBPersonnes ou dans CBPersonnes provoque bien la mise à jour correspondante de l'affichage dans les autres composants. Il faut remarquer au passage que la modification d'une donnée dans un des TextBox entraîne une modification permanente, du moins dans le DataSet en cours. Cette particularité est étudiée plus loin. En matière d'affichage, les codes précédents n'offrent pas la possiblité de balayer les divers enregistrements, l'un après l'autre, dans un sens ou dans l'autre. Cette fonctionnalité nécessaire s'obtient par l'usage d'un objet BindingContext qui possède des propriétés Position et Current qui vont palier à l'absence de curseur dans un objet DataSet. Pour illustrer l'emploi de cet objet, l'interface utilisateur reçoit quatre boutons supplémentaires pour permettre la désignation du déplacement souhaité. Une seule procédure événementielle appelée ici Deplacement_Click suffit à l'obtention de l'enregistrement souhaité selon le bouton cliqué parmi BPremier, BPrecedent, BSuivant et BDernier.

Private Sub Deplacement_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles BDernier.Click, BPrecedent.Click, BSuivant.Click, BPremier.Click ' Inutile de tenter un déplacement si le DataSet ne contient aucune table. If MesDonnees.Tables.Count > 0 Then ' Le BindingContext appartient à un conteneur, le formulaire dans ce cas. With Me.BindingContext(MesDonnees.Tables("PersEtLoc")) ' Modification de sa propriété Position en fonction du bouton.

Page 283: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 33

Select Case sender.Name Case "BPremier" .Position = 0 Case "BPrecedent" If .Position = 0 Then .Position = .Count - 1 ' Balayage circulaire Else .Position -= 1 End If Case "BSuivant" If .Position = .Count - 1 Then .Position = 0 ' Balayage circulaire Else .Position += 1 End If Case "BDernier" .Position = .Count - 1 End Select End With End If End Sub

La recherche d'un enregistrement La recherche sur base d'une valeur se réalise aisément par le balayage de la table appropriée du DataSet et l'évaluation du champ correspondant de chaque ligne. La mise à jour de l'affichage dans les composants visuels se programme, si l'information est trouvée, comme pour les déplacements, c'est-à-dire par l'affectation de la propriété Position du BindingContext. Pour l'expérimentation du code suivant, une boîte de texte TCherche et un bouton BCherche ont été ajoutés au formulaire.

Private Sub BCherche_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BCherche.Click

Dim i As Integer = 0 For Each R As DataRow In MesDonnees.Tables("PersEtLoc").Rows If R.RowState <> DataRowState.Deleted Then If R.Item("NomPers") = TCherche.Text Then Me.BindingContext(MesDonnees.Tables("PersEtLoc")).Position = i End If End If i += 1 Next End Sub

Une recherche par balayage comme celle-ci permet de déterminer la position de l’enregistrement trouvé et celle-ci peut être affectée à BindingContext.Position pour synchroniser tous les affichages. Mais toutes les recherches ne nécessitent pas des modifications d’affichages. Elles peuvent être destinées à l’extraction d’une ou plusieurs informations d’un DataSet à d’autres fins. La méthode Select d’un DataTable permet ce type de recherche et livre un jeu de DataRow. La chaîne passée à cette méthode est la même que celle qui constitue ordinairement la clause Where d’une requête SQL. Le code suivant produit l’affichage par MessageBox des noms des personnes habitant une localité donnée qui a été encodée dans le TextBox nommé TCherche.

Dim DT As DataRow() DT = MesDonnees.Tables("PersEtLoc").Select("Localite = '" & TCherche.Text & "'") For Each R As DataRow In DT MessageBox.Show(R.Item("NomPers")) Next

Le code suivant produit l’affichage par MessageBox de la localité de la première personne dont le nom est celui qui a été encodé dans le TextBox nommé TCherche.

Dim R As DataRow R = MesDonnees.Tables("PersEtLoc").Select("NomPers = '" & TCherche.Text & "'")(0) MessageBox.Show(R.Item("Localite"))

Page 284: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 34

La manipulation des données L'édition contrôlée d'un enregistrement La modification du contenu d'une boîte de texte liée à un DataSet modifie automatiquement la donnée correspondante dans l'enregistrement. Cette dangereuse facilité impose la mise en place de garde fous. Ceux repris dans les exemples suivants n'en constituent pas un inventaire exhaustif. Il appartient au programmeur d'inventorier les risques auxquels est exposée son application et de l'en protéger. Plusieurs dispositifs de protection des données sont déjà en place du fait des contraintes d'intégrités et de l'unicité des valeurs des identifiants. Toutefois, quand les données en mémoire ne sont qu'un extrait d'une base comportant beaucoup de tables, il est possible que certaines contraintes ne soient pas reconstituées et que de fâcheuses conséquences ne se fassent sentir qu'au moment de la mise à jour. De plus, il faut se prémunir aussi contre les modifications accidentelles de données qui n'occasionnent aucune infraction envers les règles de la base de données. Ainsi, dans la base de données utilisée pour les exemples, la modification du nom d'une personne ne pose aucun problème au niveau de la base, même si cette modification est erronée. Ce dernier type d'erreur peut être évité en interdisant la modification de la zone ou en requérant une confirmation de la modification. Les trois procédures suivantes protègent des modifications accidentelles. Une procédure TNomPers_Enter peut empêcher l'encodage dans la boîte de texte TNomPers en renvoyant le focus sur une zone autorisée. La modification du nom d'une personne est alors impossible. Cette procédure qui convient aux boîtes de textes qui présentent de l'information non modifiable, comme un identifiant par exemple, ne peut être programmée conjointement avec la suivante pour un même composant.

Private Sub TIdPers_Enter(ByVal sender As Object, ByVal e As System.EventArgs)

Handles TIdPers.Enter TCherche.Select() End Sub

La procédure TNomPers_Leave annule la modification effectuée et la recommence en cas d'obtention d'une réponse affirmative à une demande de confirmation. Cette procédure qui convient aux boîtes de textes susceptibles de modifications, ne peut être programmée conjointement avec la précédente pour un même composant.

Private Sub TNomPers_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles TNomPers.Leave

Dim Encodage As String Dim Donnee As String Encodage = TNomPers.Text ' Sauvegarde de l'encodage With MesDonnees.Tables("PersEtLoc").Rows(

Me.BindingContext(MesDonnees.Tables("PersEtLoc")).Position) .RejectChanges() ' Récupération de la donnée enregistrée Donnee = .Item("NomPers") ' Sauvegarde de la donnée enregistrée If Encodage <> Donnee Then ' N'agir que s'il y a une différence If MessageBox.Show("Remplacer " & Donnee & " par " & Encodage & " ? ",

"Mes Données", MessageBoxButtons.YesNo) = Windows.Forms.DialogResult.Yes Then .Item("NomPers") = Encodage ' Remplacer la donnée enregistrée

' A programmer ici l'appel de la modification effective de la personne pour l'Id .Item("IdPers") End If End If End With End Sub

Afin de permettre un contrôle sur le changement de la localité d'une personne, la boîte de texte TLocalite est remplacée par une liste déroulante CBLocalites. Ce ComboBox doit contenir toutes les localités, mais présenter celle correspondant à l'XIdLoc de la personne en cours. Dans la mesure où la boîte de texte TLocalite est supprimée, il faut supprimer ou isoler la ligne qui y fait référence dans la procédure AfficheDonnees et la remplacer, pour la synchronisation de l'affichage dans CBLocalites, par les trois lignes suivantes.

' TLocalite.DataBindings.Add("Text", MesDonnees.Tables("PersEtLoc"), "Localite") CBLocalites.DataSource = MesDonnees.Tables("TLoc") CBLocalites.DisplayMember = "Localite" CBLocalites.DataBindings.Add("Text", MesDonnees.Tables("PersEtLoc"), "Localite")

Page 285: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 35

Plusieurs dispositifs sont nécessaires pour contrôler les manipulations de la localité d'une personne par l’utilisateur. D’abord, une procédure TXIdLoc_Enter est écrite pour interdire la modification de XIdLoc. Pour favoriser la modification par le choix d'une localité dans la liste déroulante, le focus est renvoyé sur CBLocalites.

Private Sub TXIdLoc_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles TXIdLoc.Enter

CBLocalites.Select() End Sub

Ensuite, il faut contrôler les modifications éventuellement exercées au niveau de la liste déroulante CBLocalites. Comme pour le contrôle des modifications au niveau du nom d'une personne, c'est l'événement Leave qui donne la meilleure occasion d'intervention. La procédure CBLocalites_Leave est toutefois un peu plus complexe. En effet, les contraintes imposent qu'une localité existe avant d'être attribuée à une personne et l'encodage d'une nouvelle localité doit être détecté et géré. Pour chaque encodage de l'utilisateur, une recherche de la localité dans la liste est effectuée par la méthode FindStringExact. Si elle n'est pas trouvée, il est opportun d'interroger l'utilisateur sur ses intentions, pour savoir si son encodage est destiné à la modification du libellé la localité actuelle ou s'il a pour objet la création d'une nouvelle localité. Dans un cas comme dans l'autre, il faut faire appel aux codes d'édition ou ajout d'une localité, mais s'il ne s'agit ni d'une modification, ni d'une création, alors l'encodage est erroné et il faut l'ignorer. Par contre, si l'encodage désigne une localité présente dans la liste, il faut requérir la confirmation du déménagement de la personne vers cette localité.

Private Sub CBLocalites_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles CBLocalites.Leave

Dim Encodage As String Dim Donnee As String

Dim IdActuel As Integer ' Pour l'édition d'une localité Dim NouvelId As Integer ' Valeur de retour après édition

' Sauvegarde de l'encodage Encodage = CBLocalites.Text With MesDonnees.Tables("PersEtLoc").Rows(

Me.BindingContext(MesDonnees.Tables("PersEtLoc")).Position) ' Récupération de la donnée enregistrée et de son Id et sauvegarde. .RejectChanges() Donnee = .Item("Localite") IdActuel = .Item("XIdLoc") NouvelId = IdActuel ' Pour le cas où il n’y a pas d’édition ' Annulation de l'encodage dans CBLocalite. CBLocalites.Text = Donnee ' Rien à faire si pas de différence If Encodage <> Donnee Then ' Recherche de la localité encodée dans la liste de CBLocalite If CBLocalites.FindStringExact(Encodage, 0) < 0 Then

' A programmer ultérieurement ici, l'appel de l'édition d'une localité et le code associé. Else ' Récupérer l’Id de la localité trouvée NouvelId = MesDonnees.Tables("TLoc").Select("Localite = '" &

Encodage & "'")(0).Item("IdLoc") End If ' La localité a été trouvée ou a été créée. Demande de confirmation pour le déménagement ' de la personne. If MessageBox.Show("Déménager " & .Item("NomPers") & " de " &

Donnee & " à " & Encodage & " ? ", "Mes Données", MessageBoxButtons.YesNo) = Windows.Forms.DialogResult.Yes Then

' Si réponse affirmative, remplacer la localité dans la table PersEtLoc ainsi que XIdloc. .Item("Localite") = Encodage .Item("XIdLoc") = NouvelId

' A programmer ici l'appel de la modification effective de la personne pour l'Id .Item("IdPers"). End If End If End With End Sub

Page 286: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 36

L'ajout d'un enregistrement L'ajout d'enregistrements est illustré par le traitement de l'édition ou ajout d'une localité. Dans le code précédent, la ligne en commentaire ' A programmer ultérieurement … est remplacée par les lignes de codes présentées ci après. Ces lignes de codes instancient un formulaire FEditLoc approprié à la modification ou l'ajout d'une seule localité, et traitent la réponse renvoyée par la propriété Resultat. Celle-ci contient l'identifiant de la localité modifiée ou ajoutée, ou -1 en cas d'annulation.

' Instanciation de FEditLoc et récupération de sa propriété Resultat dans NouvelId NouvelId = New FEditLoc(MesDonnees.Tables("TLoc"),

IdActuel, Encodage, Donnee).Resultat() ' NouvelId vaut -1 si l'opération est annulée par l'utilisateur If NouvelId < 0 Then Exit Sub Else ' Une localité est ajoutée ou un libellé est modifié, donc mise à jour de la liste. CBLocalites.Update() ' L'encodage définitif est celui encodé dans FEditLoc. Il faut le récupérer par lecture de ' l'enregistrement de TLoc désigné par NouvelId.

Dim R As DataRow R = MesDonnees.Tables("TLoc").Select("IdLoc = '" & NouvelId & "'")(0) If R IsNot Nothing Then Encodage = R.Item("Localite") End If

' Si NouvelId vaut IdActuel, c'est qu'il y a eu modification du libellé, sinon c'est qu'il ' y a eu ajout d'une nouvelle localité. If NouvelId = IdActuel Then ' S'il s'agit d'une modification, il faut mettre à jour les enregistrements de PersEtLoc ' qui contiennent l'ancien libellé de la localité. For Each R In MesDonnees.Tables("PersEtLoc").Rows If R.RowState <> DataRowState.Deleted Then If R.Item("XIdloc") = IdActuel Then R.Item("Localite") = Encodage End If End If Next

' A programmer ici l'appel de la modification effective de la localité pour l'Id NouvelId ' S'il s'agit d'une modification de libellé, il n'y a pas lieu de proposer le déménagement ' de la personne. ' Si c'est un ajout de localité, la suite de CBLocalites_Leave peut être exécutée. Exit Sub Else

' A programmer ici l'appel de l'ajout effectif de la localité pour l'Id NouvelId End If End If

Voici le code du formulaire FEditLoc, lequel est représenté ci contre. Public Class FEditLoc Dim ValeurDeRetour As Integer Dim DTTLoc As DataTable ' La propriété Resultat retourne ValeurDeRetour. Public Property Resultat() As Integer Get Return ValeurDeRetour End Get Set(ByVal Valeur As Integer) ValeurDeRetour = Valeur End Set End Property ' Initialisation des données locales par le constructeur. Public Sub New(ByRef DTT As DataTable, ByVal IdDonnee As Integer,

Optional ByVal ValeurEncodee As String = Nothing, Optional ByVal ValeurDonnee As String = Nothing)

Page 287: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 37

Me.InitializeComponent() TDonnee.Text = ValeurDonnee TEncodage.Text = ValeurEncodee DTTLoc = DTT ValeurDeRetour = IdDonnee TEncodage.TabIndex = 0 Me.ControlBox = False Me.CancelButton = BAnnule Me.AcceptButton = BAjout Me.TopMost = True Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedDialog Me.StartPosition = FormStartPosition.CenterParent Me.ShowDialog() End Sub ' Interdire toute modification de la donnée de l'enregistrement. Private Sub TDonnee_Enter(ByVal sender As Object,

ByVal e As System.EventArgs) Handles TDonnee.Enter TEncodage.Select() End Sub ' Retourner -1 en cas d'annulation. Private Sub BAnnule_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles BAnnule.Click Resultat = -1 Me.Close() End Sub ' En cas de modification, remplacer le libellé de la localité en cours par l'encodage. Private Sub BModification_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles BModification.Click DTTLoc.Select("IdLoc = " & ValeurDeRetour)(0).Item("Localite") = TEncodage.Text Me.Close() End Sub ' En cas d'ajout, calculer un nouvel identifiant et l'ajouter dans la table avec le libellé ' encodé. Private Sub BAjout_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles BAjout.Click ValeurDeRetour = CreeIdLoc() DTTLoc.Rows.Add(ValeurDeRetour, TEncodage.Text) Me.Close() End Sub ' Calculer le nouvel identifiant comme étant le plus grand de la table augmenté d'une unité. Private Function CreeIdLoc() As Integer Dim NouvelIdLoc = ValeurDeRetour For Each R As DataRow In DTTLoc.Rows If R.RowState <> DataRowState.Deleted Then If R.Item("IdLoc") > NouvelIdLoc Then NouvelIdLoc = R.Item("IdLoc") End If Next Return NouvelIdLoc + 1 End Function End Class La suppression d'un enregistrement Pour illustrer la suppression d'un enregistrement, un bouton BEfface est ajouté à l'interface utilisateur FBase. Ce formulaire réalisant la présentation des personnes, la procédure événementielle de BEfface a pour effet de supprimer l'enregistrement de la personne sélectionnée. Voici cette procédure.

Private Sub BEfface_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BEfface.Click

Dim R As Integer Dim IdP As Integer ' Sert à la mise à jour

Page 288: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 38

R = Me.BindingContext(MesDonnees.Tables("PersEtLoc")).Position IdP = MesDonnees.Tables("PersEtLoc").Rows(R).Item("IdPers") If MessageBox.Show("Supprimer " & TNomPers.Text & " ? ", "Mes Données",

MessageBoxButtons.YesNo) = Windows.Forms.DialogResult.Yes Then MesDonnees.Tables("PersEtLoc").Rows(R).Delete()

' A programmer ici l'appel de la suppression effective de la personne pour l'Id IdP End If End Sub

La mise à jour de la base Les exemples précédents illustrent la modification du nom d'une personne, le changement de localité de la personne, la modification du nom d'une localité, la création d'une localité et la suppression d'une personne. Toutes ces opérations sont réalisées sur un DataSet et aucune mise à jour de la base n'a été opérée. Une fonction CreeOleDBCommande et une procédure ReInitialisation sont ajoutées à l'application afin d'éviter la répétition de ces codes fréquemment appelés.

Private Function CreeOleDBCommande(ByRef UneConnexion As OleDbConnection, ByVal Commande As String) As OleDbCommand

Dim UneCommande As New OleDbCommand UneCommande = UneConnexion.CreateCommand UneCommande.CommandText = Commande Return UneCommande End Function

Private Sub ReInitialisation() BOK_Click(Me, Nothing) ' Réinitialiser, charger et afficher End Sub

Pour assurer la mise à jour effective de la base de données, les procédures qui acceptent des modifications, ajouts et suppressions d'enregistrements doivent faire appel aux procédures décrites ci après. Les endroits de ces appels ont été notés dans les exemples précédents par le commentaire ' A programmer ici l'appel de ….

Private Sub ModificationPersonne(ByVal IdP As Integer) ' L'instanciation de la commande est faite dans la fonction CreeOleDBCommande. Dim MaCommande As OleDbCommand = Nothing ' Ouverture effective d'une connexion (True) pour travail en mode connecté. MaConnexion = OuvreOleDBConnexion(NomDB, True) ' C'est PersEtLoc qui contient la donnée modifiée With MesDonnees.Tables("PersEtLoc").Rows(

Me.BindingContext(MesDonnees.Tables("PersEtLoc")).Position) ' Création de la commande UPDATE nécessaire. MaCommande = CreeOleDBCommande(MaConnexion, "UPDATE TPers SET

NomPers = '" & .Item("NomPers") & "', XIdLoc = " & .Item("XIdLoc") & " WHERE (IdPers = " & IdP.ToString & ")")

' Il peut y avoir rejet … Try ' La méthode ExecuteNonQuery retourne le nombre de lignes affectées et -1 en cas d'échec. ' Ici, il doit y avoir 1 ligne affectée. Si la clause WHERE de la commande n'est pas ' satisfaite, aucune erreur n'est générée par le SGBD. Dans ce cas, il faut générer ' l'erreur ici (Throw) afin d'avertir l'utilisateur. If MaCommande.ExecuteNonQuery() < 1 Then

Throw New Exception("Identifiant non trouvé") ' Dans cet exemple, le traitement d'une erreur se limite à la réinitialisation des données ' en mémoire et à l'information de l'utilisateur. Catch Ex As Exception ReInitialisation() MessageBox.Show(Ex.Message & CR & "Modification refusée" & CR &

"Tables en mémoire rechargées") Finally MesDonnees.AcceptChanges() End Try End With MaConnexion.Close() End Sub

Page 289: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 39

Private Sub ModificationLocalite(ByVal IdL As Integer) Dim MaCommande As OleDbCommand = Nothing MaConnexion = OuvreOleDBConnexion(NomDB, True) ' Le libellé de la localité modifiée se trouve aussi dans PersEtLoc et il pouvait être ' obtenu de la même façon que le nom de la personne dans ModificationPersonnes. C'est pour ' l'exemple qu'ici la recherche se fait sur la table TLoc. For Each R As DataRow In MesDonnees.Tables("TLoc").Rows If R.RowState <> DataRowState.Deleted Then If R.Item("IdLoc") = IdL Then MaCommande = CreeOleDBCommande(MaConnexion, "UPDATE TLoc SET

Localite = '" & R.Item("Localite") & "' WHERE (IdLoc = " & IdL.ToString & ")")

Exit For End If End If Next ' Il peut y avoir rejet … Try If MaCommande.ExecuteNonQuery() < 1 Then

Throw New Exception("Identifiant non trouvé") Catch Ex As Exception ReInitialisation() MessageBox.Show(Ex.Message & CR & "Modification refusée" & CR &

"Tables en mémoire rechargées") Finally MesDonnees.AcceptChanges() End Try MaConnexion.Close() End Sub Private Sub AjoutLocalite(ByVal IdL As Integer) Dim MaCommande As OleDbCommand = Nothing MaConnexion = OuvreOleDBConnexion(NomDB, True) For Each R As DataRow In MesDonnees.Tables("TLoc").Rows If R.RowState <> DataRowState.Deleted Then If R.Item("IdLoc") = IdL Then MaCommande = CreeOleDBCommande(MaConnexion, "INSERT INTO TLoc

(IdLoc, Localite) VALUES (" & IdL.ToString & ", '" & R.Item("Localite") & "' )")

Exit For End If End If Next ' Il peut y avoir rejet … Try ' Pas de clause WHERE ici. Le problème de l'identifiant non trouvé n'existe pas. MaCommande.ExecuteNonQuery() ' Mais il peut y avoir rejet pour d'autres raisons. Catch Ex As Exception ReInitialisation() MessageBox.Show(Ex.Message & CR & "Ajout refusé" & CR &

"Tables en mémoire rechargées") Finally MesDonnees.AcceptChanges() End Try MaConnexion.Close() End Sub Private Sub SuppressionPersonne(ByVal IdP As Integer) Dim MaCommande As OleDbCommand = Nothing MaConnexion = OuvreOleDBConnexion(NomDB, True) MaCommande = CreeOleDBCommande(MaConnexion, "DELETE FROM TPers" &

" WHERE (IdPers = " & IdP.ToString & ")") ' Il peut y avoir rejet … Try If MaCommande.ExecuteNonQuery() < 1 Then

Throw New Exception("Identifiant non trouvé")

Page 290: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 40

Catch Ex As Exception ReInitialisation() MessageBox.Show(Ex.Message & CR & "Suppression refusée" & CR &

"Tables en mémoire rechargées") Finally MesDonnees.AcceptChanges() End Try MaConnexion.Close() End Sub

Le composant DataGridView Le DataGridView est un autre composant destiné à la présentation et à l’édition des données. Il peut être créé ligne par ligne et colonne par colonne, un peu comme peut l’être un ListView, mais il s’avère particulièrement facile à construire par la simple affectation d’un DataSet à sa propriété DataSource.

DGVPersEtLoc.DataSource = MesDonnees.Tables("PersEtLoc") L’ajout de cette ligne de code à la procédure AfficheDonnees utilisée dans les exemples des pages précédentes permet le remplissage du DataGridView nommé DGVPersEtLoc comme illustré ci contre. Les lignes et les colonnes sont automatiquement créées et les entêtes de colonnes reçoivent les noms des champs de la table pour libellés. Le programmeur qui souhaite placer là des libellés plus convenables peut les encoder comme réalisé ci-dessous pour le DataGridView nommé DGVLocalites destiné à présenter le contenu de la table TLoc.

DGVLocalites.Columns(0).HeaderText = "Numéro" DGVLocalites.Columns(1).HeaderText = "Localité"

Le programmeur peut autoriser l’utilisateur à pratiquer diverses opérations sur la grille présentée et sur les enregistrements. Les plus sensibles de ces opérations sont la modification de l’ordre des colonnes et l’ajout ou la suppression d’enregistrements.

DGVLocalites.AllowUserToAddRows = True ' True par défaut DGVLocalites.AllowUserToDeleteRows = True ' True par défaut DGVLocalites.AllowUserToOrderColumns = True ' False par défaut

Les ajouts et suppressions d’enregistrements sont soumis au contrôle des contraintes d’intégrités et dans l’exemple illustré ci dessus, la tentative de suppression d’une localité habitée provoque la levée d’une exception. Par ailleurs, les modifications effectuées ne le sont effectivement que dans le DataSet. La mise à jour de la base de données incombe toujours au programmeur. L’autorisation de réorganiser les colonnes doit être accordée prudemment. En effet, le programmeur identifie les besoins de l’utilisateur en détectant les cellules modifiées, sélectionnées, ou simplement cliquées. Ainsi, la prise en compte dans DGVPersEtLoc de la valeur de la cellule contenant l’identifiant de la personne nommée Azerty désigne bien cette personne. Mais si les colonnes IdPers et XIdLoc sont permutées, la même interprétation de la même cellule désigne la personne nommée Panzani. La vérification des textes des entêtes de colonnes pallie à cet inconvénient. Le programmeur dispose de nombreux moyens de détections et d’actions sur les différents éléments et valeurs d’un DataGridView lequel dispose d’ailleurs de collections connues telles que Columns, Rows, Cells, …, chacune équipée des propriétés et méthodes habituelles. Détection d’un clic quelconque du DataGridView (cette détection masque les autres clics) :

Private Sub DGVPersEtLoc_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles DGVPersEtLoc.Click

MessageBox.Show(DGVPersEtLoc.CurrentCell.ColumnIndex) MessageBox.Show(DGVPersEtLoc.CurrentCell.RowIndex) MessageBox.Show(DGVPersEtLoc.CurrentCell.Value) MessageBox.Show(DGVPersEtLoc.CurrentRow.Index.ToString) End Sub

Page 291: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 41

Détection du clic d’un entête de colonne :

Private Sub DGVPersEtLoc_ColumnHeaderMouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs)

Handles DGVPersEtLoc.ColumnHeaderMouseClick MessageBox.Show(e.ColumnIndex) MessageBox.Show(DGVPersEtLoc.Columns(e.ColumnIndex).HeaderText) End Sub

Détection du clic d’un entête de ligne :

Private Sub DGVPersEtLoc_RowHeaderMouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs)

Handles DGVPersEtLoc.RowHeaderMouseClick MessageBox.Show(e.RowIndex) End Sub

Détection du clic d’une cellule :

Private Sub DGVPersEtLoc_CellMouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs)

Handles DGVPersEtLoc.CellMouseClick MessageBox.Show(e.ColumnIndex) MessageBox.Show(e.RowIndex) DGVPersEtLoc.CurrentCell.Value = "Nouvelle valeur" End Sub

L’impression des données par CrystalReport L’impression des données contenues dans un DataSet peut bien entendu être programmée par l’usage des outils d’impression étudiés précédemment. Mais il existe un autre composant qui mérite d’être abordé ici, même si ce n’est que de manière très sommaire. Il s’agit du Crystal Report, lequel se présente sous la forme du CrystalReportViewer grâce auquel l’utilisateur peut visualiser le document et en gérer l’impression. Ce document, quant à lui, est un CrystalReport présent sous la forme d’un fichier dont l’extension est .rpt ajouté à la solution en cours de développement de diverses manières dont voici la plus simple. La première tentative d’insertion d’un Composant Crystal Report provoque le téléchargement et l’installation de l’outil à partir d’un site de SAP, le nouveau détenteur des droits sur ce produit. Plusieurs réglages sont requis pour disposer des composants nécessaires au niveau du projet. Il faut : 1. choisir un Framework cible dans Projet/Propriétés/Compiler/Options avancées …; 2. ajouter les références comme illustré ci-contre. A noter qu’un Framework Client est limité et n’offre pas toutes les fonctionnalités nécessaires. Le composant CrystalReportViewer n’est pas disponible. Le menu contextuel obtenu par un clic du bouton droit de la souris dans l’explorateur de solutions, permet notamment l’ajout d’un nouvel élément au projet.

Il suffit de choisir dans la boîte dialogue illustrée ci contre l’élément de type Rapport Crystal et de le nommer de manière représentative de son rôle. Une application peut contenir de nombreux rapports. La première boîte de dialogue qui s’ouvre alors offre le choix entre plusieurs modèles de rapport, ainsi que le choix de ne pas utiliser l’assistant de conception et de créer un rapport vide. L’aide de l’assistant peut être utile pour l’élaboration de rapports plus évolués, mais pour un rapport standard, le plus simple est sans conteste de partir d’un rapport vide.

Page 292: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 42

Le menu contextuel obtenu par un clic du bouton droit de la souris sur le rapport vide ouvre l’accès à un Expert Base de données.

L’Expert Base de données ouvre une boîte de dialogue qui permet la sélection de diverses sources de données. Pour l’exemple illustré ci contre, le choix s’est porté sur Créer une nouvelle connexion/Fichiers de base de données et a permis la désignation de la base des exemples précédents. Les tables qui concernent le rapport envisagé sont ensuite glissées dans la liste étiquettée Tables sélectionnées.

L’onglet Liens de la boîte de dialogue permet d’établir les relations existant entre ces tables. Ces opérations réalisées, l’Explorateur de champs accessible par le menu contextuel illustré ci-dessus propose les champs des tables sélectionnées ainsi que d’autres spécifiques à CrystalReport. Ces champs peuvent être glissés sur le rapport vide selon la mise en page souhaitée.

Le rapport est créé et il faut implémenter son exploitation dans l’application.

Le placement d’un composant visuel CrystalReportViewer sur un formulaire instancié à la demande offre l’avantage de fournir une prévisualisation à l’utilisateur et de dispenser le programmeur de la gestion de l’impression. Le travail du programmeur se résume alors à peu de choses comme l’illustre l’exemple suivant.

Page 293: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBDB - 43

Un CrystalReportViewer nommé MonCrystalVisu a été déposé sur le formulaire principal de l’application. Un bouton Rapport nommé BRapport a également été placé sur ce formulaire et son rôle est d’activer le remplissage du rapport CRPersEtLoc et de le présenter dans MonCrystalVisu. La seule programmation requise ici est celle de la procédure événementielle BRapport_Click suivante.

Private Sub BRapport_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BRapport.Click

Dim UnRapport As New CRPersEtLoc ' Instancier le rapport souhaité MonCrystalVisu.ReportSource = UnRapport ' Lier le rapport au Viewer End Sub

Et voici le résultat de son exécution. Dans l’exemple précédent, l’objet CrystalReport lit toute la base de données et remplit le rapport conformément aux réglages réalisés à l’aide du concepteur. Il est plus fréquemment utile de ne présenter qu’une partie des enregistrements, ceux correspondant à une sélection particulière. L’utilisateur de l’exemple précédent aurait pu souhaiter n’obtenir que la liste des personnes n’habitant pas la localité Loc 1. Alors que la gestion d’une base de données par une application permet toutes les sélections souhaitables par l’utilisateur, les rapports intégrés dans cette application ne sont pas modifiables en cours d’exécution. L’édition de rapports partiels, à la demande de l’utilisateur, n’est donc pas possible si le programmeur ne met en place les dispositifs adéquats. Ces dispositifs sont une base de données Rapports et des procédures réalisant son remplissage et sa vidange au gré des besoins des utilisateurs. Les rapports sont ensuite conçus à partir de cette base. Cette base de données Rapports doit contenir toutes les entités nécessaires pour satisfaire n’importe lequel des rapports intégrés et au moins, toutes les entités présentes dans la base de données réelle de l’exploitation. Elle peut aussi contenir l’une ou l’autre entité qui serait utile à certains rapports. Les entités qui reproduisent celles de la base de données réelle en sont des copies conformes, sauf qu’elles ne contiennent aucun enregistrement. Afin d’alléger le travail du S.G.B.D. sur cette base, les relations et contraintes ne sont pas reproduites. Par ailleurs, les relations nécessaires à un rapport sont spécifiées lors de sa conception. La base de données Rapports doit être stockée dans le dossier par défaut de l’application et la désignation de son nom au concepteur ne doit comporter aucun chemin. Dans l’application, l’édition d’un rapport partiel doit réaliser le remplissage des entités de la base Rapports avant de désigner le rapport au CrystalReportViewer. Il convient que les entités soient vidées de leurs enregistrements après le traitement du rapport afin d’être disponibles pour un autre.

Page 294: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 1

Eléments de programmation orientée Internet

en VB.Net

Page 295: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 2

Tables des matières des pages VBWEB Introduction ........................................................................................................................................................................ 3 Les Web Forms ................................................................................................................................................................... 4

La page HTML ......................................................................................................................................................... 4 La page ASPX .......................................................................................................................................................... 6

Les composants ASP visuels .......................................................................................................................... 8 L'objet Page ......................................................................................................................................... 9 Le composant TextBox ...................................................................................................................... 11 Le composant HyperLink .................................................................................................................. 11 Le composant LinkButton ................................................................................................................. 11 Le composant ImageButton ............................................................................................................... 11 Le composant Image .......................................................................................................................... 12 Le composant HiddenField ................................................................................................................ 12 Le composant Menu........................................................................................................................... 12 Le composant Table ........................................................................................................................... 13 Les composants CheckBox, CheckBoxList, RadioButton et RadioButtonList ................................. 13 Le composant Panel ........................................................................................................................... 16 Les composants View et MultiView .................................................................................................. 16 Le composant FileUpload .................................................................................................................. 17

Liaisons de composants à une base de données ........................................................................................... 18 Connexion à une base Access ............................................................................................................ 18 Les composants ListBox et DropDownList ....................................................................................... 19

Les services Web .............................................................................................................................................................. 20 Introduction ............................................................................................................................................................ 20

Cycle de fonctionnement ............................................................................................................................. 20 Utilisation d'un service Web .................................................................................................................................. 21

Recherche du service ................................................................................................................................... 21 Mise en place du service .............................................................................................................................. 21 Exploitation du service ................................................................................................................................. 23

Création d'un service Web ...................................................................................................................................... 24 Exploitation du service ................................................................................................................................. 26

Exemple d'exploitation synchrone ..................................................................................................... 26 Exemple d'exploitation asynchrone ................................................................................................... 26

Création et exploitation d'un service avec contrôle d'accès ......................................................................... 27 Le Net Remoting .............................................................................................................................................................. 29

Le choix entre Service Web et Net Remoting ........................................................................................................ 29 Fonctionnement du Net Remoting ......................................................................................................................... 29 Programmation du Net Remoting ........................................................................................................................... 30

Création d'une application serveur élémentaire ........................................................................................... 30 Création d'une application cliente ................................................................................................................ 31 Démarrage d'une application serveur par une application cliente ................................................................ 32

Page 296: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 3

Introduction

L'Internet (pour InterNetwork) est l'interconnexion au niveau mondial de réseaux hétéroclites dans laquelle chaque machine est identifiée par une référence unique dite adresse IP (Internet Protocol). Un ordinateur connecté peut être indépendant ou être membre d'un réseau local et néanmoins recevoir une adresse IP particulière, à moins qu'un des ordinateurs de ce réseau dispose d'un accès à Internet et le partage. Il est possible également que les ordinateurs d'un réseau local non connecté à Internet soient différenciés par une adresse IP et il s'agit là aussi d'Internet. Soit, comme l'indique l'abréviation IP, il y a Internet dès qu'il y a IP. Le Web (World Wide Web, toile d'araignée mondiale), souvent confondu avec Internet, est une des applications utilisatrices d'Internet, au même titre que la messagerie électronique par exemple. Le rôle du Web est d'établir des liens entre des informations dispersées sur Internet et de permettre leur exploitation à l'aide de navigateurs. Le Web dispose de serveurs HTTP (HyperText Transfert Protocol) desservant des ressources essentiellement HTML (HyperText Markup Language). Le navigateur est une application cliente dont le rôle principal est d'accéder aux ressources du Web. Les applications liées de près ou de loin à ce réseau de transport d'informations qu'est Internet sont nombreuses et diverses. Elles sont notamment la gestion de réseaux, le courrier électronique, la vidéoconférence, les services tels que VPN (Virtual Private Network), RDP (Remote Desktop Protocol), les applications client serveur de tous genres, ainsi que la création de serveurs et de clients de n'importe quelle sorte pourvu qu'ils dialoguent par l'Internet. L'environnement de développement VB.Net offre toutes les fonctionnalités nécessaires à la réalisation de n'importe quel projet Internet, la plupart étant livrées par l'espace de noms System.Net. L'exemple ci-dessous, illustre l'usage d'une de ces fonctionnalités. Le programme effectue la recherche d'un ordinateur, indifféremment sur base de son nom ou de son adresse IP, par la consultation des serveurs DNS (Domain Name System) et ce, en quelques lignes seulement. Ce cours étant essentiellement orienté vers l'informatique de gestion, trois aspects les plus fréquemment utiles de la programmation pour Internet sont abordés dans ces pages. Il s'agit des Web Forms, des Web Services et du Net Remoting. Imports System.Net Public Module UnModule Sub Main() Dim MachineCherchee As String Dim MachineTrouvee As IPHostEntry Dim i As Integer Console.WriteLine(("Machine Locale = " & Dns.GetHostName())) Do Console.Write("Machine recherchée (<Enter> pour arrêter) : ") MachineCherchee = Console.ReadLine().Trim().ToLower() If MachineCherchee <> String.Empty Then Try MachineTrouvee = Dns.GetHostEntry(MachineCherchee) Console.WriteLine(("Machine : " & MachineTrouvee.HostName)) Console.Write(("Adresses IP : " & MachineTrouvee.AddressList(0).ToString)) For i = 1 To MachineTrouvee.AddressList.Length - 1 Console.Write((", " & MachineTrouvee.AddressList(i).ToString)) Next i Console.WriteLine() If MachineTrouvee.Aliases.Length <> 0 Then Console.Write(("Alias : " & MachineTrouvee.Aliases(0))) For i = 1 To MachineTrouvee.Aliases.Length - 1 Console.Write((", " & MachineTrouvee.Aliases(i))) Next i Console.WriteLine() End If Catch Console.WriteLine("Machine [" & MachineCherchee & "] non trouvée.") End Try End If Loop Until MachineCherchee = String.Empty End Sub End Module

Page 297: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 4

Les Web Forms Un site Web est un ensemble des ressources HTML mises à la disposition de l'utilisateur par un serveur Web. Ces ressources sont essentiellement des pages HTML, c'est à dire des fichiers textes dotés de l'extension .htm ou .html et contenant du code HTML. Le code HTML définit la présentation des pages à l'écran et il peut contenir de l'information, des liens hypertextes, des références de fichiers à afficher sur la page, ainsi que des scripts permettant quelques traitements locaux. Les pages HTML sont interprétées par un navigateur qui assure la présentation du contenu dans la forme décrite et réalise l'exécution des scripts éventuels. Ceux-ci sont écrits dans n'importe quel langage de script supporté par le navigateur. Par exemple, VBScript et JavaScript ont toujours été bien interprétés par Internet Explorer de Microsoft, mais certains codes JavaScript n’étaient pas compris par le navigateur Netscape (av. 2008). Le navigateur transmet des requêtes vers les serveurs selon les URL (Uniform Resource Locator) encodées par l'utilisateur et au gré des liens cliqués. Le plus souvent, le serveur concerné répond à ces requêtes en activant les liens désignés et en livrant les pages demandées. Ces pages HTML qui ne nécessitent aucun traitement particulier de la part du serveur sont dites statiques. Des traitements plus importants peuvent être programmés à charge du serveur. Ainsi par exemple, la consultation d'un compte personnel sur le site Web d'une banque ne peut se réduire à la simple présentation d'une page désignée par un lien hypertexte. Cette opération requiert au moins l'identification préalable de l'utilisateur. Les pages permettant un dialogue continu entre le navigateur et le serveur sont dites dynamiques. Elles présentent toujours une portion de code HTML, ne serait-ce que pour permettre leur présentation par le navigateur, mais elles contiennent aussi un programme, ou bien sa désignation, qui n'est pas transmis au navigateur et qui reste sur le serveur, seul habilité à l'exécuter. Ces programmes exécutés sur les serveurs sont écrits dans n'importe quel langage supporté par le serveur. Par exemple, le serveur Apache ne peut pas exécuter des codes ASP, mais bien ceux écrits en PHP (Personnal Home Page) et le serveur IIS (Internet Information Server) de Microsoft exécute naturellement les codes ASP (Active Server Page).

La page HTML Il n'est pas question ici d'étudier ce langage, mais les pages HTLM sont indispensables à toute application Web, même si elles ne contiennent parfois que très peu de code, ou même aucun ! De plus, si l'environnement Visual Studio.Net ne permet pas la création de pages HTML isolées, il permet leur insertion dans n'importe quel projet Web (Explorateur de solutions/Ajouter/Nouvel élément/Page HTML) et présente alors les composants visuels adéquats. Il est donc tout à fait opportun ici d'illustrer cette démarche. Parler d'une page HTML sans code semble être l'expression même de l'absurdité. Pourtant, comme signalé précédemment, une page HTML est un fichier texte doté de l'extension .htm ou .html. Cette simple affirmation justifie bien une petite expérience. L'exécution en session Dos de la commande prompt > UnePage.htm crée un fichier texte vide de tout caractère et nommé UnePage.htm. Il suffit d'ouvrir ce fichier avec un navigateur pour se rendre compte qu'une page Web est effectivement affichée et la visualisation de son source par le navigateur (Affichage/Source) montre bien une feuille vide. En ouvrant cette page avec un éditeur de texte quelconque, il est possible d'y placer quelques caractères, par exemple Bonjour le monde. Si le fichier modifié est enregistré avec la même extension, sa réouverture avec le navigateur permet de constater que la chaîne encodée est affichée. La visualisation du source de cette page par le navigateur montre bien les caractères encodés et toujours pas de code HTML. Bien entendu, une page HTML sans code n'est pas très utile aux applications Web. Après avoir démarré la création d'une Application Web ASP.NET vide sous VisualStudio, une page HTML peut être ajoutée comme indiqué ci dessus par l'usage du menu contextuel de l'explorateur de solutions. Cette page est nommée HTMLPage.htm pour l’exemple. La visualisation du code sous VisualStudio montre un code qui ne contient pratiquement que quelques balises. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title></title> </head> <body> </body> </html>

Page 298: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 5

L'ouverture de cette page par le navigateur affiche une page blanche, tout comme le fichier vide de l'expérience précédente, mais cette fois la visualisation du code source de la page par le navigateur montre un code HTML, exactement celui reproduit ci-dessus. Après l’insertion de la page HTML, l'environnement de développement présente des onglets sous la fenêtre d'affichage, dont Source et Design. Le choix de ce dernier ouvre un formulaire vierge qui est la partie visible de la page HTML. La boîte à outils propose alors les composants visuels HTML. Pour l'exemple, un composant Input (Text) est glissé sur le formulaire et sa propriété Value est fixée à Bonjour le monde dans la fenêtre des propriétés. Le retour à l'onglet Source montre le code HTML actualisé. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title></title> </head> <body> <p><input id="Text1" type="text" value="Bonjour le monde" /></p> </body> </html> L'ouverture de la page avec le navigateur présente bien une boîte de texte contenant la chaîne Bonjour le monde et la visualisation de son code source par le navigateur montre effectivement le code ci-dessus. Afin d'illustrer l'usage de scripts dans la page HTML, deux composants Input (Button) sont ajoutés au formulaire et les propriétés Value sont fixées respectivement à B1 et B2. Le code actualisé présente bien les descriptions de ces boutons. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title></title> </head> <body> <p><input id="Text1" type="text" value="Bonjour le monde" /> <input id="Button1" type="button" value="B1" /> <input id="Button2" type="button" value="B2" /></p> </body> </html> Ces lignes de description des boutons sont ensuite modifiées pour désigner les scripts à exécuter sur l'événement Click de chaque bouton. Le clic du bouton B1 doit lancer le script B1_onclick et le script B2_onclick est affecté au bouton B2.

<input id="Button1" type="button" value="B1" onclick="return B1_onclick()" /> <input id="Button2" type="button" value="B2" onclick="return B2_onclick()" />

Pour l'exemple, le script B1_onclick est écrit en JavaScript et B2_onclick est écrit en VBScript. Ces scripts qui sont chacun délimité par une balise annonçant le langage utilisé, peuvent être placés n'importe où sur la page. Toutefois, lorsque leur écriture est initiée par VisualStudio, ils sont placés dans la partie du code HTML délimitée par les balises <head>.

<script language="javascript" type="text/javascript" > function B1_onclick() { Text1.value = "Bonjour de JS"; } </script>

Page 299: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 6

<script language="vbscript" type="text/vbscript" > sub B2_onclick() Text1.value = "Bonjour de VB" end sub </script>

Une fois la page ouverte par le navigateur, le clic d'un bouton provoque bien l’affichage correspondant dans la boîte de texte. La visualisation du code source de la page par le navigateur montre l'entièreté du code HTML et des scripts. Les pages HTML des expérimentations précédentes peuvent être stockées n'importe où, dans n'importe quel dossier, et elles peuvent être accédées par l'explorateur de Windows. Leur ouverture peut alors se faire par le menu contextuel Ouvrir avec … dans lequel est choisi le navigateur, Internet Explorer par exemple. Ces expérimentations ne sont donc pas dépendantes d'un serveur Web. L'affichage de la page et l'exécution des scripts sont des opérations strictement locales réalisées par le navigateur seul.

La page ASPX L'écriture d’une page dynamique sous DotNet se fait avec le langage ASP.Net, ou ASPX. Lors de la création d'une application Application Web ASP.NET vide, l’insertion d’une Web Form s’effectue comme l’insertion de la page HTML des illustrations précédentes (Explorateur de solutions/Ajouter/Nouvel élément/Web Form). Après l’insertion d’un premier Web Form nommé Default.aspx, l’explorateur de solution présente deux fichiers : Default.aspx et Default.aspx.vb. Le fichier Default.aspx.vb est la partie code de la page et le fichier Default.aspx est la partie HTML générée en fonction des composants visuels ASP.Net qui y sont placés. L'onglet Design permet l'accès à la partie graphique de la page. Cette fois, la boîte à outils présente de nombreux composants visuels, généralement groupés en familles comme illustré ci contre. Les composants les plus importants de l'ASP.Net sont ici les composants standards très semblables à ceux utilisés pour les Windows Forms. Les composants HTML peuvent également être déposés sur la page ASP pour l'initier des scripts. Contenu du fichier Default.aspx.vb au moment de la création : Partial Class Default Inherits System.Web.UI.Page End Class Contenu du fichier Default.aspx au moment de la création : <%@ Page Language="vb" AutoEventWireup="false"

CodeBehind="Default.aspx.vb" Inherits="NomDuProjet._Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> </div> </form> </body> </html> Cette page est bien une page HTML mais elle ne peut être interprétée par le navigateur seul. En effet, sa première ligne de code est un script qui désigne le fichier de codes lié à cette page (CodeBehind="Default.aspx.vb"). De plus, la partie affichable de cette page doit être exécutée par le serveur (<form id="form1" runat="server">).

Page 300: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 7

Par contre, si la page est stockée sur un site Web et invoquée par un navigateur, alors elle est correctement affichée et présente, dans son état actuel, une page blanche. La visualisation du code source par le navigateur ne restitue pas exactement le code HTML précédent, les parties de ce code qui ne concernent que le serveur n'ont pas été transmises au navigateur et un champ caché (<input type="hidden" …) a été ajouté. Cette visualisation du code source ne présente plus guère d'intérêts. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head><title></title></head> <body> <form name="form1" method="post" action="Default.aspx" id="form1"> <divclass="aspNetHidden"> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"

value="/wEPDwUJNzgzNDMwNTMzZGRzdYXf0BqIYlQU1+ZxP7GrvKgrRg==" /> </div> <div> </div> </form> </body> </html> Pour poursuivre l'observation de ces fichiers, une boîte de texte nommée TAspx, un bouton nommé BAspx, deux composants ASP (ou standard), sont glissés sur la page. Deux composants HTML, une boîte de texte nommée THtml de type Input (Text) et un bouton nommé BScript, de type Input (Button) sont également déposés. Contrairement à ce qui ce fait pour les composants des Windows Forms, les noms des objets ne sont pas stockés ici dans une propriété Name, mais bien dans une propriété Id. Le fichier Default.aspx est automatiquement mis à jour et les descriptions des quatre composants y sont présentes.

<body> <form id="form1" runat="server"> <div> <asp:TextBox ID="TAspx" runat="server" /> <asp:Button ID="BAspx" runat="server" Text="BAspx" /> <input id="THtml" type="text" /> <input id="BScript" type="button" value="BScript" /> </div> </form> </body>

La lecture de ces descriptions montre que, contrairement au composant HTML, les composants ASP sont tous réputés devoir être exécutés sur le serveur. A ce stade, aucun code n'est ajouté à Default.aspx.vb ni aucun script à Default.aspx. L'invocation de ce dernier par un navigateur présente la page illustrée ci contre et la visualisation du code source montre une description strictement HTML des quatre composants, les composants ASP ayant été redessinés par le serveur avant l'envoi de la page.

<div> <input name="TAspx" type="text" id="TAspx" /> <input type="submit" name="BAspx" value="BAspx" id="BAspx" /><br /> <input id="THtml" type="text" /> <input id="BScript" type="button" value="BScript" /> </div>

Comme pour la programmation des Windows Forms, le double-clic d'un composant dans l'interface de développement initie une procédure événementielle et la présente au programmeur pour l'écriture du code requis. Le double-clic du composant BAspx ouvre le fichier Default.aspx.vb et présente la procédure BAspx_Click qui est ensuite complétée en VB par la ligne de code TAspx.Text = "Bonjour d'Asp".

Page 301: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 8

Protected Sub BAspx_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BAspx.Click

TAspx.Text = "Bonjour d'ASP" ' Le serveur n’accède qu’aux composants ASP End Sub

Le double-clic du composant BScript ouvre le fichier Default.aspx et présente la fonction BScript_onclick qui est ensuite complétée, ici en JavaScript, par deux lignes de code pour produire l'affichage de Bonjour de JS dans chaque boîte de texte. Par défaut, l'environnement de développement initie les scripts en JavaScript. Le programmeur souhaitant les écrire en VBScript doit placer les balises adéquates.

<script language="javascript" type="text/javascript"> function BScript_onclick() { document.getElementById("THtml").value = "Bonjour de JS"; document.getElementById("TAspx").value = "Bonjour de JS"; }

</script> Il faut remarquer au passage que la description du bouton BScript dans ce fichier a été automatiquement modifiée. <input id="BScript" type="button" value="BScript" onclick="return

BScript_onclick()" /> La visualisation du code source de la page par le navigateur montre les mêmes modifications que celles déjà constatées précédemment. Ce qui ne concerne que le serveur n'est pas visible, tandis que les descriptions des composants HTML et le code des scripts sont visibles. Il faut encore noter que certains traitements effectués par les scripts sur les composants ASP peuvent être connus du serveur grâce aux événements émis par ces composants. Le serveur n’a accès qu’aux composants ASP. Il faut préciser aussi que les procédures événementielles des scripts ne peuvent répondre qu'à des événements émis par des composants HTML et que toutes les procédures événementielles des composants ASP sont exécutées sur le serveur. Par exemple, dans la procédure suivante, le bouton BAspx est désactivé par le serveur après l'exécution du script.

Protected Sub TAspx_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TAspx.TextChanged

BAspx.Enabled = False End Sub

Les aspects HTML de la programmation pour Internet ne sont pas autrement abordés dans ces pages dont l'objet est davantage l'étude de l'ASP. Les composants ASP visuels Seuls quelques composants issus du groupe Standard sont étudiés ici. Tous les composants ASP utilisés appartiennent à l'espace de noms System.Web.UI.WebControls. Une première série de composants est réunie sur une même page illustrée ci contre dans l'interface de développement et dans celle du navigateur. Aucun code n'a encore été écrit. Les textes en caractères plus épais indiquent les noms des composants et les textes présents sur les composants, ou entre parenthèses, représentent les identifications qui leur sont attribuées dans le code. Comme déjà signalé, contrairement aux composants des Windows Forms qui sont différenciés par une propriété Name, ceux des Web Forms sont différenciés par une propriété Id.

Page 302: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 9

L'illustration montre en outre que du texte libre peut être dactylographié et mis en forme directement sur la page. Ces libellés strictement inertes sont seulement décrits dans la partie HTML de la page et affichés tel quel. L'objet Page Avant d'énumérer des membres de composants, il est intéressant d'en examiner quelques uns de la page elle-même. L'objet Page est l'équivalent en Web du Form de Windows. Comme cela a déjà été illustré, il est décrit en HTML dans le fichier .aspx dont l'accès à la partie visible par l'onglet Design permet d'y glisser les composants souhaités. La programmation sous jacente à cette page est écrite dans le fichier dont l'extension est .aspx.vb. L'interface de programmation est identique à celle utilisée pour la programmation événementielle des Windows Forms. Une liste déroulante présente les objets et une autre présente les événements pouvant être émis par l'objet sélectionné. Parmi les événements auxquels peut répondre le code de l'objet Page, ceux liés aux échanges entre le navigateur et le serveur sont les plus importants. Ils sont au nombre de dix et offrent autant d'occasions d'interventions au programmeur. Ils sont énumérés ici dans leur ordre d'émission. Les événements PreInit, Init, InitComplete, PreLoad, Load et LoadComplete sont émis pendant le chargement de l'objet Page par le serveur (l'objet Page contient le code que doit exécuter le serveur). C'est entre les événements Load et LoadComplete que sont exécutées les éventuelles procédures événementielles liées aux composants visuels. Les événements PreRender, PreRenderComplete, SaveStateComplete et UnLoad sont émis lors de l'expédition de la page au navigateur, PreRender étant l’initiation de l'envoi et UnLoad constituant l'abandon de l'objet Page par le serveur qui termine ainsi sa session de travail. Les méthodes de l'objet Page sont nombreuses et nul doute que le programmeur Web y trouvera son bonheur. Pour ce qui concerne ce cours, seule la méthode SetFocus est retenue. Elle permet de désigner le composant qui doit avoir la main à l'issue de l'affichage de la page par le navigateur. Elle peut être programmée dans n'importe quelle partie du code pour autant qu'il soit exécuté avant l'événement PreRenderComplete.

Page.SetFocus(Bouton) ' Donne le focus au composant nommé Bouton Les trois propriétés Form, IsPostBack et EnableViewState sont à épingler ici. Form Cette propriété de l'objet Page contient le formulaire HTML de la page et donc, tous les

objets présents sur le Web Form.

For Each X As Control In Page.Form.Controls UneListe.Items.Add(X.ID) ' Remplit une ListBox nommée UneListe avec les noms Next ' des composants ASP présents sur la Web Form

IsPostBack Cette propriété en lecture seule reçoit la valeur False lors du premier affichage la page et

la valeur True quand la page est une réponse en retour au serveur. La consultation de cette propriété dans une alternative permet de séparer les tâches à accomplir lors d'un premier affichage de celles à accomplir à chaque affichage.

Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)

Handles Me.Load If Me.IsPostBack Then ' Tâches à réaliser pour répondre aux demandes de l'internaute. Else ' Initialisations diverses et tâches à réaliser avant le premier envoi au navigateur. End If End Sub

EnableViewState Cette propriété autorise la persistance des valeurs des propriétés de l'objet (l'objet Page

dans ce cas) pour lequel elle est reçoit la valeur True et l'interdit dans le cas contraire. Sa valeur par défaut est toujours True. Si la valeur False est affectée à cette propriété de l'objet Page, la persistance des propriétés des composants n'est plus possible. Les propriétés ainsi conservées doivent avoir été déclarées dans un objet StateBag qui est automatiquement mis en place par la commande ViewState. Cette commande stocke sous la référence d'un indice ou d'un libellé la propriété qui lui est désignée d’un l'objet. Au

Page 303: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 10

niveau de chaque composant, elle peut être testée et modifiée selon les besoins du programmeur. Là aussi, sa valeur par défaut est True et cette propriété d'un composant doit être traitée comme toute autre propriété dont la persistance est nécessaire car tant que les propriétés conservées ne sont pas restaurées, elles reçoivent toutes leur valeur par défaut au chargement d'un objet Page. L'exemple suivant montre la récupération d'une propriété d'un composant et celle d'une variable précédemment sauvegardées.

UnBooleen = ViewState("UnBooleen") TXT.Text = ViewState("TXTText")

Ci-dessus, la référence passée à la commande ViewState représente le nom de la variable stockée ou celui d'une propriété concaténée au nom du composant auquel elle appartient. Cette pratique améliore la lisibilité du code. Partial Class _Default Inherits System.Web.UI.Page Dim UnEntier As Integer = 0 Dim UnBooleen As Boolean Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)

Handles Me.Load Page.EnableViewState = True ' C'est la valeur par défaut If Me.IsPostBack Then ' La page est revenue et il faut répondre UnEntier = ViewState("UnEntier") ' Récupération des valeurs UnBooleen = ViewState("UnBooleen") Etiquette.EnableViewState = ViewState("EtiquetteEnableViewState") TXT.EnableViewState = ViewState("TXTEnableViewState") If Etiquette.EnableViewState Then Etiquette.BackColor = ViewState("EtiquetteBackColor") Etiquette.ForeColor = ViewState("EtiquetteForeColor") End If If TXT.EnableViewState Then TXT.Text = ViewState("TXTText") TXT.BackColor = ViewState("TXTBackColor") TXT.ForeColor = ViewState("TXTForeColor") End If UnEntier += 1 UnBooleen = Not UnBooleen TXT.Text = UnEntier Etiquette.Text = UnBooleen.ToString Else ' La page est envoyée pour la première fois Etiquette.Text = "MonTexte" TXT.Text = UnEntier UnBooleen = True End If End Sub Protected Sub Bouton_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles Bouton.Click TXT.EnableViewState = True ' C'est la valeur par défaut Etiquette.EnableViewState = Not Etiquette.EnableViewState Select Case UnEntier Case 1 Etiquette.BackColor = Drawing.Color.Cyan Etiquette.ForeColor = Drawing.Color.Brown Case 2 TXT.BackColor = Drawing.Color.Green TXT.ForeColor = Drawing.Color.Yellow Case 3 Etiquette.BackColor = Drawing.Color.Black Etiquette.ForeColor = Drawing.Color.White TXT.BackColor = Drawing.Color.White TXT.ForeColor = Drawing.Color.Magenta Case Else UnEntier = 0 End Select End Sub

Page 304: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 11

Protected Sub BImage_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles BImage.Click

' Ne rien faire ici pour l'instant. Le clic de BImage montre l'évolution de UnEntier dans ' TXT et la persistance des propriétés conservées. End Sub Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs)

Handles Me.PreRender ViewState("UnEntier") = UnEntier ' Sauvegarde des valeurs ViewState("UnBooleen") = UnBooleen ViewState("EtiquetteEnableViewState") = Etiquette.EnableViewState ViewState("EtiquetteText") = Etiquette.Text ViewState("EtiquetteBackColor") = Etiquette.BackColor ViewState("EtiquetteForeColor") = Etiquette.ForeColor ViewState("TXTEnableViewState") = TXT.EnableViewState ViewState("TXTText") = TXT.Text ViewState("TXTBackColor") = TXT.BackColor ViewState("TXTForeColor") = TXT.ForeColor End Sub End Class Le composant TextBox Le composant TextBox contient une propriété particulière AutoPostBack dont la valeur par défaut est False. Quand cette propriété a reçu la valeur True, un événement TextChanged peut être pris en compte immédiatement par le serveur. Le TextBox devient un composant de type Submit, c'est-à-dire qu'il poste la page à destination du serveur comme le fait un Button par exemple, ce dernier étant Submit par nature. Le composant HyperLink Le composant HyperLink contient et active le lien vers une autre page. Pour ce faire, deux propriétés doivent être définies. La première est sa propriété Text qui contient le libellé affiché, ou sa propriété ImageUrl qui contient la désignation d'une image à afficher à la place du libellé. La propriété ImageUrl est prioritaire sur la propriété Text. La deuxième propriété nécessaire au fonctionnement de l'HyperLink est sa propriété NavigateUrl qui contient le lien effectif vers l'autre page. Le clic du libellé ou de l'image provoque l'activation du lien défini dans NavigateUrl.

HyperLien.Text = "L'école" HyperLien.ImageUrl = "X:\MesDonnees\PhotoEcole.jpg" HyperLien.NavigateUrl = "http://www.eicw.be"

Le composant LinkButton Le composant LinkButton active également un lien vers une autre page, mais il transmet du JavaScript au serveur. Ce contrôle a la même apparence qu'un HyperLink et la même fonctionnalité qu'un Button. C'est ainsi que contrairement à l'HyperLink, celui ci émet un événement Click. Sa propriété Text permet l'affichage d'un libellé et sa propriété PostBackUrl contient le lien effectif vers l'autre page.

BLien.Text = "Page suivante" BLien.PostBackUrl = "Page2.aspx"

Le composant ImageButton Ce composant est un bouton à part entière dont la seule différence avec le Button réside dans le fait qu'il n'a pas de propriété Text, laquelle est remplacée par la propriété ImageUrl. Ce bouton présente une image à la place d'un libellé.

BImage.ImageUrl = "X:\MesDonnees\PhotoEcole.jpg"

Page 305: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 12

Le composant Image Ce composant semblable au précédent n'émet pas d'événement Click. Il n'est pas un bouton, mais simplement une illustration.

UneImage.ImageUrl = "X:\MesDonnees\PhotoEcole.jpg" Le composant HiddenField Ce composant est une zone de texte toujours invisible pour l'internaute dans laquelle le programmeur peut stocker ce qu'il veut. Ce composant est particulièrement utile pour le stockage d'une information produite chez le client par un script. Cette valeur peut d'ailleurs être immédiatement mise à la disposition du serveur à l'écoute de l'événement ValueChanged quand le composant HTML activant le script est de type Submit. Description du composant HTML Button (Submit) déposé sur la page et nommé UnBouton :

<input id="UnBouton" type="submit" value="UnBouton" onclick="return UnBouton_onclick()" />

Code du script du traitement réalisé par le navigateur :

<script language="javascript" type="text/javascript"> function UnBouton_onclick() { document.form1.ZoneCachee.value = "Texte changé"; }

</script> Prise en compte de la valeur de ZoneCachee dans le code Aspx :

Protected Sub ZoneCachee_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ZoneCachee.ValueChanged

TXT.Text = ZoneCachee.Value End Sub

Le composant Menu Le Menu sert bien entendu à proposer un choix à l'internaute et sa programmation présente deux aspects bien distincts. D'une part, il faut pouvoir gérer le menu et donc, pouvoir y placer, modifier et supprimer des libellés. D'autre part, il faut répondre au choix que fait l'internaute parmi les différents libellés présentés. La séquence suivante remplace les libellés Elem1 à Elem3 de l'illustration précédente par les libellés ElemA, ElemB et ElemC.

' Effacer tous les libellés du menu. UnMenu.Items.Clear() ' Ajouter des libellés au menu. UnMenu.Items.Add(New MenuItem("ElemA")) UnMenu.Items.Add(New MenuItem("ElemB")) UnMenu.Items.Add(New MenuItem("ElemD")) ' Supprimer un libellé désigné par son indice de 0 à UnMenu.Items.Count - 1. UnMenu.Items.RemoveAt(2) ' Insérer un libellé à l'emplacement désigné un indice. UnMenu.Items.AddAt(2, New MenuItem("ElemC"))

La détection du libellé choisi par l'internaute se fait en réponse à l'événement MenuItemClick sur base de son indice ou sur base de son texte grâce aux propriétés SelectedItem ou SelectedValue selon le cas.

Protected Sub UnMenu_MenuItemClick(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.MenuEventArgs) Handles UnMenu.MenuItemClick

If UnMenu.SelectedItem() Is UnMenu.Items(0) Then TXT.Text = "Libellé d'indice 0"

Page 306: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 13

ElseIf UnMenu.SelectedItem() Is UnMenu.Items(1) Then TXT.Text = "Libellé d'indice 1" ElseIf UnMenu.SelectedItem() Is UnMenu.Items(2) Then TXT.Text = "Libellé d'indice 2" End If End Sub Protected Sub UnMenu_MenuItemClick(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.MenuEventArgs) Handles UnMenu.MenuItemClick

Select Case UnMenu.SelectedValue Case "ElemA" TXT.Text = "Libellé d'indice 0" Case "ElemB" TXT.Text = "Libellé d'indice 1" Case "ElemC" TXT.Text = "Libellé d'indice 2" End Select End Sub

Le composant Table Le composant Table permet la présentation de données sous la forme d'un tableau composé de lignes (Rows), chacune contenant une ou plusieurs cellules (Cells). Le composant doit être construit ligne par ligne, chacune étant construite cellule par cellule. L'extrait de code suivant rempli le Table nommé UneTable avec des lignes d'une seule cellule qui referme le nom d'un composant et permet l'affichage des noms de tous les composants du Page.

For Each X As Control In Me.Form.Controls Dim R As New WebControls.TableRow ' Instanciation d'une nouvelle ligne Dim C As New WebControls.TableCell ' Instanciation d'une nouvelle cellule C.Text = X.ID ' Affectation du nom d'un composant R.Cells.Add(C) ' Ajout de la cellule à la ligne UneTable.Rows.Add(R) ' Ajout de la ligne au tableau Next

Les composants CheckBox, CheckBoxList, RadioButton et RadioButtonList Le Page ci contre a été construit pour expérimenter ces composants. Il contient deux CheckBox nommés CheckBox1 et CheckBox2, un CheckBoxList nommé ListeChoix, deux RadioButton nommés RadioButton1 et RadioButton2, ainsi qu'un RadioButtonList nommé ListeOption. Un Button nommé Bouton, non représenté ici, a également été placé sur le document. Ces composants sont pratiquement semblables à leurs homologues des Windows Forms. Toutefois, une propriété particulière, GroupName, est nécessaire au bon fonctionnement d'un ensemble de RadioButton tels que RadioButton1 et RadioButton2 de l'illustration. Tous ces composants possèdent la propriété AutoPostBack dont la valeur est False, c'est-à-dire la valeur par défaut. A l'exécution, un ou plusieurs CheckBox peuvent être cochés et décochés à volonté par l'internaute. Le test de leurs états se fait sur le serveur, lorsque le Page est de retour, par une séquence d'alternatives testant la propriété Checked de chacun. Cette propriété est de type Boolean. Sa persistance est automatiquement assurée.

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

If CheckBox1.Checked Then ' … traitement End If If CheckBox2.Checked Then ' … traitement End If End Sub

Page 307: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 14

Les CheckBox peuvent être cochés par programmation avant leur expédition au navigateur et si nécessaire, interdits de modification chez le client.

Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

CheckBox1.Text = "Choix 1" ' Définitir le libellé du CheckBox CheckBox2.Text = "Choix 2" CheckBox1.Checked = True ' Cocher le CheckBox CheckBox2.Checked = True CheckBox2.Enabled = False ' Interdire la modification du CheckBox End Sub

Le composant CheckBoxList doit être construit par l'ajout de tous les CheckBox souhaités. Idéalement, ce travail doit être réalisé avant l'expédition du page au navigateur, mais il est tout à fait possible d'ajouter des lignes à la liste en réponse à un Submit.

Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

ListeChoix.Items.Add("Choix 1") ListeChoix.Items.Add("Choix 2") ListeChoix.Items.Add("Choix 3") End Sub

La détection des CheckBox cochés dans le CheckBoxList se fait par l'évaluation de la propriété Selected de chaque ligne.

For Each X As Object In ListeChoix.Items If X.Selected Then ' … traitement End If Next

Les propriétés Value et Selected peuvent également être exploitées pour la détection des CheckBox cochés dans le CheckBoxList ou pour leur affectation par programmation.

If IsPostBack Then ListeChoix.Items(0).Value = True ' Cocher le CheckBox ListeChoix.Items(2).Selected = True ' Cocher le CheckBox If ListeChoix.Items(1).Selected Then ' … traitement End If End If

Pour en terminer avec le CheckBoxList, il faut encore noter les méthodes ClearSelection et Clear. La méthode ClearSelection donne la valeur False à chaque ChecBox de la liste.

ListeChoix.ClearSelection() Les RadioButton et RadioButtonList possèdent des fonctionnalités quasi identiques à celles des CheckBox et CheckBoxList. Les codes précédents peuvent d'ailleurs être modifiés en remplaçant CheckBox par RadioButton et ListeChoix par ListeOption. A propos du CheckBoxList et du RadioButtonList, il faut noter au passage que ces composants contiennent chacun une collection et qu'ils possèdent donc les membres ordinairement utiles à la gestion de ce type d'objet. Outre la méthode Add qui permet leur création, ils disposent aussi par exemple, de la méthode RemoveAt qui permet la suppression d'un élément désigné par son indice et de la méthode Clear qui supprime tous les éléments de la liste.

ListeChoix.Items.RemoveAt(i) ListeChoix.Items.Clear() ListeOption.Items.RemoveAt(i) ListeOption.Items.Clear()

Page 308: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 15

Chaque RadioButton déposé sur le Page est strictement indépendant des autres par défaut. C'est ainsi qu'un ou plusieurs RadioButton peuvent être cochés par l'internaute. Le test de leurs états se fait sur le serveur, lorsque le Page est de retour, par une séquence d'alternatives testant la propriété Checked de chacun. Cette propriété est de type Boolean. Sa persistance est automatiquement assurée.

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

If RadioButton1.Checked Then ' … traitement End If If RadioButton2.Checked Then ' … traitement End If End Sub

Les RadioButton peuvent être cochés par programmation avant leur expédition au navigateur et si nécessaire, interdits de modification chez le client. Mais cette dernière opération n'est utile que pour un RadioButton indépendant décoché car une fois coché, par le client ou par le code, il ne peut plus être décoché dans le navigateur.

Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

RadioButton1.Text = "Choix 1" ' Définitir le libellé du RadioButton RadioButton2.Text = "Choix 2" RadioButton1.Checked = True ' Cocher le RadioButton RadioButton2.Checked = False RadioButton2.Enabled = False ' Interdire la modification du RadioButton End Sub

L'usage des RadioButton n'est ordinairement pas celui des CheckBox et le programmeur attend un choix unique de l'utilisateur dans une batterie de RadioButton. La réunion de plusieurs RadioButton en un même ensemble se réalise par l'attribution d'un même nom à la propriété GroupeName de chacun.

Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

RadioButton1.GroupName = "Options1" RadioButton2.GroupName = "Options1" End Sub

Quand les RadioButton sont ainsi réunis, en cocher un, décoche automatiquement les autres. La recherche de l'unique coché de l'ensemble s'effectue plus efficacement par l'usage d'une cascade d'alternatives que par les mêmes alternatives en séquence.

If RadioButton1.Checked Then ' … traitement ElseIf RadioButton2.Checked Then ' … traitement End If

Le composant RadioButtonList doit être construit par l'ajout de tous les RadioButton souhaités. Idéalement, ce travail doit être réalisé avant l'expédition du Page au navigateur, mais il est tout à fait possible d'ajouter des lignes à la liste en réponse à un Submit. Les RadioButton d'un RadioButtonList sont automiquement interdépendants et en cocher un, décoche tous les autres.

Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

ListeOption.Items.Add("Choix 1") ListeOption.Items.Add("Choix 2") ListeOption.Items.Add("Choix 3") End Sub

La détection du RadioButton coché dans le RadioButtonList se fait par l'évaluation de la propriété Selected de chaque ligne. Toutefois, comme un seul peut être coché, la boucle de balayage de la liste peut être interrompue dès qu'un RadioButton coché est trouvé.

Page 309: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 16

For Each X As Object In ListeOption.Items If X.Selected Then ' … traitement Exit For ' Sortir de la boucle End If Next

Les propriétés Value et Selected peuvent également être exploitées pour la détection du RadioButton coché dans le RadioButtonList ou pour l'affectation par programmation de l'un d'entre eux.

If IsPostBack Then ListeOption.Items(0).Value = True ' Cocher le RadioButton ListeOption.Items(2).Selected = True ' Cocher le RadioButton If ListeOption.Items(1).Selected Then ' … traitement End If End If

Pour en terminer avec le RadioButtonList, il faut encore noter la méthode ClearSelection qui donne la valeur False à chaque RadioButton de la liste.

ListeOption.ClearSelection() Le composant Panel Comme son homologue des Windows Form, le Panel sert de conteneur pour d'autres composants afin d'en permettre une gestion groupée. Sa propriété Visible notamment, permet d'en cacher ou montrer tous les composants à l'aide d'une simple affectation.

Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

For Each X As Control In UnPanel.Controls UneListeBox.Items.Add(X.ID) Next UnPanel.Visible = False End Sub

Les composants View et MultiView Le composant View s'apparente au Panel parce qu'il est un conteneur d'autres composants. Mais le View doit appartenir à un composant MultiView, lequel ne peut d'ailleurs posséder que des composants View. La création d'un MultiView commence donc par la mise en place de ce composant et se poursuit par le glissé de View à l'intérieur. Chaque View peut alors recevoir les composants ASP souhaités. Un seul View peut être actif à la fois et aucun n'est actif par défaut. Pour l'expérimentation, un MultiView nommé VueMultiple a été glissé sur le Page. Ce MultiView a ensuite reçu deux View nommés Vue1 et Vue2. Du libellé libre a été dactylographié dans Vue1 et deux composants, un TextBox et un Button, y sont déposés. Un Label et un Image nommé UneImage ont été glissés dans Vue2. Enfin, un Button nommé Bouton est déposé hors du MultiView. Lors du premier affichage par le navigateur, aucun des composants contenus dans les View n'est visible. Ce n'est qu'à l'occasion des clics de Bouton que les View sont activés, et donc rendus visibles, à tour de rôle.

Page 310: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 17

Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

UneImage.ImageUrl = "X:\MesImages\ArbresEnneiges.jpg" End Sub Protected Sub Bouton_Click(ByVal sender As Object, ByVal e As System.EventArgs)

Handles Bouton.Click If VueMultiple.ActiveViewIndex = 0 Then VueMultiple.ActiveViewIndex = 1 Else VueMultiple.ActiveViewIndex = 0 End If End Sub

Les noms donnés aux View sont accessibles dans le code par le balayage de la collection Views de l'objet MultiView et la consultation de la propriété ID de chaque View.

For Each X As View In VueMultiple.Views UneListeBox.Items.Add(X.ID) Next

Le composant FileUpload Ce composant, qui est constitué d'une boîte de texte et d'un bouton étiquetté Parcourir, permet la sélection d'un fichier sur l'ordinateur client en ouvrant une boîte de dialogue OpenFileDialog et son transfert vers le serveur. Après sélection du fichier, son nom est présenté dans la boîte de texte. Lors du retour du Page au serveur, le fichier peut être enregistré par la méthode SaveAs de l'objet FileUpload. Pour l'expérimentation, un Button nommé BTransfert est placé sur le Page pour réaliser le Submit. Deux TextBox, nommées TName et TByte, sont aussi déposés sur le Web Form et sont destinés à informer l'internaute sur l'opération réalisée. Elles ne sont visibles qu'après un premier transfert. Le composant FileUpload est nommé UnTransfert.

Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

If Not IsPostBack Then TName.Visible = False ' TexBox invisibles au premier chargement TByte.Visible = False Else TName.Visible = True ' TexBox visibles aux chargements suivants TByte.Visible = True End If End Sub Protected Sub BTransfert_Click(ByVal sender As Object,

ByVal e As System.EventArgs) Handles BTransfert.Click If UnTransfert.FileName IsNot String.Empty Then UnTransfert.SaveAs(UnTransfert.FileName) TName.Text = "Fichier " & UnTransfert.FileName TByte.Text = UnTransfert.FileBytes.Length.ToString &

" octet(s) transféré(s)." Else TName.Text = "Fichier non désigné." TByte.Text = "Aucun fichier transféré." End If End Sub

Premier affichage

Vue1 active Vue2 active

Page 311: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 18

Liaisons de composants à une base de données Certains composants ASP, dont CheckBoxList et RadioButtonList déjà étudiés, possèdent des propriétés particulières qui les rendent très utiles aux présentations et gestions de données issues d'une base de données. Comme pour les liaisons aux bases de données des applications Console ou Windows Forms, c'est la technologie ADO.Net qui apporte les moyens nécessaires. Il n'est pas question de refaire ici le cours de programmation orientée bases de données, mais il est sans doute opportun d'en rappeler quelques points. La technologie ADO.Net livre les moyens nommés fournisseurs OLEDB ou, à défaut, fournisseurs ODBC. Les fournisseurs OLEDB sont produits par les éditeurs de bases de données. Le fournisseur ODBC est réglé par le programmeur sur le système local à partir de pilotes ODBC également produits par les éditeurs de bases de données. Lorsque OLEDB et ODBC sont tous deux disponibles, c'est l'OLEDB qui doit être utilisé pour garantir les meilleures performances de la technologie ADO.Net. Concrètement, ces fournisseurs d'accès sont des espaces de noms renfermant les classes à utiliser selon la base de données choisie.

System.Data.ODBC Contient les objets associés aux pilotes ODBC System.Data.OleDB Contient les objets associés aux fournisseurs OLEDB.Net System.Data.SqlClient Contient les objets associés au fournisseur Sql Server System.Data.OracleClient Contient les objets associés au fournisseur Oracle

Les classes à utiliser dépendent du mode de travail choisi. L'ADO.Net offre le choix entre le mode connecté et le mode déconnecté. Nul doute que ce dernier est plus approprié aux liaisons par Internet qui donne l'accès simultané à un plus grand nombre d'utilisateurs. Les classes du mode déconnecté sont essentiellement Connection, DataSet et DataAdapter. Des outils secondaires, mais toutefois indispensables, sont livrés dans l'espace de nom System.Data qui contient les classes utiles à tous les traitements des bases de données, sans distinction d'origine. La programmation d'une application de gestion base de données par Internet doit donc débuter par l'écriture des Imports nécessaires.

Imports System.Data Imports System.Data.OleDb ' Nécessaire pour les base de données Access

Connexion à une base Access Pour utiliser une base de données, il faut d'abord instancier un objet Connection du fournisseur d'accès choisi, soit un objet OleDbConnection pour une base Access, et définir sa chaîne de connexion.

Dim MaConnexion As New OleDbConnection Dim NomDB As String = "X:\MesDonnees\MaBaseAccess.mdb" MaConnexion.ConnectionString = "Provider=Microsoft.JET.OLEDB.4.0;Data Source= " &

NomDB & ";" Il faut ensuite, pour travailler en mode déconnecté, définir un DataAdapter approprié, soit ici un OleDbDataAdapter, ainsi qu'une table pour recueillir les données produites par la méthode Fill du DataAdapter.

Dim MaCommande As OleDbDataAdapter Dim MaTable As New DataTable MaCommande = New OleDbDataAdapter("Select * from TLoc", MaConnexion) MaCommande.Fill(MaTable)

Après ces opérations, les données sont en mémoire et l'objet DataTable utilisé, MaTable dans cet exemple, peut être géré comme cela a été étudié précédemment dans le cours de programmation orientée bases de données. Les composants ASP ListBox et DropDownList sont les outils de base de présentation de listes à l'internaute. C'est leur expérimentation qui illustre ci après l'usage du DataTable.

Page 312: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 19

Les composants ListBox et DropDownList La liaison d'un ListBox à la base de données se réalise d'une façon très similaire à celle utilisée pour les ListBox des Windows Forms, de même que la liaison d'un DropDownList est similaire à celle utilisée pour les ComboBox. Pour les exemples suivants, un Web Form a reçu deux ListBox nommés UnListBox et AutreListeBox, un DropDownList nommé UnComboBox, deux TextBox nommés UnTextBox et AutreTextBox, ainsi qu'un Button nommé BOK pour assurer le Submit.

Protected Sub BOK_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles BOK.Click

UnListBox.DataSource = MaTable ' Désignation de la source UnListBox.DataTextField = "Localite" ' Désignation du champ à afficher UnListBox.DataValueField = "IdLoc" ' Désignation d'une valaur annexe UnListBox.DataBind() ' Réalisation effective de la liaison

UnComboBox.DataSource = MaTable ' Désignation de la source UnComboBox.DataTextField = "Localite" ' Désignation du champ à afficher UnComboBox.DataValueField = "IdLoc" ' Désignation d'une valaur annexe UnComboBox.DataBind() ' Réalisation effective de la liaison End Sub

Les composants ListBox et DropDownList possèdent tous deux la propriété AutoPostBack qui peut être réglée à True pour générer un Submit à chaque changement de sélection. Une procédure événementielle SelectedIndexChanged peut ainsi réagir à chaque fois. L'exemple suivant, qui traite un ListBox, peut servir à l'expérimentation d'un DropDownList. Il suffit de remplacer tous les UnListBox par UnComboBox.

Protected Sub UnListBox_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles UnListBox.SelectedIndexChanged

UnTextBox.Text = UnListBox.SelectedItem.ToString AutreTextBox.Text = UnListBox.SelectedValue.ToString End Sub

Le ListBox dispose d'une propriété SelectionMode qui permet d'autoriser ou non les sélections multiples. Les valeurs d'une sélection multiple peuvent être récupérées par le balayage de la liste et l'évaluation de la propriété Selected de chaque Item. Une méthode ClearSelection permet de désélectionner toutes les lignes. Le DropDownList n'a pas de propriété SelectionMode et il ne permet pas les sélections multiples.

Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

UnListBox.SelectionMode = ListSelectionMode.Multiple AutreListBox.SelectionMode = ListSelectionMode.Single End Sub

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)

Handles Me.Load If IsPostBack Then AutreListeBox.Items.Clear() With UnListBox For i As Integer = 0 To .Items.Count - 1 If .Items(i).Selected Then AutreListeBox.Items.Add(.Items(i).Value & " " & .Items(i).Text) End If Next End With End If End Sub

Page 313: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 20

Les services Web

Introduction Un service Web, aussi appelé Business Service, est un composant logiciel représentant une application ou un service. Il est accessible par toute application Web, y compris par d'autres services Web. Il s'agit d'une technologie permettant à des applications de dialoguer à distance via Internet, et ceci indépendamment des plates-formes et des langages sur lesquelles elles reposent. Le RPC (Remote Procedure Call) existe depuis longtemps et donc, le concept du dialogue entre applications n'est pas nouveau. L'innovation des services Web réside dans l'usage généralisé de XML (eXtensible Markup Language) et de protocoles standardisés. Les services Web s'appuient sur un ensemble de protocoles qui déterminent les modes d'invocations mutuels des composants applicatifs. Ces protocoles, qui sont répartis selon quatre niveaux, sont par exemple (standard en 2008) :

Le transport des messages entre les applications s'effectue par le biais de HTTP, FTP ou SMTP. La mise en forme des messages se fait à l'aide d'un vocabulaire XML commun. C'est le protocole SOAP

(Simple Object Access Protocol) qui définit la structure des messages échangés par les applications via Internet.

La description de l'interface publique des services Web s'effectue par le biais de WSDL (Web Service Description Language) qui fournit un mode de description des composants applicatifs permettant d'invoquer leurs fonctions à distance. Cette description de l'interface publique d'utilisation des services Web repose sur le langage XML.

La publication des services Web (de leurs descriptions) dans un référentiel commun s'effectue par le biais de UDDI (Universal Description Discovery and Integration) qui fournit les références des connexions permettant d'invoquer dynamiquement et à distance les services Web proposés par des fournisseurs.

Cycle de fonctionnement Un service Web et la description de son interface publique étant réalisés par un fournisseur, l'usage de ce service est le suivant :

1. Le service est publié par le fournisseur, c'est-à-dire enregistré auprès d'un distributeur. Cette opération se fait en envoyant directement à l'annuaire un message UDDI (encapsulé dans une enveloppe SOAP) via un protocole de transport. Les informations fournies regroupent la localisation du service, la méthode d'invocation (et les paramètres associés) ainsi que le format de réponse. Toutes ces informations sont formalisées ensuite à l'aide de WSDL.

2. Le service est recherché par un client dans l'annuaire UDDI du distributeur. Cette recherche se fait en envoyant un message UDDI (requête) encapsulé dans une enveloppe SOAP via un protocole de transport.

3. Une réponse du distributeur est envoyée au client. C'est un message WSDL encapsulé dans une enveloppe SOAP. L'extension du fichier est idéalement .wsdl, mais elle peut être autre chose comme .asmx par exemple.

4. L'interrogation du service Web chez le fournisseur est alors possible sur base de la réponse reçue du distributeur. La demande de service s'effectue à l'aide d'un message SOAP via un protocole de transport.

5. Une réponse du fournisseur est envoyée au client qui peut alors exploiter le service. Cette réponse est transmise à l'aide d'un message SOAP.

Le fournisseur peut publier ses services sur son site et dans ce cas, les phases de recherche et d'exploitation du client s'effectuent en communication directe avec le fournisseur.

Fournisseur Distributeur Client 1. Publication2. Recherche

3. Réponse

4. Interrogation

5. Exploitation

Page 314: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 21

Utilisation d'un service Web Recherche du service Les services Web sont rarement freeware et leur recherche n'est pas chose aisée. Il suffit de lancer le moteur de recherche d'un navigateur sur le mot UDDI pour s'en rendre compte. De très nombreux articles y font référence et conduisent parfois à un ou plusieurs services Web, mais ceux-ci ne sont pas gratuits. Par exemple, en septembre 2006, une telle recherche aboutit à un portail UDDI de l'Ecole polytechnique de Lausanne qui présente un service Email Verify d'un fournisseur et propose le lien vers le site de ce dernier. L'illustration de ce portail montre aussi un menu UDDI qui laisse penser que l'école s'occupe également de publications de services Web et joue donc un rôle de distributeur. Le lien proposé vers le fournisseur est obsolète, mais sa reproduction partielle dans la zone d'adresses du navigateur permet la poursuite du voyage qui aboutit à l'objet de la recherche par l'onglet Developers de la page d'accueil du site. Plusieurs services Web sont proposés et l'un d'entre eux est gratuit ! Toutefois, c'est le service Email Verify qui est choisi pour la suite. Mise en place du service Pour utiliser le service Web trouvé, il faut charger son fichier descriptif. Pour cet exemple, le clic du lien Get WSDL visible sur l'illustration ci contre ouvre le fichier et en présente le contenu XML dans le navigateur. Il suffit de l'enregistrer localement (menu Fichier puis Enregistrer sous…). Le fichier chargé ici est nommé Emailvernotestemail.asmx. Il faut ensuite l'insérer dans l'application qui doit l'utiliser par le menu contextuel obtenu par le nom du projet dans l'explorateur de solutions.

Le point Ajouter une référence Web ouvre un assistant qui propose plusieurs types de recherches. Quand le fichier descriptif du service est stocké localement, la méthode la plus simple est sans doute de fournir son nom et son chemin d'accès dans la zone étiquettée URL et de cliquer le bouton Aller à. Le menu illustré ci contre est issu de Visual Studio 2005. Depuis 2008, il faut suivre un parcours Ajouter une référence de service / Avancé / Ajouter une référence Web. La démarche doit donc être adaptée selon la version utilisée.

Page 315: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 22

L'assistant ouvre le fichier et présente la description du service. Le clic du bouton libellé Ajouter la référence après avoir nommé la référence, Email dans cet exemple, termine la mise en place du service. La référence est visible dans l'explorateur de solutions.

Page 316: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 23

Exploitation du service L'exploitation du service dépend bien entendu des besoins de l'application, mais elle se résume à l'usage des membres du service. Le service Email Verify vérifie la syntaxe de l'adresse testée et l'existence du serveur de messagerie. Module UnModule Sub Main() Dim Test As New Email.EmailVerNoTestEmail Dim Adresse As String Do Console.Write("Adresse à tester ? ") Adresse = Console.ReadLine Try Dim Reponse As String = Test.AdvancedVerifyEmail(Adresse, 10, "0").ResponseText If Reponse Is Nothing Then Console.WriteLine("Pas de réponse") Else Console.WriteLine(Reponse) End If Catch ex As System.Web.Services.Protocols.SoapException Console.WriteLine(ex.Message) End Try Console.WriteLine() Loop Until Adresse = String.Empty End Sub End Module Exécution de ce programme.

Adresse à tester ? [email protected] ' Adresse existante et serveur valide Mail Server will accept email Adresse à tester ? scx#belgacom.net ' Syntaxe erronnée et serveur valide Email Domain Not Found Adresse à tester ? sc()[email protected] ' Adresse inexistante et serveur valide Mail Server will accept email Adresse à tester ? [email protected] ' Syntaxe correcte et serveur inexistant Email Domain Not Found

Voici un deuxième exemple d'utilisation d'un service Web. Celui-ci nécessite une clé d'accès sans laquelle il ne retourne pas de résultat valide. Ce service est proposé par Google et offre diverses méthodes permettant d'intégrer des outils de ce site dans les applications. Le fichier descriptif est nommé GoogleSearch.wsdl et il se télécharge à partir de http://api.google.com/apis/. Un fichier Googleapi.zip qui contient une application de démonstration du service est également disponible à cette adresse. Le code suivant permet la correction orthographique de mots et phrases encodés en anglais.

Dim Test As New Google.GoogleSearchService Dim Cle As String Dim Phrase As String Console.Write("Clé Google ? ") Cle = Console.ReadLine Console.Write("Phrase à tester ? ") Phrase = Console.ReadLine Try Dim Suggestion As String = Test.doSpellingSuggestion(Cle, Phrase) If Suggestion Is Nothing Then Console.WriteLine("Pas de suggestion") Else Console.WriteLine(Suggestion) End If Catch ex As System.Web.Services.Protocols.SoapException Console.WriteLine(ex.Message) End Try

Page 317: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 24

Création d'un service Web Pour expérimenter la création d'un service Web, sa publication et son exploitation, il est utile préparer le site du service. Le serveur IIS (Internet Information Services) gère un dossier InetPub dans lequel l'espace site Web est wwwroot. Un sous dossier DesServices y est créé pour l'expérimentation. Ce dernier contiendra tous les services publiés, chacun dans un sous dossier qui lui est nécessairement réservé. La création d'un service Web commence par le démarrage d'un nouveau projet de type Application Web ASP.NET vide et l’ajout d’un nouvel élément de type Web Service.

Chaque service Web XML a besoin d'être identifié par un espace de noms unique afin que les applications clientes puissent le différencier des autres services sur le Web. L'environnement de développement attribue par défaut un espace de noms http://tempuri.org/ qui est disponible pour les services Web en cours de programmation, cependant, les services publiés doivent utiliser un espace de noms permanent. Le service Web doit être identifié par un espace de noms contrôlé tel que le nom de domaine Internet du fournisseur du service, comme http://api.google.com/apis/ pour http://www.google.com/, par exemple. Bien que de nombreux espaces de noms de services Web XML ressemblent à des URL, ils n'ont pas besoin de pointer vers une ressource réelle sur le Web. Les espaces de noms de services Web sont des URI (Uniform Resource Identifier). A ce stade du développement du service Web, il convient de le personnaliser un peu et de le tester. Le nom de l'espace de noms tempuri.org est remplacé par MesServices, le nom de la fonction HelloWorld par Bonjour et la chaîne retournée est francisée. Une propriété Name peut être ajoutée à l'attribut WebService. Elle sera référencée dans l'application cliente. Le fichier descriptif du service WebService1.asmx est renommé SBonjour.asmx dans la fenêtre des propriétés. A noter ici qu’il aurait pu être nommé correctement dès son ajout ci-dessus.

Imports System.Web.Services Imports System.Web.Services.Protocols Imports System.ComponentModel <WebService(Name:="SBonjour", Namespace:="MesServices")> _ <System.Web.Services.WebServiceBinding

(ConformsTo:=WsiProfiles.BasicProfile1_1)> _ <ToolboxItem(False)> _ Public Class WebService1 Inherits System.Web.Services.WebService <WebMethod()> _ Public Function Bonjour() As String Return "Bonjour le monde" End Function End Class

Page 318: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 25

Il convient d'éviter les mots Service et Services pour quelque usage que ce soit lors la programmation des services Web car ils ont chacun un homonyme dans le Framework. L'exécution du service dans son environnement de développement, avant même de le référencer dans une autre application, permet d'estimer son bon fonctionnement.

Le lien Bonjour ouvre la page de test présentée ci dessous. Son bouton Appeler lance le test qui s'avère concluant quand sa page présente l'espace de noms défini dans le service et la valeur renvoyée prévue.

Il faut maintenant publier le site. Si le serveur Web autorise l'installation à distance de sites, l'assistant (menu Générer/Publier …) peut être utilisé et il suffit de lui désigner l'emplacement sur le site. Tous les fichiers éventuellement placés là auparavant sont supprimés et remplacés par les fichiers nécessaires à l'exploitation du service. Si le serveur Web ne permet pas cette installation à distance, il faut désigner à l'assistant un emplacement local pour les fichiers. Ceux-ci peuvent être copiés par d'autres moyens dans un dossier virtuel mis à disposition sur le serveur, par exemple en utilisant un réseau local, comme illustré ici.

Le lien Description du service ouvre la page WSDL, partiellement illustrée ci-dessous.

Page 319: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 26

Exploitation du service Enfin, l'exploitation de ce service peut se faire exactement comme expliqué précédemment dans "Utilisation d'un service Web". Il ne faut toutefois pas oublier que l’exécution réelle d’un Service Web ne peut se faire que sur un serveur IIS de Microsoft. Il faut donc installer un tel serveur sur l’ordinateur de développement, ou sur un ordinateur voisin sur le réseau. Plusieurs systèmes Windows acceptent l’installation de ce serveur. Pour les expérimentations, il est aussi possible d’utiliser le serveur de développement de Visual Studio. Simplement, il faut lancer deux instances de Visual Studio, l’une pour la mise au point du service et l’autre pour la réalisation d’une application cliente. Il est alors possible de désigner le service par http://localhost:N°Port/Chemin/LeService.asmx. Il faut noter encore que, contrairement à ce qui a été fait dans les exemples précédents, il est prudent de mettre en œuvre l'exploitation d'un service Web par l'usage des techniques de la programmation asynchrone. En effet, les temps de réponses, qui dépendent d'un serveur lointain, mais aussi de la qualité des communications, peuvent devenir infinis et paralyser le déroulement de l'application consommatrice du service Web. Lors de la compilation d'un service Web, DotNet met en place les méthodes et événements nécessaires, compte tenu des méthodes Web exposées. C'est ainsi que le service SBonjour précédent, qui n'expose pourtant que la méthode Bonjour, présente aussi dans l'application cliente une méthode asynchrone BonjourAsync et un événement BonjourCompleted qui permettent son exploitation en mode asynchrone. Exemple d'exploitation synchrone Module UnModule Sub Main() Dim SB As New SBonjour.SBonjour Console.WriteLine(SB.Bonjour) Console.ReadLine() End Sub End Module Exemple d'exploitation asynchrone Module UnModule Dim WithEvents SB As New SBonjour.SBonjour Sub Main() Dim Chaine As String Chaine = "Voici le résultat " ' La chaine, qui peut être Nothing, est passée à SB.BonjourAsync(Chaine) ' la procédure événementielle SB_BonjourCompleted Console.ReadLine() ' de traitement du résultat. End Sub Private Sub SB_BonjourCompleted(ByVal sender As Object,

ByVal e As SBonjour.BonjourCompletedEventArgs) Handles SB.BonjourCompleted Console.WriteLine(e.UserState.ToString & e.Result.ToString) End Sub End Module

Page 320: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 27

Création et exploitation d'un service avec contrôle d'accès Il est fréquemment souhaitable que les services Web publiés ne soient pas exploitables par n'importe quel internaute, mais seulement par ceux qui ont reçu l'agrément du fournisseur (suite au payement d'un droit d'exploitation, par exemple). L'usage d'un entête SOAP permet la transmission au serveur d'informations qui peuvent être traitées en vue d'accorder ou non le droit à l'exécution des méthodes exposées. L'illustration de la technique est réalisée ci après par la modification du service Web SBonjour précédent. Un fichier MesClients.txt a été ajouté aux fichiers du site pour constituer une liste d'utilisateurs autorisés. Pour l'exemple, ce fichier est naïvement stocké dans le dossier virtuel du site. Il contient, pour chaque utilisateur, son nom, son login d'accès et son mot de passe, le tout sans aucun cryptage. Il est évident qu'en situation réelle, il peut être utile que les données de ce fichier soient cryptées et surtout, que ce fichier soit stocké dans un dossier inaccessible via le Web. La classe EnteteLogin suivante hérite de la classe SoapHeader et permet la transmission au serveur du login et du mot de passe du client. Public Class EnteteLogin Inherits SoapHeader Private Nom As String Private MotDePasse As String Public Property Login() As String Get Return Nom End Get Set(ByVal valeur As String) Nom = valeur End Set End Property Public Property Passwd() As String Get Return MotDePasse End Get Set(ByVal valeur As String) MotDePasse = valeur End Set End Property End Class Pour contrôler l'accès au service, il faut ajouter à ce dernier une référence à EnteteLogin et modifier la méthode exposée afin qu'elle récupère les informations transmises et réagisse en conséquence. La méthode Bonjour devient un intermédiaire de contrôle qui commande l'exécution du réel code d'exploitation si l'utilisateur est reconnu et autorisé. Imports System.Web Imports System.Web.Services Imports System.Web.Services.Protocols <WebService(Name:="SBonjour", Namespace:="MesServices")> _ <WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _ <Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _ Public Class Service Inherits System.Web.Services.WebService Public Authentification As EnteteLogin ' Une référence à EnteteLogin Private Function Execution() As String Return "Bonjour le monde" ' Code réel d'exploitation End Function

Page 321: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 28

<WebMethod(), SoapHeader("Authentification")> _ ' Désignation modifiée de la WebMethod Public Function Bonjour(ByVal Client As String) As String If (Authentification Is Nothing) Then Return String.Empty ' Si pas d'entête Soap End If If UtilisateurOK(Client, Authentification.Login, Authentification.Passwd) Then Return Execution() ' Commande du code réel d'exploitation Else Return String.Empty ' Utilisateur non reconnu par la End If ' fonction UtilisateurOK qui effectue End Function ' les contrôles par lecture du fichier Private Function UtilisateurOK(ByVal Client As String, ByVal Login As String,

ByVal MotPasse As String) As Boolean Dim F As New System.IO.StreamReader(Me.Server.MapPath("SBonjour") &

"\..\MesClients.Txt") Dim UnClient As String Dim SonLogin As String Dim SonPsswd As String Do Until F.Peek = -1 UnClient = F.ReadLine() SonLogin = F.ReadLine() SonPsswd = F.ReadLine() If UnClient = Client Then If SonLogin = Login And SonPsswd = MotPasse Then F.Close() Return True End If End If Loop F.Close() Return False End Function End Class Bien entendu, cet aménagement du service SBonjour induit un mode d'exploitation différent. Voici un exemple d'exploitation synchrone du service modifié.

Sub Main() Dim SB As New SBonjour.SBonjour Dim Login As New SBonjour.EnteteLogin Dim Nom As String Console.Write("Nom d'utilisateur ? ") Nom = Console.ReadLine() Console.Write("Chaîne de login ? ") Login.Login = Console.ReadLine() Console.Write("Mot de passe ? ") Login.Passwd = Console.ReadLine() SB.EnteteLoginValue = Login Console.WriteLine(SB.Bonjour(Nom)) End Sub

Page 322: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 29

Le Net Remoting

Le choix entre Service Web et Net Remoting Il n'entre pas dans le cadre de ces pages d'exposer une étude comparative approfondie des différentes techniques, mais une petite comparaison de la technologie du Web Service et de celle du Net Remoting s'impose. Par défaut, le modèle des Web Services propose une architecture de service qui ne met pas en corrélation plusieurs appels provenant d'un même utilisateur. Chaque appel provoque la création de l'objet nécessaire pour répondre à la requête et cet objet est détruit aussitôt la demande satisfaite. La liaison entre le client et le serveur ne dure donc que le temps requis à l'exécution d'une méthode exposée. Ce type de liaison des services Web, dit à couplage faible, n'est guère favorable au développement d'applications importantes et très sollicitées. Les services Web sont simples à mettre en œuvre et en privilégiant les standards SOAP et HTTP, ils permettent des échanges faciles entre des technologies hétérogènes. Un Service Web ASP.NET ne peut être installé que sur un serveur IIS de Microsoft. Le Net Remoting n'est autre que le développement d’applications traditionnelles dites distribuées qui exploitent la technologie lourde des architectures client/serveur telles que DCOM (Distributed Component Object Model de Microsoft), CORBA (Common Object Request Broker Architecture de Object Management Group) ou RMI (Remote Method Invocation de Sun). Le Net Remoting est une architecture client/serveur de Microsoft, parfaitement intégrée dans la technologie DotNet. Les applications serveurs et clients peuvent être installées sur des systèmes non serveurs. Le Net Remoting permet la mise en corrélation de plusieurs appels provenant d'un même utilisateur, notamment en fonction du mode d'activation des objets. Les objets activés SingleCall sont sans état (comme ceux des Web Services) et ceux activés Singleton conservent le même état pour tous les clients. Les objets activés par les clients peuvent alors conserver leur état. La liaison entre le client et le serveur, qui peut donc être permanente, est dite à couplage fort. Ce type de liaison permet le développement d'applications importantes nécessitant l'usage d'outils tels que les transactions et les levées d'exceptions, ainsi que d'autres ordinairement exploités dans des développements non distribués. Le développeur dispose ainsi d’un meilleur contrôle du comportement de ses applications et des éventuels disfonctionnements inhérents à celles-ci et aux environnements. Il a notamment la possibilité d'optimiser les coûts des traitements en les délégant aux ressources disponibles, c'est-à-dire en répartissant les différents traitements sur différents sites en fonction de leurs coûts et disponibilités. Par l'usage de protocoles binaires légers (tel que TCP), le Net Remoting permet une meilleure exploitation des ressources réseaux et une optimisation du flux.

Fonctionnement du Net Remoting Le principe du Net Remoting est d'activer un objet coté serveur et de permettre à un client d'en récupérer la référence d'instance. L'objet peut alors être exploité comme s'il était local. La mise en place d'un système Net Remote comporte les étapes essentielles suivantes : Création d'un objet commun aux deux

applications (client et serveur) correspondant à l'objet publié (classe ou interface);

Création d'un programme s'exécutant sur le serveur et permettant de référencer l’objet publié pour permettre son accès à distance (création du Remoting coté serveur);

Création d'un canal de communication entre client et serveur;

Création du Remoting dans l'application cliente en enregistrant dans sa configuration le type d’objet à récupérer et les différentes options disponibles;

Création d'une instance chez le client de l’objet publié. En créant cette instance le Framework crée automatiquement un proxy qui est en fait la référence à l’objet publié. Ce proxy est l’interlocuteur entre le client et l’instance distante et c’est lui qui transmet les différents appels vers le serveur par le biais du canal de communication. Cet objet est dit marshal by reference parce que l’instance n’est pas copiée dans le domaine de l’application cliente, mais seulement référencée. Le proxy permet d’utiliser les objets MarshalByRef comme s’ils étaient en local en re-routant les appels faits sur ceux-ci vers le serveur. Le proxy représente la référence physique alors que la véritable instance, activée sur le serveur, est considérée comme une référence logique.

Client Serveur

Proxy

Objet distribué

Canal de communication Remoting Remoting

Objet référencé

Marshal by reference

Page 323: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 30

Programmation du Net Remoting Concrètement, avant de commencer la programmation d'un objet distribué et d'une application cliente, il convient de recenser les outils de développement nécessaires. La batterie usuelle d'Imports des applications doit être complétée de la désignation de l'espace de noms System.Runtime.Remoting.Channels ainsi que du System.Runtime.Remoting.Channels.Tcp ou du System.Runtime.Remoting.Channels.Http selon le mode de communication choisi. Les applications doivent également référencer System.Runtime.Remoting, ainsi qu'un Interface créé conjointement aux applications et qui a pour utilité de présenter les méthodes exposées par l'objet publié. Création d'une application serveur élémentaire Imports System Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp ' Imports System.Runtime.Remoting.Channels.Http (Tcp pour cet exemple) Module SimpleServeur Public Sub Main() Try ' Création d'un nouveau canal d'écoute sur le port 3381 Dim Canal As TcpChannel = New TcpChannel(3381) ' Dim channel As HttpChannel = New HttpChannel(3381) (Tcp pour cet exemple) ' Enregistrement du canal dans l'annuaire ChannelServices.RegisterChannel(Canal, False) ' True pour activer les paramètres de ' Démarrage de l'écoute en Singleton ' sécurité du canal, sinon False RemotingConfiguration.RegisterWellKnownServiceType(Nothing, Nothing,

WellKnownObjectMode.Singleton) Console.WriteLine("Le serveur a démarré avec succès") Catch Console.WriteLine("Erreur lors du démarrage du serveur") End Try Console.WriteLine("<Enter> pour arrêter le serveur") Console.ReadLine() End End Sub End Module Ce programme démarre bien comme serveur, mais il n'est en fait à l'écoute d'aucun appel et il ne possède d'ailleurs aucun élément de réponse à présenter. Il ne représente que l’esquisse d'un serveur. Pour transformer ce programme en véritable serveur, il faut lui désigner les services à exposer et fournir à l'application cliente le moyen de prendre connaissance de ces services. Il faut bien entendu programmer aussi les méthodes de ces services. La création d'un Interface présentant les services fournit le type commun aux applications cliente et serveur de ces services. Les méthodes programmées côté serveur doivent obligatoirement implémenter les services ainsi présentés et l'application cliente peut choisir la méthode qui lui convient à un moment donné, dès lors qu'elle référence cet Interface. La création de cet Interface nommé ici InterFaceServeur débute par la création d'un projet de type Bibliothèque de classes dans lequel le mot Class est remplacé par le mot Interface. Le code suivant permet de doter le serveur de deux méthodes UneChaine et UneReponse. Public Interface InterFaceServeur Function UneChaine() As String Function UneReponse(ByVal Chaine As String) As String ' … et tous les autres services souhaitables … End Interface L'Interface compilé donne une dll qui doit être référencée dans les projets serveurs et clients qui souhaitent exploiter ces services. Il convient maintenant d'ajouter à l'application serveur, une classe nommée ici Reponses qui implémente les méthodes annoncées dans InterFaceServeur et définit ainsi l’objet publié. Cette classe doit hériter de MarshalByRefObject.

Page 324: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 31

Public Class Reponses Inherits MarshalByRefObject Implements InterFaceServeur.InterFaceServeur ' Indique que l'objet a une durée de vie illimitée Public Overrides Function InitializeLifetimeService() As Object Return Nothing End Function ' Définition des méthodes présentées dans l'Interface Public Function UneChaine() As String

Implements InterFaceServeur.InterFaceServeur.UneChaine Console.WriteLine("Appel client sur UneChaine()") ' Sortie sur le serveur Return "Voici une chaîne" ' Sortie vers le client End Function Public Function UneReponse(ByVal Chaine As String) As String

Implements InterFaceServeur.InterFaceServeur.UneReponse Console.WriteLine("Appel client sur UneReponse(" & Chaine & ")") Return "La chaîne est " & Chaine End Function End Class Il est maintenant aisé de compléter le programme serveur précédent de sorte qu'il expose réellement les méthodes de type InterFaceServeur programmées dans la classe Reponses. L'application serveur compilée peut être installée sur un site de la même manière qu'un service Web. Simplement, il faut que cette application soit démarrée pour qu'une l'application cliente puisse utiliser ses services. L'installation peut être de type XCopy du contenu du dossier bin de développement. Imports System Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp Module SimpleServeur Public Sub Main() Try Dim Canal As TcpChannel = New TcpChannel(3381) ChannelServices.RegisterChannel(Canal, False) ' Démarrage de l'écoute en exposant l'objet Reponses en Singleton sous le nom MesMethodes RemotingConfiguration.RegisterWellKnownServiceType(GetType(Reponses),

"MesMethodes", WellKnownObjectMode.Singleton) Console.WriteLine("Le serveur a démarré avec succès") ' Sortie sur le serveur Catch Console.WriteLine("Erreur lors du démarrage du serveur") ' Sortie sur le serveur End Try Console.WriteLine("<Enter> pour arrêter le serveur") ' Sortie sur le serveur Console.ReadLine() End End Sub End Module Création d'une application cliente L'application cliente doit disposer de l'Interface de présentation des services du serveur pour la référencer et pouvoir ensuite en exploiter les méthodes dans son code. Pour l'exemple, un projet TestSimpleServeur de type application Console référence InterFaceServeur et contient le module UnModule dont voici le code. Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp ' ou .Http (selon le serveur)

Page 325: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 32

Module UnModule Dim Operation As InterFaceServeur.InterFaceServeur Private Sub InitialisationDeLaLiaisonAuServeur() Try ' Instanciation d'un canal de communication dont le port ne doit pas être spécifié ici Dim Canal As TcpChannel = New TcpChannel ' Dim channel As HttpChannel = New HttpChannel (Tcp pour cet exemple) ' Enregistrement du canal dans l'annuaire ChannelServices.RegisterChannel(Canal, False) ' Récupération des références des méthodes par lecture de l’interface du côté serveur.

' InterFaceServeur présentées sous le nom MesMethodes. ' Tcp:// doit être remplacé par Http:// si communication Http.

Operation = CType(Activator.GetObject(GetType(InterFaceServeur.InterFaceServeur), "Tcp://10.0.0.2:3381/MesMethodes"), InterFaceServeur.InterFaceServeur)

Catch Console.WriteLine("Erreur de connexion au serveur") End Try End Sub Sub Main() InitialisationDeLaLiaisonAuServeur() Try If Operation IsNot Nothing Then ' Sorties chez le client Console.WriteLine(Operation.UneChaine) ' Affiche : Voici une chaîne Console.WriteLine(Operation.UneReponse("Azerty")) ' Affiche : La chaîne est Azerty End If Catch e As Exception Console.WriteLine("Erreur : " & e.Message) End Try Console.ReadLine() End Sub End Module Démarrage d'une application serveur par une application cliente L'application cliente précédente ne peut bénéficier des services de l'application serveur que si cette dernière est effectivement démarrée. L'usage d'un service Web habilité à lancer et à arrêter l'application serveur permet à une application cliente d'être indépendante des opérateurs systèmes du serveur. Le service Web démarre et arrête une instance de l’application serveur à la demande de l’application cliente. Imports System.Web Imports System.Web.Services Imports System.Web.Services.Protocols Imports System.Diagnostics <WebService(Name:="PiloteServeur", Namespace:="SimpleServeur")> _ <WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _ <Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _ Public Class Service Inherits System.Web.Services.WebService <WebMethod()> _ Public Function Demarrage() As String ' Démarrage du serveur Dim IdProcessus As Integer Try ' DesServices est un dossier virtuel sur l’IIS IdProcessus = Shell(Me.Server.MapPath("DesServices") & "\..\SimpleServeur.exe") Catch e As Exception Return e.Message End Try Return IdProcessus.ToString End Function

Page 326: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBWEB - 33

<WebMethod()> _ Public Sub Arret(ByVal N As Integer) ' Arrêt du serveur Dim P As Process Try For Each P In Process.GetProcesses If P.Id = N Then P.Kill() Next Catch End Try End Sub End Class ' Imports … Module UnModule Dim Operation As InterFaceServeur.InterFaceServeur Dim IdProcessus As Integer = 0 ' Mémoriser l'Id retourné par Shell Private Sub InitialisationDeLaLiaisonAuServeur() Dim PS As New PiloteServeur.PiloteServeur If Operation IsNot Nothing Then IdProcessus = CType(PS.Demarrage(), Integer) If IdProcessus > 0 Then Console.WriteLine("Serveur démarré") End If End If Try Dim Canal As TcpChannel = New TcpChannel ChannelServices.RegisterChannel(Canal, False) Operation = CType(Activator.GetObject(GetType(InterFaceServeur.InterFaceServeur),

"Tcp://10.0.0.2:3381/MesMethodes"), InterFaceServeur.InterFaceServeur) Catch Console.WriteLine("Erreur de connexion au serveur") Exit Sub End Try End Sub Private Sub FinDeLaLiaisonAuServeur() If IdProcessus > 0 Then Try Dim PS As New PiloteServeur.PiloteServeur PS.Arret(IdProcessus) Catch Console.WriteLine("Erreur à l'arrêt du serveur") Exit Sub End Try Console.WriteLine("Serveur arrêté") End If End Sub Sub Main() InitialisationDeLaLiaisonAuServeur() ' Initialiser la communication Try If Operation IsNot Nothing Then Console.WriteLine(Operation.UneChaine) Console.WriteLine(Operation.UneReponse("Azerty")) End If Catch e As Exception Console.WriteLine("Erreur !" & " " & e.Message) End Try Console.ReadKey() FinDeLaLiaisonAuServeur() ' Arrêt du serveur Console.ReadKey () End Sub End Module

Le service Web publié sur le site de l'application serveur est ensuite référencé dans l'application cliente qui peut alors utiliser sa méthode Demarrage. L'application cliente doit conserver le PID du processus serveur qu’elle a démarré de sorte à pouvoir l’arrêter ultérieurement par l’usage de la méthode Arret du service Web.

Page 327: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 1

Compléments divers en

VB.Net

Page 328: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 2

Tables des matières des pages VBCD Le Basic : son histoire en un coup d’œil ............................................................................................................................. 3 Le Visual Studio et "son" framework ................................................................................................................................. 3 Améliorations du langage à partir de Visual Basic 2005 .................................................................................................... 4

Introduction .............................................................................................................................................................. 4 My ............................................................................................................................................................................ 4 Les commentaires XML ........................................................................................................................................... 6 Génériques ................................................................................................................................................................ 7 Instruction Using ...................................................................................................................................................... 8 Instruction Continue ................................................................................................................................................. 8 Mot Global ............................................................................................................................................................... 8 Opérateur IsNot ........................................................................................................................................................ 9 Instruction TryCast ................................................................................................................................................... 9 Surcharge des opérateurs et opérateurs de conversion ............................................................................................. 9 Accessibilité des mécanismes d'accès aux propriétés .............................................................................................. 9 Limites explicites des tableaux ................................................................................................................................. 9 Accesseurs aux événements personnalisés ............................................................................................................. 10 Types partiels ......................................................................................................................................................... 10 Événements de niveau application ......................................................................................................................... 11 Avertissements du compilateur .............................................................................................................................. 11 Nouveaux types numériques .................................................................................................................................. 11 Instances par défaut ................................................................................................................................................ 12

La documentation du code source .................................................................................................................................... 13 Commentaires XML ............................................................................................................................................... 13 Les commentaires ordinaires .................................................................................................................................. 14 Les commentaires spécialisés ................................................................................................................................. 14 Les signets .............................................................................................................................................................. 15

Usage de fichiers de paramètres ....................................................................................................................................... 16 La lecture de fichiers XML .................................................................................................................................... 16 Le fichier ressource programmé ............................................................................................................................. 17 Synthèse des fichiers de paramètres ....................................................................................................................... 17

Fichier de ressources ajouté à l’espace My ................................................................................................... 17 Fichier de paramètres ajouté à l’espace My .................................................................................................. 17 Fichier XML de paramètres ......................................................................................................................... 17 Fichier de paramètres programmé ................................................................................................................ 17

L’empaquetage ................................................................................................................................................................. 18

Page 329: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 3

Le Basic : son histoire en un coup d’œil

1964 Le langage Basic, dérivé du Fortran, est un langage compilé sur gros système. Fin des ‘70 Arrivée de la micro informatique.

Versions interprétées du langage. L’interpréteur est stocké en ROM. Souvent, il est à la fois le langage de programmation et le langage du système.

1979 Elaboration du projet IBM PC (Personnal Computer). Microsoft produit l’OS (MS-Dos) et l’interpréteur Basic pour ces PC. Versions : Basica et GWBasic.

Fin des ‘80 Jusque fin ‘90

Quick Basic Environnement de développement pour DOS. (Editeur de code source, débogueur, éditeur de liens, compilateur)

1991 Visual Basic (VB 1.0 à 6.0) Environnement de développement pour Windows. 2000 à 2003 Maturation de Visual Studio

qui inclut VB.Net (VB 7.0 et 7.1) L’environnement DotNet repose sur un Framework qui doit être supporté par l’OS (Operating System). Les différentes versions de Visual Studio, et donc de VB.Net, sont essentiellement des adaptations aux versions successives de Windows.

2006 Version 2005 (VB 8.0) 2007 Version 2008 (VB 9.0) 2009 Version 2010 (VB … ) … … …

Le Visual Studio et "son" framework

Visual Studio

Applications Système, Console, Windows, Web, …, en code

CIL ou MSIL (Microsoft …)

Framework (intégré à l’OS)

FCL Framework Class

Library

CLR Common Language

Runtime

JIT Just-In-Time

Environnement de

développement multi langages (VB, C#, C++, …)

Compilateur CIL Common Intermediaire Language

FCL fournit les librairies d’objets exploitables par l’OS.

JIT fournit le code exécutable, fonction par fonction, juste au moment opportun.

CLR gère l’utilisation de la mémoire par les différents codes en exécution.

Page 330: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 4

Améliorations du langage à partir de Visual Basic 2005 Le texte suivant présente sommairement les principales améliorations apportées après Visual Basic 2003 illustrées de quelques extraits de code. Certaines nouvelles fonctionnalités qui sont étudiées dans le cours, ou présentées plus loin dans ces pages, ne sont pas détaillées ici.

Introduction Les principales modifications du langage Visual Basic concernent le mot-clé My, les commentaires XML et les génériques. Plusieurs instructions nouvelles, notamment Using, Continue et TryCast, ainsi que le mot-clé Global, comblent certaines lacunes sur le plan logique. Par ailleurs, des améliorations structurelles ont été apportées concernant l'accessibilité des mécanismes d'accès aux propriétés, les accesseurs aux événements personnalisés, les types partiels et les événements de niveau application. La surcharge des opérateurs et les opérateurs de conversion sont des nouveautés, ainsi que l'opérateur IsNot. D'autres modifications utiles portent notamment sur l’ajout de types numériques, les instances par défaut des formulaires, les avertissements du compilateur.

My Les objets d'environnement et d'application communément utilisés figurent dans la bibliothèque de classes du Framework et il n’est pas aisé d’y retrouver une classe donnée. La nouvelle hiérarchie My de Visual Basic 2005 accélère et simplifie le développement d'applications en permettant un accès pratique et logiquement organisé aux nombreuses classes souvent utilisées. L’espace de noms My est essentiellement un ensemble de raccourcis pour accélérer l'accès aux classes couramment utilisées. Les raccourcis My de niveau supérieur, tels que My.Application, My.Computer, My.User, … contiennent chacun leur propre hiérarchie d'objets. Par exemple, My.User expose notamment Audio, FileSystem, Keyboard, Mouse, Network, Registry.

MessageBox.Show(My.User.Name()) MessageBox.Show(My.Computer.Info.OSFullName) MessageBox.Show(My.Computer.FileSystem.CurrentDirectory) MessageBox.Show(My.Application.CommandLineArgs.Count.ToString)

L’espace de noms My contient les classes MyApplication et MySettings, ainsi qu’un module Resources qui expose notamment les informations de cultures et des informations concernant les ressources ajoutées par le programmeur. La classe MySettings contient sous forme de propriétés fortement typées, des valeurs ajoutées au projet par le programmeur. Ces valeurs sont accessibles dans le code sous forme de propriétés en lecture seule ou non selon qu’elles ont reçu une portée de niveau Application ou de niveau Utilisateur. La classe MySettings permet en outre de programmer des réponses à des événements émis lors d’actions sur les paramètres. Ces classes sont automatiquement actualisées par VB.Net lorsque sont ajoutés des formulaires, des ressources, des paramètres ou des services Web dans le projet. Les accès à My.Resources et à My.Settings se font respectivement par un double-clic sur le fichier Resources.resx ou sur le fichier Settings.settings dans l’explorateur de solutions. Ces clics ouvrent en fait la fenêtre des propriétés du projet sur les onglets Ressources ou Paramètres selon le cas. L’illustration ci-dessous montre l’ajout de deux ressources, une image existante et une chaîne de caractères.

Page 331: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 5

Après l’ajout de ces ressources, l’explorateur de solutions est doté d’un dossiers Resources contenant l’image ajoutée et la visualisation du fichier Resources.Designer.vb montre que les informations correspondant aux ressources ajoutées sont disponibles sous forme de propriétés en lecture seule.

Friend ReadOnly Property ArbresEnneiges() As System.Drawing.Bitmap Get Dim obj As Object = ResourceManager.GetObject("ArbresEnneiges",

resourceCulture) Return CType(obj,System.Drawing.Bitmap) End Get End Property Friend ReadOnly Property ChaineRessource() As String Get Return ResourceManager.GetString("ChaineRessource", resourceCulture) End Get End Property

Après l’ajout des paramètres tels qu’illustrés ci contre, le fichier Settings.Designer.vb contient les valeurs ajoutées sous forme de propriétés en lecture seule ou non selon la portée définie. L’onglet Afficher le code ouvre la fenêtre de programmation présentée ci-dessous. C’est là que le programmeur peut ajouter des membres idéalement Friend au projet et programmer les réponses événementielles. <Global.System.Configuration.UserScopedSettingAttribute(), _ Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _ Global.System.Configuration.DefaultSettingValueAttribute("Test de nom publique")> _

Public Property NomPublique() As String Get Return CType(Me("NomPublique"),String) End Get Set Me("NomPublique") = value End Set End Property

<Global.System.Configuration.UserScopedSettingAttribute(), _ Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _ Global.System.Configuration.DefaultSettingValueAttribute("Test de nom privé")> _

Public ReadOnly Property NomPrive() As String Get Return CType(Me("NomPrive"),String) End Get End Property

<Global.System.Configuration.UserScopedSettingAttribute(), _ Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _ Global.System.Configuration.DefaultSettingValueAttribute("2007")> _

Public Property UnAutre() As UInteger Get Return CType(Me("UnAutre"),UInteger) End Get Set Me("UnAutre") = value End Set End Property

Page 332: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 6

Une fois ces ressources et propriétés ajoutées, elles sont disponibles pour le programmeur dans l’ensemble de son projet. Public Class FBase Dim UnNomPrive As String Dim UnNomPublique As String Dim UnEntier As UInt32 Dim UneRessourceChaine As String Private Sub FBase_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)

Handles MyBase.Load UnNomPrive = My.Settings.NomPrive UnNomPublique = My.Settings.NomPublique UnEntier = My.Settings.UnAutre UneRessourceChaine = My.Resources.ChaineRessource PictureBox1.Image = My.Resources.ArbresEnneiges MessageBox.Show(UnNomPublique) MessageBox.Show(UnNomPrive) MessageBox.Show(UnEntier) MessageBox.Show(UneRessourceChaine) My.Settings.NomPublique = "Nouveau nom publique" ' OK : portée utilisateur My.Settings.UnAutre = 1234 ' OK : portée utilisateur ' My.Settings.NomPrive = "Nouveau nom privé" ' Interdit : portée application ' My.Resources.ChaineRessource = "Nouvelle chaîne ressource" ' Interdit : ressource ' My.Resources.ArbresEnneiges = "X:\MesImages\UneImage.jpg" ' Interdit : ressource End Sub End Class

Les commentaires XML Les commentaires XML, qui étaient déjà à la disposition des programmeurs C# sous les versions précédentes de Visual Studio, sont une nouveauté en Visual Basic 2005. Ils sont expliqués plus loin dans ces pages sous le titre "La documentation du code source".

Page 333: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 7

Génériques Le Framework fournit désormais l'espace de noms System.Collections.Generic, qui contient plusieurs classes définissant des collections dites génériques, c'est-à-dire sans type prédéfini, dont tous les objets seront d’un même type, celui déclaré par l’utilisateur. Les génériques permettent un gain de temps considérable notamment pour la constitution de collections personnalisées. Dans Visual Basic, les génériques permettent de créer l'équivalent d'une collection personnalisée avec une ligne de code. Les principales collections génériques proposées ont été exposées dans le cours.

Dim MesPersonnes As New System.Collections.Generic.List(Of Personne) Voici par exemple une classe Personne contenant deux propriétés publiques, présentées ici en infraction des règles de la POO par souci de brièveté, sous forme de variables publiques. Public Class Personne Implements IComparable Public Nom As String Public Age As Integer Public Sub New(ByVal NomPers As String, ByVal AgePers As Decimal) Nom = NomPers Age = AgePers End Sub Public Function CompareTo(ByVal UnObjet As Object) As Integer

Implements IComparable.CompareTo Dim P As Personne = CType(UnObjet, Personne) If Age > P.Age Then Return 1 If Age < P.Age Then Return -1 Return 0 End Function End Class L’application cliente de la classe Personne peut constituer aisément une liste de personnes et dispose même de l’outil de comparaison adapté, la méthode CompareTo disponible ici du fait de l’implémentation de l’Interface IComparable dans la classe. Private Sub UneProcedure() Dim Permutation As Boolean Dim MesPersonnes As New System.Collections.Generic.List(Of Personne) Dim P As Personne ' Remplissage de la liste P = New Personne("Nom 1", 10) MesPersonnes.Add(P) P = New Personne("Nom 2", 20) MesPersonnes.Add(P) P = New Personne("Nom 3", 30) MesPersonnes.Add(P) P = New Personne("Nom 4", 40) MesPersonnes.Add(P) Do ' Tri décroissant sur les âges Permutation = False For i As Integer = 0 To MesPersonnes.Count - 2 If MesPersonnes(i).CompareTo(MesPersonnes(i + 1)) = -1 Then P = MesPersonnes(i) MesPersonnes(i) = MesPersonnes(i + 1) MesPersonnes(i + 1) = P Permutation = True End If Next Loop While Permutation For i As Integer = 0 To MesPersonnes.Count – 1 ' Affichage UneListBox.Items.Add(MesPersonnes(i).Nom & " " & MesPersonnes(i).Age) Next End Sub

Page 334: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 8

Instruction Using L'instruction Using est un raccourci pour obtenir un objet, exécuter le code qui l'accompagne et le libérer immédiatement. Avec un certain nombre d'objets du Framework, tels que des graphiques, des gestionnaires de fichiers, des ports de communications et des connexions SQL, il convient de libérer les objets créés au plus tôt pour éviter des déperditions de mémoire dans les applications. L'instruction Using fait automatiquement le nécessaire. Cette instruction ne peut s’employer qu’avec des objets implémentant IDisposable.

Dim ChaineDeConnexion As String = "Provider=Microsoft.JET.OLEDB.4.0; Data Source= X:\MesDonnees\MaBaseAccess.mdb;"

Using UneConnexion As New System.Data.OleDb.OleDbConnection(ChaineDeConnexion) ' Utilisation d’une connexion temporaire … End Using

Instruction Continue L'instruction Continue saute à l'itération suivante d'une boucle, ce qui rend la logique de cette dernière plus concise et plus facile à lire. L'instruction Continue s’utilise dans les boucles For, While et Do.

Dim j As Integer Dim Produit As Integer For i As Integer = 0 To 100 j = 0 While j < i Produit = i * j If Produit > 5000 Then Continue For ' Ignore le reste du code contrôlé et passe End If ' directement à l’instruction Next j += 1 End While Next

Mot Global Le mot-clé Global permet d'accéder à l'espace de noms racine, tout en haut de la hiérarchie d'espaces de noms. Jusqu'à présent, il était impossible de définir un espace de noms System dans la hiérarchie d'espaces de noms à moins de s’exposer à confondre les références de cet espace avec celles de l’espace de même nom existant dans le Framework.

Namespace MonEspace.System Public Class UneClasse Public Sub UneProcedure() System.Console.WriteLine("Bonjour") End Sub End Class End Namespace

Alors que le code précédent génère l’erreur présentée en illustration, le code suivant enlève toute ambiguïté au niveau des espaces de noms.

Namespace MonEspace.System Public Class UneClasse Public Sub UneProcedure() Global.System.Console.WriteLine("Bonjour") End Sub End Class End Namespace

Page 335: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 9

Opérateur IsNot Le nouvel opérateur logique IsNot vient en contrepartie de l'opérateur Is pour permettre une programmation plus cohérente, mais surtout plus lisible.

If Mobj IsNot Nothing Then … ' Forme plus lisible que la suivante If Not MObj Is Nothing Then …

Instruction TryCast Dans Visual Basic 2003, il existe deux possibilités pour convertir un type d'objet dans un autre :

Dim MObj As MonObjet MObj = CType(UnObjetQuelconque, MonObjet) MObj = DirectCast(UnObjetQuelconque, MonObjet)

C’est généralement CType qui est utilisé pour convertir un type en un autre, car DirectCast nécessite qu'il y ait une relation d'héritage ou d'implémentation entre les objets. Mais dans les deux cas, une exception est levée à l'exécution si UnObjetQuelconque ne peut pas être converti dans le type MonObjet. La nouvelle instruction TryCast, qui ne peut opérer qu’avec des paramètres du type référence, s’utilise comme CType ou DirectCast, mais renvoie Nothing si la conversion est impossible à réaliser.

MObj = TryCast(UnObjetQuelconque, MonObjet) If MObj IsNot Nothing Then ' Utilisation de MObj End If

Surcharge des opérateurs et opérateurs de conversion La surcharge des opérateurs est sans conteste le meilleur apport de VB.Net 2005. Elle complète la panoplie des moyens de programmation orientée objet en VB.Net. Cette fonctionnalité qui est expliquée dans le cours n’est plus détaillée ici.

Accessibilité des mécanismes d'accès aux propriétés Les routines Get et Set d’accès aux propriétés doivent en VB.Net 2003, avoir la même portée (Public, Friend ou Private). Dans Visual Basic 2005, les accesseurs Get et Set peuvent avoir des paramètres d'accessibilité différents, pour autant que Set soit plus restrictif que Get.

Dim XLocale As Integer Public Property X() As Integer Get Return XLocale End Get Private Set(ByVal Valeur As Integer) XLocale = Valeur End Set End Property

Ceci s'avère utile dans les environnements de développement en équipe ainsi que pour le programmeur qui souhaite réutiliser son code au maximum.

Limites explicites des tableaux Il est maintenant possible de déclarer des tableaux en utilisant des limites explicites comme cela se faisait en VB6. Toutefois, la limite inférieure ne pouvant être différente de zéro, cette nouveauté est parfaitement inutile et elle ne peut servir à la récupération d’un code VB6 qui utiliserait des bornes inférieures différentes de zéro. Ainsi, Dim T1(10) As Integer et Dim T1(0 To 10) As Integer sont deux déclarations strictement équivalentes, le seconde étant plus lisible pour qui ne sait pas que la limite inférieure est toujours 0.

Page 336: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 10

Accesseurs aux événements personnalisés Les accesseurs aux événements permettent de définir un événement personnalisé et de contrôler ce qui se produit lorsque des clients ajoutent ou suppriment des gestionnaires et déclenchent l'événement en question. En VB.net 2003, les événements se déclarent dans la classe émettrice selon une des deux formules suivantes :

Public Class MesEvenements Public Event Evenement1() ' Forme conventionnelle ou non Public Event Evenement2 As EventHandler ' Forme conventionnelle seulement

Une troisième formule est offerte sous VB.Net 2005 :

Public Custom Event Evenement3 As EventHandler Quand cette dernière formule est choisie, un <Enter> à la fin de la ligne provoque la création d’un prototype d’accesseurs aux routines AddHandler, RemoveHandler et RaiseEvent de l’événement par l’environnement de développement, de la même manière que cela se fait pour les routines Get et Set des propriétés.

Public Custom Event Evenement4 As EventHandler AddHandler(ByVal value As EventHandler) ' Code éventuellement nécessaire sur l’ajout d’un gestionnaire d’événements par

' l’application cliente de la classe MesEvenements End AddHandler RemoveHandler(ByVal value As EventHandler) ' Code éventuellement nécessaire sur la suppression d’un gestionnaire

' d’événements par l’application cliente de la classe MesEvenements End RemoveHandler RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs) ' Code éventuellement nécessaire sur le déclenchement de l’événement par

' l’application cliente de la classe MesEvenements End RaiseEvent End Event

Cette nouvelle formule de déclaration d’événements permet au programmeur d’entreprendre des contrôles et actions, comme des sauvegardes d’informations par exemple, spécifiques aux manipulations que l’application cliente effectue sur les événements.

Types partiels Les types partiels permettent la définition d'un type unique dans plusieurs fichiers sources. La définition du type a une déclaration normale dans un fichier :

Public Class TestTypePartiel ' Implémentation … End Class

et la même déclaration flanquée du mot clé Partial dans d’autres fichiers :

Partial Public Class TestTypePartiel ' Implémentation … End Class

Le mot-clé Partial indique au compilateur qu’un code est la suite de la définition d’une classe d'origine. Le code des classes générées par les concepteurs de l’environnement de développement, notamment par le Concepteur Windows Form, est marqué à l'aide du mot-clé Partial et séparé du code utilisateur. Par exemple, l’ajout d’un Windows Form FTest produit un nouveau noeud nommé FTest.vb dans l’arborescence de l’explorateur de solutions. Sous ce nœud se trouve aussi FTest.Designer.vb. Le fichier FTest.vb est la partie de la classe FTest que doit traiter le programmeur et le fichier FTest.Designer.vb est la partie réservée au Concepteur Windows Form.

Page 337: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 11

Dans des équipes de projet, l'existence de classes partielles permet de donner à chaque développeur un fichier contenant la partie de la classe qui le concerne. Grâce à cette possibilité de fractionner une définition de classe en plusieurs fichiers source, les différents développeurs qui travaillent sur les mêmes fichiers source n'ont plus besoin de s'en partager l'accès.

Événements de niveau application Un nouvel ensemble d'événements de niveau application est disponible dans une classe partielle nommée MyApplication. Ces événements sont exploitables par programmation dans le fichier nommé ApplicationEvents.vb présent dans l’explorateur de solutions (si ce fichier n’est pas présent, il peut être rendu visible par le clic du bouton Afficher les événements de l’application disponible dans les propriétés du projet sous l’onglet Application). Ces événements sont Shutdown, Startup, StartupNextInstance, NetworkAvailabiltyChanged et UnhandledException. Les trois premiers sont déclenchés lorsque l'application démarre et s'arrête. Le quatrième, NetworkAvailabilityChanged, se déclenche lorsque l'état du réseau change sur la machine. Le code de réponse à l'événement UnhandledException permet de gérer une exception qui n’est prise en charge nulle part ailleurs.

Avertissements du compilateur Visual Basic prend en charge des avertissements du compilateur ou warnings, qui donnent une vision directe de ce qui peut poser un problème lors de l'exécution. Un avertissement s'affiche en vert sous le code (les erreurs sont en bleu). Les avertissements du compilateur portent notamment sur l'accès récursif à des propriétés, le chevauchement de blocs Catch ou d'instructions Case, la création d'une fonction sans valeur de retour, … . Au lieu que toutes les erreurs s'affichent dans la Liste des tâches de l'interface de développement, il existe maintenant une fenêtre Liste d'erreurs qui distinguent trois catégories de messages à l’aide d’onglets : erreurs, avertissements et messages. L'utilisation de Option Strict est à présent jugée préférable lors de la programmation avec Visual Studio. Autant que possible, mieux vaut activer Option Strict dans le code en plaçant l'instruction Option Strict On en haut de tout fichier de code (module, classe, …) individuel. L’activation peut aussi être générale pour tout un projet en paramétrant Option Strict dans ses propriétés (Projet/Propriétés/Compiler) comme cela se fait aussi pour Option Explicit et Option Compare. C’est également dans ces propriétés qu’il est possible de désigner les avertissements souhaités ou de les désactiver. L'instruction Option Strict On génère les erreurs de compilation selon différents scénarios. Option Strict marque le code lors des tentatives de conversions restrictives implicites (conversion avec perte de données) :

Dim iValeur As Integer Dim lValeur As Long iValeur = lValeur ' Erreur lValeur = iValeur ' Ok

Un avertissement est également produit lors des liaisons tardives. Un objet est à liaison tardive lorsqu'il est affecté à une variable du type Object :

Dim UnObjet As Object UnObjet.UneMethode() ' Danger à la liaison tardive

Nouveaux types numériques Les types numériques ajoutés dans VB.Net 2005 sont une version signée du type Byte existant et les versions non signées des entiers 16, 32 et 64 bits : System.SByte 1 octet -128 à 127. System.UInt16 2 octets 0 à 65 535. Synonyme autorisé : UShort

System.UInt32 4 octets 0 à 4 294 967 295. Synonyme autorisé : UInteger

System.UInt64 8 octets 0 à 18 446 744 073 709 551 615. Synonyme autorisé : ULong

Page 338: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 12

Instances par défaut L’usage d’instances par défaut pour les formulaires est à nouveau possible en VB.Net, comme cela était le cas en VB6. L’instanciation d’un formulaire membre d’un projet n’est donc plus obligatoire en VB.Net.

Dim MonFormulaireX As New UnFormulaireX ' Instanciation et ouverture MonFormulaireX.Show() ' d’un Form UnFormulaireX.Show() ' Ouverture sans instanciation My.Forms.UnFormulaireX.Show() ' Formule recommandée, mais verbeuse !

Page 339: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 13

La documentation du code source

Commentaires XML Les commentaires XML permettent une documentation structurée du code. Ils peuvent décrire divers éléments du code, y compris les classes, les champs, les méthodes et les énumérations. Ils constituent une documentation qui est présentée dans l’explorateur d’objets et dans l’aide contextuelle en cours de développement. Lors de l’utilisation d’une instance d’une classe, l’aide contextuelle de VB.Net présente la syntaxe des membres envisagés avec parfois des explications complémentaires. Par exemple, si la classe MaClasse qui contient une méthode UneProcedure, est instanciée dans la classe cliente, la recherche des méthodes de l’instance présente notamment la méthode UneProcedure assortie de son prototype comme illustré ci-dessous.

Private Sub UneProcedure() Dim UnObjet As New MaClasse UnObjet. … Public Class MaClasse Public Sub UneProcedure(ByVal Nombre As Integer) ' … End Sub End Class

En tapant trois apostrophes suivies d’un <Enter> à gauche de la ligne de déclaration de la procédure, avant le mot Public dans cet exemple, VB.Net génère un modèle de commentaire XML correspondant à la méthode. Le programmeur peut compléter ce modèle à sa guise.

''' <summary> ''' Méthode illustrative du commentaire XML ''' </summary> ''' <param name="Nombre">Nombre est un entier</param> ''' <remarks>Ne pas abuser des méthodes inutiles</remarks>

Après avoir complété le modèle XML comme ci-dessus, l’aide contextuelle agit en deux endroits. Une première fois quand est recherchée la méthode et une deuxième fois quand vient le moment d’encoder son paramètre effectif.

Quant aux informations placées entre les balises <remarks>, elles documentent les classes et leurs membres dans l’explorateur d’objets. Visual Basic génère automatiquement un fichier de documentation du code à partir des commentaires XML structurés qui sont insérés dans les fichiers sources. Il s'agit d'une fonctionnalité clé dans les environnements de travail en équipe et en entreprise. Les outils développés par un programmeur transmettent ainsi leurs propres informations à un autre programmeur qui doit les utiliser. Par ailleurs, un développeur peut créer des prototypes des méthodes qui seront nécessaires à son application et les commenter avant de les mettre en œuvre. L’aide contextuelle l’assiste ensuite au moment de la programmation de ces méthodes.

Page 340: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 14

Les commentaires ordinaires L’insertion des commentaires ordinaires utilisés depuis toujours par les programmeurs, dans n’importe quel langage, sont sûrement le premier et le plus courant des moyens de documentation du code source. Ces commentaires sont marqués en VB par le mot clé Rem, ou plus communément par l’apostrophe. Cette marque peut être placée au début d’une ligne, qui devient alors une ligne entière de commentaires, ou à la suite d’une ligne de code. Le marquage pour commentaires de plusieurs lignes (comme cela se fait en C avec les marques /* et */) n’est pas possible en VB, mais l’éditeur de Visual Studio offre un outil à cet effet. Cet outil est constitué par les deux boutons de la barre d’outils Editeur de texte (menu Affichage/Barres d’outils/Editeur de texte) mis en évidence sur l’illustration. Le clic du premier de ces boutons provoque la pose d’une marque de commentaire au début de la ligne sur laquelle se trouve le curseur ou au début de chaque ligne d’une sélection. A l’inverse, le clic du second bouton enlève une marque de commentaire.

Les commentaires spécialisés Les commentaires spécialisés se placent exactement de la même manière que les commentaires ordinaires. Leur spécialisation provient de l’association d’un mot jeton à la marque de commentaire. Les jetons Todo, Hack et Undone sont intégrés d’origine dans Visual Studio. Le programmeur peut en ajouter à sa convenance, en supprimer ou en modifier, même ceux d’origine (pour les franciser, par exemple). Le lien entre la signification du jeton et son utilisation est purement conventionnel et l’utilisateur choisit le jeton à placer à sa guise, par exemple Todo pour désigner du code à écrire, Hack pour du code supprimé car erroné et Undone pour désigner du code remplacé ou supprimé pour d’autres raisons. La gestion des jetons se fait par le menu Outils/Options/Environnement/Liste des tâches. L’illustration ci contre présente cette fenêtre de gestion des jetons après que le programmeur ait ajouté un jeton TMP. A titre d’exemple, des commentaires spécialisés ont été placés dans un extrait de code d’un module MonModule.

' UNDONE Fonction remplacée par UneFonction optimalisée ' Commentaire UNDONE ' Public Function UneFonction(ByVal N As Integer, ByVal S As String) As Integer ' Return S.Length + N ' End Function Public Function UneFonction(ByVal N As Integer, ByVal S As String) As Integer ' TODO Ecrire le code de UneFonction ' Commentaire TODO ' Return ' Commentaire ordinaire End Function ' HACK UneAutreFonction en erreur si S = 0 ' Commentaire HACK ' Public Function UneAutreFonction(ByVal N As Byte, ByVal S As Byte) As Integer ' Return N / S ' End Function Sub Main() ' TMP Dim N As New My.MySettings ' Commentaire TMP

L’intérêt de ces commentaires spécialisés serait quasi nul si l’environnement Visual Studio n’offrait pas un outil d’exploitation approprié : la liste des tâches. Celle-ci s’obtient par le menu Affichage/Liste des tâches et consiste en un tableau ordinairement présenté conjointement à la fenêtre Liste d’erreurs.

Page 341: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 15

La liste des tâches présente deux catégories de tâches dans une liste déroulante. La première, Commentaires concerne toutes les annotations faites en commentaires spécialisés et la deuxième, Tâches utilisateur, concerne des annotations directement dactylographiées dans la liste des tâches. Comme illustré ici, la catégorie Commentaires présente tous les commentaires spécialisés insérés dans le code source. La colonne Description livre chaque jeton suivi de l’annotation du programmeur. Des colonnes Fichier et Ligne apportent un complément d’information sur la localisation du commentaire. Cette localisation est cependant superflue car il suffit d’un double clic sur une ligne du tableau pour que l’éditeur positionne directement son curseur sur le commentaire concerné dans le code source. La catégorie Tâches utilisateur, offre au programmeur un mémo dans lequel il peut noter n’importe quoi. L’ajout d’une ligne dans ce mémo se commande par le clic du bouton pointé par la flèche sur l’illustration. Une première colonne (!) peut recevoir un niveau de priorité parmi Basse, Normale et Haute. Le tri sur cette colonne permet la présentation des tâches selon leur importance. La deuxième colonne est munie de cases à cocher et permet ainsi de marquer des tâches comme étant terminées tout en conservant leur trace. Bien sûr, une ligne ou une sélection de lignes peut être supprimée à la demande par le programmeur.

Les signets Le signet est ordinairement un objet qui est inséré entre les pages d’un livre pour faciliter le retour par le lecteur à certaines informations ainsi repérées. Il est donc une information de repérage ajoutée à celles déjà présentes dans le livre comme la tables de matières ou l’index, par exemple. A ce titre, il convenable de le considérer comme une documentation à part entière du code source. De plus sous Visual Studio, les principales commandes d’exploitation des signets se trouvent sur la barre d’outils Editeur de texte déjà utilisée gérer des blocs de commentaires. Le clic du premier bouton place un signet dans la marge de l’éditeur au niveau de la ligne sur laquelle se trouve le curseur. Les trois paires suivantes de boutons permettent la navigation d’un signet à l’autre dans le document en cours, dans les documents actifs, ou encore dans les documents du dossier actif. Le dernier bouton commande le retrait de tous les signets. Mais ces fonctionnalités sont complétée d’un autre outil : la fenêtre des signets. La fenêtre des signets s’obtient par le menu Affichage/Fenêtre signet et elle est ordinairement présentée en avant plan des fenêtres Liste d’erreurs et Liste des tâches. Après consultation de ces listes, un simple clic dans la feuille de code remet la fenêtre des signets à l’avant plan. La barre d’outils de cette fenêtre présente aussi les commandes d’ajout et suppression de signets, ainsi que les boutons de navigation. Deux autres boutons sont présents, l’un pour la création de dossiers de signets et l’autre pour l’activation et la désactivation de tous les signets. La création d’un dossier de signets permet au programmeur d’en regrouper à sa guise. Les signets peuvent être déplacés dans les dossiers par de simples glisser lâcher. Chaque signet présenté dans cette fenêtre est assorti d’une case à cocher par laquelle il peut être activé ou désactivé par le programmeur. Un signet désactivé n’est pas retiré de l’éditeur, mais il est ignoré lors de la navigation.

Page 342: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 16

Usage de fichiers de paramètres Plusieurs moyens sont à la disposition du programmeur pour sauvegarder les valeurs de base d’une application. Ces valeurs sont des chemins de dossiers par défaut, des noms de fichiers, des options définies au moment de l’installation, des choix effectués par l’utilisateur lors de la première exécution du programme, des libellés de boutons, d’étiquettes, de barres de titre, …, soit toutes sortes de valeurs qu’il convient généralement de régler une fois pour toutes, ou presque, et qui sont communément désignées sous le nom générique de "paramètres". Les paramètres peuvent être gérés dans n’importe quel système de fichier qui convienne au programmeur, même au sein de la base de données d’une application, et aussi comme cela a été illustré dans les pages précédentes sous le titre "Améliorations du langage dans Visual Basic 2005", par l’usage de fichiers Resource et Settings ajoutés à l’espace de nom My. Deux autres moyens sont brièvement présentés ci après.

La lecture de fichiers XML Un fichier XML doit être ajouté au projet par le menu contextuel de l’explorateur de solution Ajouter/Nouvel élément. La boîte de dialogue illustrée ci contre permet la sélection d’un Fichier XML et de le nommer comme il convient. La sélection de cet élément dans l’explorateur de solution présente le contenu du fichier au programmeur. Le fichier ne contient à ce moment que sa première ligne et il appartient au programmeur de le compléter. Ce dernier crée les balises selon sa logique et y insère les éléments qui constitueront les paramètres de son application, comme illustré ci-dessous. L’organisation des paramètres en paire clé et valeur, rend aisée la lecture des informations dans une SortedList, par exemple à l’aide de la procédure suivante.

Private Sub LitValeursInitialesXML(ByVal Balise As String, ByRef ListeValeurs As SortedList)

Dim XMLDoc As Xml.XmlDocument = New Xml.XmlDocument() XMLDoc.Load("..\..\MesParametres.XML") ' Le fichier doit être dans un dossier connu Dim JeuDeValeursXML As Xml.XmlNodeList JeuDeValeursXML = XMLDoc.DocumentElement.GetElementsByTagName(Balise) Dim Info As Xml.XmlNode For Each Info In JeuDeValeursXML ListeValeurs.Add(Info.ChildNodes(0).InnerText, Info.ChildNodes(1).InnerText) Next End Sub

L’exploitation de cette procédure pourrait être la suivante : Dim ValeursInitiales As New SortedList LitValeursInitialesXML("MesLibelles", ValeursInitiales) For Each Info As Object In ValeursInitiales Console.WriteLine(Info.key & " = " & Info.value) Next ValeursInitiales.Clear() LitValeursInitialesXML("MesDonnees", ValeursInitiales) For Each Info As Object In ValeursInitiales Console.WriteLine(Info.key & " = " & Info.value) Next Le fichier de paramètres XML est construit en cours de développement et il est ensuite lu par l’application en exécution. Il convient donc parfaitement au stockage de paramètres permanents. Il présente en outre l’avantage (mais c’est aussi parfois un inconvénient) de pouvoir être modifié à l’aide de n’importe quel éditeur de texte. Le programmeur en intervention de maintenance peut aisément modifier des valeurs de paramètres.

Page 343: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 17

Le fichier ressource programmé Le fichier de ressources présenté ici est géré par l’application en cours d’exécution, tant en écriture qu’en lecture. Il est donc adapté au stockage de paramètres dont les valeurs sont fréquemment sujettes à modifications pendant l’exploitation de l’application. En tant que fichier dynamique, il convient également au stockage de lots d’informations ordinairement traitées par paires, comme par exemple, les paires clé valeur d’une SortedList. Il faut noter que, contrairement au fichier XML, le fichier de paramètre tel que programmé ci-dessous n’est pas modifiable à l’aide d’un éditeur de texte. Sa mise en œuvre requiert l’Imports System.Resources.

' Ouverture du fichier en création Dim FParametresASauver As New

ResourceWriter("X:\VBNet\TestFichiersSpeciaux\MesParametres.cfg ") ' Enregistrement de chaque paramètre sous forme d’une clé et d’une valeur FParametresASauver.AddResource("Id1", "Une longue chaîne de caractères") FParametresASauver.AddResource("UneCle", "1234") FParametresASauver.AddResource("TitreFormBase", "Gestion des personnes") ' … ' Assure l'écriture des valeurs et ferme le fichier FParametresASauver.Close() ' Ouverture du fichier en lecture Dim FAutreLecture As New

ResourceReader("X:\VBNet\TestFichiersSpeciaux\MesParametres.cfg") ' Création d'une collection de paires Clé-Valeur par la lecture du fichier Dim MesParametres As IDictionaryEnumerator = FAutreLecture.GetEnumerator() ' Lecture de la collection et affichage des clés et valeurs correspondantes While MesParametres.MoveNext() Console.Write("Nom du paramètre = {0}", MesParametres.Key) Console.WriteLine(" Valeur = {0}", MesParametres.Value) End While ' Fermeture du fichier FAutreLecture.Close()

Synthèse des fichiers de paramètres Fichier de ressources ajouté à l’espace My

Constitué par le programmeur en cours de développement à l’aide d’un assistant. Valeurs accessibles en lecture seule par l’application en cours d’exécution. Fichier de type XML complexe accessible par tout éditeur de texte.

Fichier de paramètres ajouté à l’espace My

Constitué par le programmeur en cours de développement à l’aide d’un assistant. Valeurs de portée Utilisateur accessibles en lecture et en écriture par l’application. Valeurs de portée Application accessibles en lecture seule par l’application. Fichier de type XML complexe accessible par tout éditeur de texte.

Fichier XML de paramètres

Constitué par le programmeur en cours de développement à l’aide de l’éditeur intégré. Valeurs accessibles en lecture seule par l’application en cours d’exécution. Fichier de type XML simple accessible par tout éditeur de texte et facilement modifiable.

Fichier de paramètres programmé

Gestion programmée dans l’application. Valeurs accessibles par l’application selon les fonctionnalités programmées. Fichier non modifiable par un éditeur de texte.

Page 344: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 18

L’empaquetage La mise en service d’une application par une installation de type XCopy est bien sûr d’un grand confort. Toutefois, si elle doit être réalisée par l’utilisateur final, elle nécessite de la part de celui-ci une connaissance du système supérieure à celle requise pour le lancement d’un programme d’installation. Il est également plus aisé d’informer l’utilisateur sur le nom de l’exécutable qu’il doit lancer que de lui expliquer l’installation XCopy. Il est enfin plus professionnel de transmettre un kit d’installation à un utilisateur que de lui remettre un lot de fichiers sans lien apparent entre eux. La réalisation d’un kit d’installation se réalise par l’ajout d’un nouveau projet de type Assistant Installation à la solution de l’application.

Il suffit de suivre les étapes proposées par l’assistant pour que la majeure partie du travail soit réalisée. La première étape propose le choix entre différent type de package. L’étape suivante permet la désignation des fichiers du projet à joindre au kit d’installation. La sortie principale du projet est l’application exécutable elle-même. L’assistant permet ensuite l’ajout d’autres fichiers que le programmeur souhaite faire installer en même temps que son application, comme une base de données par exemple.

Page 345: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 19

La dernière étape proposée par l’assistant permet de vérifier les choix effectués aux étapes précédentes et de valider le projet. Il maintenant possible de procéder aux réglages éventuellement nécessaires de certaines propriétés et de définir les raccourcis souhaités pour l’utilisateur. Certaines propriétés du projet d’installation représentent des valeurs par défaut qui seront présentées au moment de l’installation. Par exemple, Manufacturer se retrouve dans le chemin du dossier d’installation qui sera proposé. Ce chemin peut être modifié dans les propriétés du Dossier d’application illustrées ci-dessous.

Les dossiers Bureau de l’utilisateur et Menu Programme de l’utilisateur sont destinés à recevoir des raccourcis vers l’application installée. Quand un de ces dossiers est ouvert, un clic du bouton droit de la souris dans la partie droite de la fenêtre permet la création du raccourci à placer dans ce dossier.

La création du raccourci s’effectue alors par la désignation du programme à lancer, soit ici la Sortie principale de Test2005Fin. Le raccourci créé est ensuite visible dans la partie droite de la fenêtre et il est possible de modifier son nom dans sa fenêtre de propriétés comme illustré ci dessous.

Page 346: VB Complet

[ phr @ skynet . be ] 15/03/2015 PHR VB.Net VBCD - 20

Le programme d’installation terminé, il faut le générer. Il convient toutefois de générer d’abord le projet de l’application en mode Release et de générer ensuite le projet d’installation, en mode Release également. Après la génération des projets, le dossier Setup demandé au début de la procédure d’empaquetage se trouve bien à l’endroit demandé. Il contient notamment un sous dossier Release dans lequel se trouve le nécessaire d’installation, un fichier Setup.exe et un fichier Setup.msi. Il suffit maintenant de transmettre ces fichiers sur l’ordinateur cible et de lancer Setup.exe pour procéder à l’installation. Le programme d’installation propose le dossier d’installation par défaut désigné précédemment dans les propriétés du Dossier d’application et il bien entendu possible d’en changer.

L’avantage du programme d’installation est bien sûr de rendre l’installation facile pour l’utilisateur, mais aussi d’offrir un excellent outil de désinstallation. Il suffit de relancer Setup.exe et de choisir l’option adéquate pour désinstaller parfaitement l’application. La désinstallation supprime le dossier de l’installation et tous ses fichiers.