• Aucun résultat trouvé

Gestion d'événements

Dans le document Télécharger cours de C# en pdf (Page 137-142)

pour fixer ou connaître la marque de fin de ligne à utiliser par la méthode WriteLine // méthodes

5.2 Gestion d'événements

A quoi peut servir un objet de type delegate ?

Comme nous le verrons dans l'exemple suivant, cela sert surtout à la gestion des événements. Une classe C1 peut générer des événements evti. Lors d'un événement evti, un objet de type C1 lancera l'exécution d'un objet evtiDéclenché de type delegate. Toutes les fonctions enregistrées dans l'objet delegate evtiDéclenché seront alors exécutées. Si un objet C2 utilisant un objet C1 veut être averti de l'occurrence de l'événement evti sur l'objet C1, il enregistrera l'une de ses méthodes C2.f dans l'objet délégué C1.evtiDéclenché de l'objet C1 afin que la méthode C2.f soit exécutée à chaque fois que l'événement evti se produit sur l'objet C1. Comme l'objet délégué C1.evtiDéclenché peut enregistrer plusieurs fonctions, différents objets Ci pourront s'enregistrer auprès du délégué C1.evtiDéclenché pour être prévenus de l'événement evti sur C1.

Considérons l'exemple suivant :

//gestion d'événements

using System;

public class myEventArgs : EventArgs{ // la classe d'un évt

// attribut

private string _saisie; // constructeur

public myEventArgs(string saisie){ _saisie=saisie;

}//constructeur

// propriété saisie en lecture seule

public override string ToString(){ return _saisie;

}//ToString

}// classe myEventArgs

public class émetteur{

// la classe émettrice d'un évt // attribut

private string _nom; // nom de l'émetteur // constructeur

public émetteur(string nom){ _nom=nom;

}//constructeur // ToString

public override string ToString(){ return _nom;

Interfaces graphiques 138

}//ToString

// le prototype des fonctions chargées de traiter l'évt

public delegate void _evtHandler(object sender, myEventArgs evt); // le pool des gestionnaires d'évts

public event _evtHandler evtHandler; // méthode de demande d'émission d'un évt

public void envoyerEvt(myEventArgs evt){ // on prévient tous les abonnés

// on fait comme si l'évt provenait d'ici evtHandler(this,evt);

}//envoyerEvt }//émetteur

// une classe de traitement de l'évt

public class souscripteur{ // attribut

private string nom; // nom du souscripteur

private émetteur sender; // l'émetteur des évts // constructeur

public souscripteur(string nom, émetteur e){ // on note le nom du souscripteur

this.nom=nom;

// et l'émetteur des évts this.sender=e;

// on s'inscrit pour recevoir les evts de l'émetteur e e.evtHandler+=new émetteur._evtHandler(traitementEvt); }//souscripteur

// gestionnaire d'évt

public void traitementEvt(object sender, myEventArgs evt){ // affichage evt

Console.Out.WriteLine("L'objet [" + sender + "] a signalé la saisie erronée [" + evt + "] au souscripteur [" + nom + "]");

}//traitement1 }// classe souscripteur // un programme de test

public class test{

public static void Main(){

// création d'un émetteur d'evts

émetteur émetteur1=new émetteur("émetteur1"); // création d'un tableau de souscripteurs // pour les évts émis par émetteur1

souscripteur[] souscripteurs=new souscripteur[] {new souscripteur("s1",émetteur1), new

souscripteur("s2",émetteur1)};

// on lit une suite d'entiers au clavier // dès que l'un est erroné, on émet un évt

Console.Out.Write("Nombre entier (rien pour arrêter) : "); string saisie=Console.In.ReadLine().Trim();

// tant que la ligne saisie est non vide while(saisie!=""){

// la saisie est-elle un nombre entier ? try{

int n=int.Parse(saisie); }catch(Exception){

// ce n'est pas un entier // on prévient tout le monde

émetteur1.envoyerEvt(new myEventArgs(saisie)); }//try-catch

// nouvelle saisie

Console.Out.Write("Nombre entier (rien pour arrêter) : "); saisie=Console.In.ReadLine().Trim(); }//while // fin Environment.Exit(0); }//Main }//classe test

Le code précédent est assez complexe. Détaillons-le. Dans une gestion d'événements, il y a un émetteur d'événements (sender) qui envoie le détail des événements (EventArgs) à des souscripteurs qui se sont déclarés intéressés par les événements en question. La classe émettrice des événements est ici la suivante :

public class émetteur{

// la classe émettrice d'un évt // attribut

private string _nom; // nom de l'émetteur // constructeur

public émetteur(string nom){ _nom=nom;

}//constructeur // ToString

Interfaces graphiques 139

return _nom; }//ToString

// le prototype des fonctions chargées de traiter l'évt

public delegate void _evtHandler(object sender, myEventArgs evt);

// le pool des gestionnaires d'évts public event _evtHandler evtHandler;

// méthode de demande d'émission d'un évt public void envoyerEvt(myEventArgs evt){ // on prévient tous les abonnés

// on fait comme si l'évt provenait d'ici

evtHandler(this,evt); }//envoyerEvt

}//émetteur

Chaque objet émetteur a un nom fixé par construction. La méthode ToString a été redéfinie de telle façon que lorsqu'on transforme un objet émetteur en string c'est son nom qu'on récupère. La classe définit le prototype des fonctions qui peuvent traiter les événements qu'elle émet :

public delegate void _evtHandler(object sender, myEventArgs evt);

Le premier argument d'une fonction de traitement d'un événement sera l'objet qui émet l'événement. Le second argument sera de type myEventArgs, un objet qui donnera des détails sur l'événement et sur lequel nous reviendrons.

Une fois le prototype (delegate) des gestionnaires d'événements défini, il nous faut un objet pour enregistrer des fonctions correspondant à ce prototype et qui seront exécutées lorsque l'objet qui les enregistre recevra les paramètres permettant de les exécuter.

public event _evtHandler evtHandler;

On notera le mot clé event qui impose au delegate d'être de type void f(object, EventArgs). Les fonctions enregistrées dans evtHandler seront exécutées par une instruction evtHandler(o,evt) où o est de type object ou dérivé et evt de type myEventArgs ou dérivé. Cette exécution ne peut se produire que dans un objet de la classe qui définit l'événement evtHandler, c.a.d. un objet de type émetteur. Afin de pouvoir déclencher un événement de l'extérieur d'un objet émetteur, nous ajoutons à la classe la méthode envoyerEvt :

// méthode de demande d'émission d'un évt

public void envoyerEvt(myEventArgs evt){ // on prévient tous les abonnés

// on fait comme si l'évt provenait d'ici evtHandler(this,evt);

}//envoyerEvt

Nous devons définir quel sera le type d'événements déclenchés par la classe émetteur. Nous avons pour cela défini la classe myEventArgs :

public class myEventArgs : EventArgs{

// la classe d'un évt // attribut

private string _saisie;

// constructeur

public myEventArgs(string saisie){ _saisie=saisie;

}//constructeur

// propriété saisie en lecture seule public override string ToString(){ return _saisie;

}//ToString

}// classe myEventArgs

Nous avons vu que les fonctions de traitement des événements devaient correspondre au prototype void f(object, EventArgs). EventArgs est une classe qui donne des informations sur l'événement qui s'est produit. Pour chaque nouveau type d'événement, on est donc amené à dériver cette classe. C'est ce qui est fait ici. Les événements qui vont nous intéresser sont des saisies au clavier erronées. On va demander à un utilisateur de taper des nombres entiers au clavier et dès qu'il tapera une chaîne qui ne représente pas un entier, on déclenchera un événement. Comme détail de l'événement, nous nous conterons de donner la saisie erronée. C'est le sens de l'attribut _saisie de la classe. Un objet myEventArgs est donc construit avec pour paramètre la saisie erronée. On redéfinir par ailleurs la méthode ToString pour que lorsqu'on transforme un objet myEventArgs en chaîne, on obtienne l'attribut _saisie. Nous avons défini l'émetteur des événements et le type d'événements qu'il émet. Nous définissons ensuite le type des souscripteurs intéressés par ces événements.

// une classe de traitement de l'évt

public class souscripteur{ // attribut

Interfaces graphiques 140 private émetteur sender; // l'émetteur des évts

// constructeur

public souscripteur(string nom, émetteur e){ // on note le nom du souscripteur

this.nom=nom;

// et l'émetteur des évts this.sender=e;

// on s'inscrit pour recevoir les evts de l'émetteur e e.evtHandler+=new émetteur._evtHandler(traitementEvt); }//souscripteur

// gestionnaire d'évt

public void traitementEvt(object sender, myEventArgs evt){ // affichage evt

Console.Out.WriteLine("L'objet [" + sender + "] a signalé la saisie erronée [" + evt + "] au souscripteur [" + nom + "]");

}//traitement1 }// classe souscripteur

Un souscripteur sera défini par deux paramètres : son nom (attribut nom) et l'objet émetteur dont il veut traiter les événements (attribut sender). Ces deux paramètres seront passés au constructeur de l'objet. Au cours de cette même construction, le souscripteur s'abonne aux événements de l'émetteur :

e.evtHandler+=new émetteur._evtHandler(traitementEvt);

La fonction enregistrée auprès de l'émetteur est traitementEvt. Cette méthode de la classe souscripteur affiche les deux arguments qu'elle a reçues (sender, evt) ainsi que le nom du récepteur (nom).

Ont été définis, le type des événements produits, le type de l'émetteur de ces événements, le type des souscripteurs. Il ne nous reste plus qu'à les mettre en oeuvre :

// un programme de test

public class test{

public static void Main(){

// création d'un émetteur d'evts

émetteur émetteur1=new émetteur("émetteur1"); // création d'un tableau de souscripteurs // pour les évts émis par émetteur1

souscripteur[] souscripteurs=new souscripteur[] {new souscripteur("s1",émetteur1), new

souscripteur("s2",émetteur1)};

// on lit une suite d'entiers au clavier // dès que l'un est erroné, on émet un évt

Console.Out.Write("Nombre entier (rien pour arrêter) : "); string saisie=Console.In.ReadLine().Trim();

// tant que la ligne saisie est non vide while(saisie!=""){

// la saisie est-elle un nombre entier ? try{

int n=int.Parse(saisie); }catch(Exception){

// ce n'est pas un entier // on prévient tout le monde

émetteur1.envoyerEvt(new myEventArgs(saisie)); }//try-catch

// nouvelle saisie

Console.Out.Write("Nombre entier (rien pour arrêter) : "); saisie=Console.In.ReadLine().Trim(); }//while // fin Environment.Exit(0); }//Main }//classe test

Nous créons un objet émetteur :

// création d'un émetteur d'evts

émetteur émetteur1=new émetteur("émetteur1");

Nous créons un tableau de deux souscripteurs pour les événements émis par l'objet émetteur1 :

// création d'un tableau de souscripteurs // pour les évts émis par émetteur1

souscripteur[] souscripteurs=new souscripteur[] {new souscripteur("s1",émetteur1), new

souscripteur("s2",émetteur1)};

Nous demandons à l'utilisateur de taper des nombres entiers au clavier. Dès qu'une saisie est erronée, nous demandons à émetteur1 d'envoyer un événement à ses souscripteurs :

// on prévient tout le monde

Interfaces graphiques 141

L'événement envoyé est de type myEventArgs et contient la saisie erronée. Les deux souscripteurs devraient recevoir cet événement et le signaler. C'est ce que montre l'exécution qui suit.

Les résultats de l'exécution sont les suivants : E:\data\serge\MSNET\c#\événements\2>evt1 Nombre entier (rien pour arrêter) : 4 Nombre entier (rien pour arrêter) : a

L'objet [émetteur1] a signalé la saisie erronée [a] au souscripteur [s1] L'objet [émetteur1] a signalé la saisie erronée [a] au souscripteur [s2] Nombre entier (rien pour arrêter) : 1.6

L'objet [émetteur1] a signalé la saisie erronée [1.6] au souscripteur [s1] L'objet [émetteur1] a signalé la saisie erronée [1.6] au souscripteur [s2] Nombre entier (rien pour arrêter) :

Accès aux bases de données 142

6. Accès aux bases de données

6.1 Généralités

Il existe de nombreuses bases de données pour les plate-formes windows. Pour y accéder, les applications passent au travers de programmes appelés pilotes (drivers).

Dans le schéma ci-dessus, le pilote présente deux interfaces : • l'interface I1 présentée à l'application

• l'interface I2 vers la base de données

Afin d'éviter qu'une application écrite pour une base de données B1 doive être ré-écrite si on migre vers une base de données B2 différente, un effort de normalisation a été fait sur l'interface I1. Si on utilise des bases de données utilisant des pilotes "normalisés", la base B1 sera fournie avec un pilote P1, la base B2 avec un pilote P2, et l'interface I1 de ces deux pilotes sera identique. Aussi n'aura-t-on pas à ré-écrire l'application. On pourra ainsi, par exemple, migrer une base de données ACCESS vers une base de données MySQL sans changer l'application.

Il existe deux types de pilotes normalisés :

• les pilotes ODBC (Open DataBase Connectivity)

• les pilotes OLE DB (Object Linking and Embedding DataBase)

Les pilotes ODBC permettent l'accès à des bases de données. Les sources de données pour les pilotes OLE DB sont plus variées : bases de données, messageries, annuaires, ... Il n'y a pas de limite. Toute source de données peut faire l'objet d'un pilote Ole DB si un éditeur le décide. L'intérêt est évidemment grand : on a un accès uniforme à une grande variété de données.

La plate-forme .NET est livrée avec deux types de classes d'accès aux données : 1. les classes SQL Server.NET

2. les classes Ole Db.NET

Les premières classes permettent un accès direct au SGBD SQL Server de Microsoft sans pilote intermédiaire. Les secondes permettent l'accès aux sources de données OLE DB.

La plate-forme .NET est fournie (mai 2002) avec trois pilotes OLE DB pour respectivement : SQL Server, Oracle et Microsoft Jet (Access). Si on veut travailler avec une base de données ayant un pilote ODBC mais pas de pilote OLE DB, on ne peut pas. Ainsi on ne peut pas travailler avec le SGBD MySQL qui (mai 2002) ne fournit pas de pilote OLE DB. Il existe cependant une série de classes permettant l'accès aux sources de données ODBC, les classes odbc.net. Elles ne sont pas livrées en standard avec le SDK et il faut aller les chercher sur le site de Microsoft. Dans les exemples qui vont suivre, nous utiliserons surtout ces classes ODBC car la plupart des bases de données sous windows sont livrées avec un tel pilote. Voici par exemple, une liste des pilotes ODBC installés sur une machine Win 2000 (Menu Démarrer/Paramètres/Panneau de configuration/Outils d'administration) :

Application Base de

données

Dans le document Télécharger cours de C# en pdf (Page 137-142)