Vous êtes sur la page 1sur 21

Livret 10

Construction dvnements
Outil utilis : C#

RM di scala

Cours informatique programmation Rm di Scala http://www.discala.net

10 : Les vnements avec

Plan gnral:

1. Construction de nouveaux vnements


Design Pattern observer Abonn un vnement Dclaration d'un vnement Invocation d'un vnement Comment s'abonner un vnement Restrictions et normalisation Evnement normalis avec informations Evnement normalis sans information

2. Les vnements dans les Windows.Forms


Contrles visuels et vnements Evnement Paint : avec information Evnement Click : sans information Code C# gnr

rm di Scala - 2006

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

Rappel Programmation oriente vnements Un programme objet orient vnements est construit avec des objets possdant des proprits telles que les interventions de lutilisateur sur les objets du programme et la liaison dynamique du logiciel avec le systme dclenchent lexcution de routines associes. Le traitement en programmation vnementielle, consiste mettre en place un mcanisme d'inteception puis de gestion permettant d'informer un ou plusieurs objets de la survenue d'un vnement particulier.

1. Construction de nouveaux vnements


Le modle de conception de l'observateur (Design Pattern observer) est utilis par Java et C# pour grer un vnement. Selon ce modle, un client s'inscrit sur une liste d'abonns auprs d'un observateur qui le prviendra lorsqu'un vnement aura eu lieu. Les clients dlguent ainsi l'interception d'un vnement une autre entit. Java utilise ce modle sous forme d'objet couteur.

Design Pattern observer


Dans l'univers des Design Pattern on utilise essentiellement le modle observateur dans les cas suivants : Quand le changement d'un objet se rpercute vers d'autres. Quand un objet doit prvenir d'autres objets sans pour autant les connaitre.

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

C# propose des mcanismes supportant les vnements, mais avec une implmentation totalement diffrente de celle de Java. En observant le fonctionnement du langage nous pouvons dire que C# combine efficacement les fonctionnalits de Java et de Delphi. Abonn un vnement C# utilise les dlgus pour fournir un mcanisme explicite permettant de grer l'abonnement/notification. En C# la dlgation de l'coute (gestion) d'un vnement est confie un objet de type dlgu : l'abonn est alors une mthode appele gestionnaire de l'vnement (contrairement Java o l'abonn est une classe) acceptant les mmes arguments et paramtres de retour que le dlgu.

Dclaration d'un vnement

Code C# :
using System; using System.Collections; namespace ExempleEvent { //--> dclaration du type dlgation :(par exemple procdure avec 1 paramtre string ) public delegate void DelegueTruc (string s); public class ClassA { //--> dclaration d'une rfrence event de type dlgu : public event DelegueTruc Truc; } }

Invocation d'un vnement


rm di Scala - 2006

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

Une fois qu'une classe a dclar un vnement Truc, elle peut traiter cet vnement exactement comme un dlgu ordinaire. La dmarche est trs semblable celle de Delphi, le champ Truc vaudra null si le client ObjA de ClassA n'a pas raccord un dlgu l'vnement Truc. En effet tre sensible plusieurs vnements n'oblige pas chaque objet grer tous les vnement, dans le cas o un objet ne veut pas grer un vnement Truc on n'abonne aucune mthode au dlgu Truc qui prend alors la valeur null. Dans l'ventualit o un objet doit grer un vnement auquel il est sensible, il doit invoquer l'vnement Truc (qui rfrence une ou plusieurs mthodes). Invoquer un vnement Truc consiste gnralement vrifier d'abord si le champ Truc est null, puis appeler l'vnement (le dlgu Truc). Remarque importante L'appel d'un vnement ne peut tre effectu qu' partir de la classe qui a dclar cet vnement.

Exemple construit pas pas Considrons ci-dessous la classe ClassA qui est sensible un vnement que nous nommons Truc (on dclare la rfrence Truc), dans le corps de la mthode void DeclencheTruc( ) on appelle l'vnement Truc. Nous dclarons cette mthode void DeclencheTruc( ) comme virtuelle et protge, de telle manire qu'elle puisse tre redfinie dans la suite de la hirarchie; ce qui constitue un gage d'volutivit des futures classes quant leur comportement relativement l'vnement Truc :

Il nous faut aussi prvoir une mthode publique qui permettra d'invoquer l'vnement depuis une autre classe, nous la nommons LancerTruc.
Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

Code C# , construisons progressivement notre exemple, voici les premires lignes du code :
using System; using System.Collections; namespace ExempleEvent { //--> dclaration du type dlgation :(par exemple procdure avec 1 paramtre string) public delegate void DelegueTruc (string s); public class ClassA { //--> dclaration d'une rfrence event de type dlgu : public event DelegueTruc Truc; protected virtual void DeclencheTruc( ) { .... if ( Truc != null ) Truc("vnement dclench"); .... } public void LancerTruc( ) { .... DeclencheTruc( ) ; .... } } }

Comment s'abonner (se raccorder, s'inscrire, ...) un vnement Un vnement ressemble un champ public de la classe qui l'a dclar. Toutefois l'utilisation de ce champ est trs restrictive, c'est pour cela qu'il est dclar avec le spcificateur event. Seulement deux oprations sont possibles sur un champ d'vnement qui rapellons-le est un dlgu : Ajouter une nouvelle mthode ( la liste des mthodes abonnes l'vnement). Supprimer une mthode de la liste (dsabonner une mthode de l'vnement).

Enrichissons le code C# prcdent avec la classe ClasseUse :


using System; using System.Collections; namespace ExempleEvent { //--> dclaration du type dlgation :(par exemple procdure avec 1 paramtre string) public delegate void DelegueTruc (string s); public class ClassA { //--> dclaration d'une rfrence event de type dlgu : public event DelegueTruc Truc; protected virtual void DeclencheTruc( ) { .... if ( Truc != null ) Truc("vnement dclench"); .... } public void LancerTruc( ) {
rm di Scala - 2006

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

.... DeclencheTruc( ) ; .... } } public class ClasseUse { static private void methodUse( ) { ClassA ObjA = new ClassA( ); ClasseUse ObjUse = new ClasseUse ( ); //.... } static public void Main(string[] x) { methodUse( ) ; //.... } } }

Il faut maintenant dfinir des gestionnaires de l'vnement Truc (des mthodes ayant la mme signature que le type dlgation " public delegate void DelegueTruc (string s); ". Ensuite nous ajouterons ces mthodes au dlgu Truc (nous les abonnerons l'vnement Truc), ces mthodes peuvent tre de classe ou d'instance. Supposons que nous ayons une mthode de classe et trois mthodes d'instances qui vont s'inscrire sur la liste des abonns Truc, ce sont quatre gestionnaires de l'vnement Truc :

Ajoutons au code C# de la classe ClassA les quatre gestionnaires :


using System; using System.Collections; namespace ExempleEvent { //--> dclaration du type dlgation :(par exemple procdure avec 1 paramtre string) public delegate void DelegueTruc (string s); public class ClassA { //--> dclaration d'une rfrence event de type dlgu : public event DelegueTruc Truc;

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

protected virtual void DclencheTruc( ) { .... if ( Truc != null ) Truc("vnement dclench") .... } public void LancerTruc( ) { .... DeclencheTruc( ) ; .... } } public class ClasseUse { public void method100(string s); { //...gestionnaire d'vnement Truc: mthode d'instance. } public void method101(string s); { //...gestionnaire d'vnement Truc: mthode d'instance. } public void method102(string s); { //...gestionnaire d'vnement Truc: mthode d'instance. } static public void method103(string s); { //...gestionnaire d'vnement Truc: mthode de classe. } static private void methodUse( ) { ClassA ObjA = new ClassA( ); ClasseUse ObjUse = new ClasseUse ( ); //... il reste abonner les gestionnaires de l'vnement Truc } static public void Main(string[ ] x) { methodUse( ) ; } } }

Lorsque nous ajoutons en C# les nouvelles mthodes method100, ... , method103 au dlgu Truc, par surcharge de l'oprateur +, nous dirons que les gestionnaires method100,...,method103, s'abonnent l'vnement Truc.

rm di Scala - 2006

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

Prvenir (informer) un abonn correspond ici l'action d'appeler l'abonn (appeler la mthode) :

Terminons le code C# associ la figure prcdente :


Nous compltons le corps de la mthode " static private void methodUse( ) " par l'abonnement au dlgu des quatre gestionnaires. Pour invoquer l'vnement Truc, il faut pouvoir appeler enfin titre d'exemple une invocation de l'vnement :

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

using System; using System.Collections; namespace ExempleEvent { //--> dclaration du type dlgation :(par exemple procdure avec 1 paramtre string) public delegate void DelegueTruc (string s); public class ClassA { //--> dclaration d'une rfrence event de type dlgu : public event DelegueTruc Truc; protected virtual void DeclencheTruc( ) { //.... if ( Truc != null ) Truc("vnement Truc dclench"); //.... } public void LancerTruc( ) { //.... DeclencheTruc( ) ; //.... } } public class ClasseUse { public void method100(string s) { //...gestionnaire d'vnement Truc: mthode d'instance. System.Console.WriteLine("information utilisateur : "+s); } public void method101(string s) { //...gestionnaire d'vnement Truc: mthode d'instance. System.Console.WriteLine("information utilisateur : "+s); } public void method102(string s) { //...gestionnaire d'vnement Truc: mthode d'instance. System.Console.WriteLine("information utilisateur : "+s); } static public void method103(string s) { //...gestionnaire d'vnement Truc: mthode de classe. System.Console.WriteLine("information utilisateur : "+s); } static private void methodUse( ) { ClassA ObjA = new ClassA( ); ClasseUse ObjUse = new ClasseUse ( ); //-- abonnement des gestionnaires: ObjA.Truc += new DelegueTruc ( ObjUse.method100 ); ObjA.Truc += new DelegueTruc ( ObjUse.method101 ); ObjA.Truc += new DelegueTruc ( ObjUse.method102 ); ObjA.Truc += new DelegueTruc ( method103 ); //-- invocation de l'vnement: ObjA.LancerTruc( ) ; //...l'appel cette mthode permet d'invoquer l'vnement Truc } static public void Main(string[] x) { methodUse( ) ; } } }

Restrictions et normalisation .NET Framework


rm di Scala - 2006

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

Bien que le langage C# autorise les vnements utiliser n'importe quel type dlgu, le .NET Framework applique ce jour des fins de normalisation, certaines indications plus restrictives quant aux types dlgus utiliser pour les vnements. Les indications .NET Framework spcifient que le type dlgu utilis pour un vnement doit disposer de deux paramtres et d'un retour dfinis comme suit : un paramtre de type Object qui dsigne la source de l'vnement, un autre paramtre soit de classe EventArgs , soit d'une classe qui drive de EventArgs, il encapsule toutes les informations personnelles relatives l'vnement, enfin le type du retour du dlgu doit tre void.

Evnement normalis sans information : Si vous n'utilisez pas d'informations personnelles pour l'vnement, la signature du dlgu sera : public delegate void DelegueTruc ( Object sender , EventArgs e ) ;

Evnement normalis avec informations : Si vous utilisez des informations personnelles pour l'vnement, vous dfinirez une classe MonEventArgs qui hrite de la classe EventArgs et qui contiendra ces informations personnelles, dans cette ventualit la signature du dlgu sera : public delegate void DelegueTruc ( Object sender , MonEventArgs e ) ;

Il est conseill d'utiliser la reprsentation normalise d'un vnement comme les deux exemples ci-dessous le montre, afin d'augmenter la lisibilit et le portage source des programmes vnementiels.

Mise en place d'un vnement normalis avec informations


Pour mettre en place un vnement Truc normalis contenant des informations personnalises vous devrez utiliser les lments suivants :
1) une classe d'informations personnalises sur l'vnement 2) une dclaration du type dlgation normalise (nom termin par EventHandler) 3) une dclaration d'une rfrence Truc du type dlgation normalise spcifie event 4.1) une mthode protge qui dclenche l'vnement Truc (nom commenant par On: OnTruc) 4.2) une mthode publique qui lance l'vnement par appel de la mthode OnTruc 5) un ou plusieurs gestionnaires de l'vnement Truc 6) abonner ces gestionnaires au dlgu Truc 7) consommer l'vnement Truc Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

10

Exemple de code C# directement excutable, associ cette dmarche :


using System; using System.Collections; namespace ExempleEvent {

//--> 1) classe d'informations personnalises sur l'vnement


public class TrucEventArgs : EventArgs { public string info ; public TrucEventArgs (string s) { info = s ; } }

//--> 2) dclaration du type dlgation normalis


public delegate void DelegueTrucEventHandler ( object sender, TrucEventArgs e ); public class ClassA {

//--> 3) dclaration d'une rfrence event de type dlgu :


public event DelegueTrucEventHandler Truc;

//--> 4.1) mthode protge qui dclenche l'vnement :


protected virtual void OnTruc( object sender, TrucEventArgs e ) { //.... if ( Truc != null ) Truc( sender , e ); //.... }

//--> 4.2) mthode publique qui lance l'vnement :


public void LancerTruc( ) { //.... TrucEventArgs evt = new TrucEventArgs ("vnement dclench" ) ; OnTruc ( this , evt ); //.... } } public class ClasseUse {

//--> 5) les gestionnaires d'vnement Truc


public void method100( object sender, TrucEventArgs e ){ //...gestionnaire d'vnement Truc: mthode d'instance. System.Console.WriteLine("information utilisateur 100 : "+e.info); }

rm di Scala - 2006

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

11

public void method101( object sender, TrucEventArgs e ) { //...gestionnaire d'vnement Truc: mthode d'instance. System.Console.WriteLine("information utilisateur 101 : "+e.info); } public void method102( object sender, TrucEventArgs e ) { //...gestionnaire d'vnement Truc: mthode d'instance. System.Console.WriteLine("information utilisateur 102 : "+e.info); } static public void method103( object sender, TrucEventArgs e ) { //...gestionnaire d'vnement Truc: mthode de classe. System.Console.WriteLine("information utilisateur 103 : "+e.info); } static private void methodUse( ) { ClassA ObjA = new ClassA( ); ClasseUse ObjUse = new ClasseUse ( );

//--> 6) abonnement des gestionnaires:


ObjA.Truc += new DelegueTrucEventHandler ( ObjA.Truc += new DelegueTrucEventHandler ( ObjA.Truc += new DelegueTrucEventHandler ( ObjA.Truc += new DelegueTrucEventHandler ( ObjUse.method100 ); ObjUse.method101 ); ObjUse.method102 ); method103 );

//--> 7) consommation de l'vnement:


ObjA.LancerTruc( ) ; //...l'appel cette mthode permet d'invoquer l'vnement Truc } static public void Main(string[] x) { methodUse( ) ; } } }

Rsultats d'excution du programme console prcdent :

Mise en place d'un vnement normalis sans information


En fait, pour les vnements qui n'utilisent pas d'informations supplmentaires personnalises, le .NET Framework a dj dfini un type dlgu appropri : System.EventHandler (quivalent au TNotifyEvent de Delphi): public delegate void EventHandler ( object sender, EventArgs e ); Pour mettre en place un vnement Truc normalis sans information spciale, vous devrez
Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

12

utiliser les lments suivants :


1) la classe System.EventArgs 2) le type dlgation normalise System.EventHandler 3) une dclaration d'une rfrence Truc du type dlgation normalise spcifie event 4.1) une mthode protge qui dclenche l'vnement Truc (nom commenant par On: OnTruc) 4.2) une mthode publique qui lance l'vnement par appel de la mthode OnTruc 5) un ou plusieurs gestionnaires de l'vnement Truc 6) abonner ces gestionnaires au dlgu Truc 7) consommer l'vnement Truc

Remarque, en utilisant la dclaration public delegate void EventHandler ( object sender, EventArgs e ) contenue dans System.EventHandler, l'vnement n'ayant aucune donne personnalise, le deuxime paramtre n'tant pas utilis, il est possible de fournir le champ static Empty de la classe EventArgs .

Exemple de code C# directement excutable, associ un vnement sans information personnalise :


using System; using System.Collections; namespace ExempleEvent {

//--> 1) classe d'informations personnalises sur l'vnement


System.EventArgs est dj dclare dans using System;

//--> 2) dclaration du type dlgation normalis


System.EventHandler est dj dclare dans using System; public class ClassA {

//--> 3) dclaration d'une rfrence event de type dlgu :


public event EventHandler Truc;

//--> 4.1) mthode protge qui dclenche l'vnement :

rm di Scala - 2006

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

13

protected virtual void OnTruc( object sender, EventArgs e ) { //.... if ( Truc != null ) Truc( sender , e ); //.... }

//--> 4.2) mthode publique qui lance l'vnement :


public void LancerTruc( ) { //.... OnTruc( this , EventArgs.Empty ); //.... } } public class ClasseUse {

//--> 5) les gestionnaires d'vnement Truc


public void method100( object sender, EventArgs e ){ //...gestionnaire d'vnement Truc: mthode d'instance. System.Console.WriteLine("information utilisateur 100 : vnement dclench"); } public void method101( object sender, EventArgs e ) { //...gestionnaire d'vnement Truc: mthode d'instance. System.Console.WriteLine("information utilisateur 101 : vnement dclench"); } public void method102( object sender, EventArgs e ) { //...gestionnaire d'vnement Truc: mthode d'instance. System.Console.WriteLine("information utilisateur 102 : vnement dclench"); } static public void method103( object sender, EventArgs e ) { //...gestionnaire d'vnement Truc: mthode de classe. System.Console.WriteLine("information utilisateur 103 : vnement dclench"); } static private void methodUse( ) { ClassA ObjA = new ClassA( ); ClasseUse ObjUse = new ClasseUse ( );

//--> 6) abonnement des gestionnaires:


ObjA.Truc += new DelegueTruc ( ObjA.Truc += new DelegueTruc ( ObjA.Truc += new DelegueTruc ( ObjA.Truc += new DelegueTruc ( ObjUse.method100 ); ObjUse.method101 ); ObjUse.method102 ); method103 );

//--> 7) consommation de l'vnement:


ObjA.LancerTruc( ) ; //...l'appel cette mthode permet d'invoquer l'vnement Truc } static public void Main(string[] x) { methodUse( ) ; } } } Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

14

Rsultats d'excution de ce programme :

2. Les vnements dans les Windows.Forms


Le code et les copies d'cran sont effectues avec C#Builder 1.0 de Borland version personnelle gratuite.

Contrles visuels et vnements


L'architecture de fentres de .Net FrameWork se trouve essentiellement dans l'espace de noms System.Windows.Forms qui contient des classes permettant de crer des applications contenant des IHM (interface humain machine) et en particulier d'utiliser les fonctionnalits affrentes aux IHM de Windows. Plus spcifiquement, la classe System.Windows.Forms.Control est la classe mre de tous les composants visuels. Par exemple, les classes Form, Button, TextBox, etc... sont des descendants de la classe Control qui met disposition du dveloppeur C# 58 vnements auxquels un contrle est sensible. Ces 58 vnements sont tous normaliss, certains sont des vnements sans information spcifique, d'autres possdent des informations spcifiques, ci-dessous un extrait de la liste des vnements de la classe Control, plus particulirement les vnements traitant des actions de souris :

rm di Scala - 2006

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

15

Nous avons mis en vidence deux vnements Click et Paint dont l'un est sans information (Click), l'autre est avec information (Paint). Afin de voir comment nous en servir nous traitons un exemple : Soit une fiche (classe Form1 hritant de la classe Form) sur laquelle est dpos un bouton poussoir (classe Button) de nom button1 :

Montrons comment nous programmons la raction du bouton un click de souris et son redessinement. Nous devons faire ragir button1 qui est sensible au moins 58 vnements, aux deux vnements Click et Paint. Rappelons la liste mthodologique ayant trait au cycle vnementiel, on doit utiliser :
Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

16

1) une classe d'informations personnalises sur l'vnement 2) une dclaration du type dlgation normalise (nom termin par EventHandler) 3) une dclaration d'une rfrence Truc du type dlgation normalise spcifie event 4.1) une mthode protge qui dclenche l'vnement Truc (nom commenant par On: OnTruc) 4.2) une mthode publique qui lance l'vnement par appel de la mthode OnTruc 5) un ou plusieurs gestionnaires de l'vnement Truc 6) abonner ces gestionnaires au dlgu Truc 7) consommer l'vnement Truc

Les tapes 1 4 ont t conues et developpes par les quipes de .Net et ne sont plus notre charge. Il nous reste les tapes suivantes : 5) construire un gestionnaire de raction de button1 l'vnement Click et un gestionnaire de raction de button1 6) abonner chaque gestionnaire au dlgu correspondant (Click ou Paint) L'tape 7 est assure par le systme d'exploitation qui se charge d'envoyer des messages et de lancer les vnements.

Evnement Paint : normalis avec informations


Voici dans l'inspecteur d'objets de C#Builder l'onglet Evnements qui permet de visualiser le dlgu utiliser ainsi que le gestionnaire abonner ce dlgu.

Dans le cas de l'vnement Paint, le dlgu est du type PaintEventArgs situ dans System.WinForms :

rm di Scala - 2006

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

17

signature du dlgu Paint dans System.Windows.Forms : public delegate void PaintEventHandler ( object sender, PaintEventArgs e );

La classe PaintEventArgs :

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

18

Evnement Click normalis sans information


Dans le cas de l'vnement Click, le dlgu est de type Event Handler situ dans System :

signature du dlgu Click dans System : public delegate void EventHandler ( object sender, EventArgs e );

En fait l'inspecteur d'objet de C#Builder permet de raliser en mode visuel la machinerie des tapes qui sont notre charge : le dlgu de l'vnement, [ public event EventHandler Click; ] le squelette du gestionnaire de l'vnement, [ private void button1_Click(object sender, System.EventArgs e){ } ] l'abonnement de ce gestionnaire au dlgu. [ this.button1.Click += new System.EventHandler ( this.button1_Click ); ]

Code C# gnr
rm di Scala - 2006

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

19

Voici le code gnr par C#Builder utilis en conception visuelle pour faire ragir button1 aux deux vnements Click et Paint :
public class WinForm : System.Windows.Forms.Form { private System.ComponentModel.Container components = null ; private System.Windows.Forms.Button button1; public WinForm( ) { InitializeComponent( ); } protected override void Dispose (bool disposing) { if (disposing) { if (components != null ) { components.Dispose( ) ; } } base.Dispose(disposing) ; } private void InitializeComponent( ) { this.button1 = new System.Windows.Forms.Button( ); ...... this.button1.Click += new System.EventHandler( this.button1_Click ); this.button1.Paint += new System.Windows.Forms.PaintEventHandler( this.button1_Paint ); ..... } static void Main( ) { Application.Run( new WinForm( ) ); } private void button1_Click(object sender, System.EventArgs e) //..... gestionnaire de l'vnement OnClick } {

private void button1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) //..... gestionnaire de l'vnement OnPaint } }

La machinerie vnementielle est automatiquement gnre par C#Builder comme dans Delphi, ce qui pargne de nombreuses lignes de code au dveloppeur et le laisse libre de penser au code spcifique de raction l'vnement.

Livret 10 : Construire des vnements avec C# - ( rv. 07.12.2005 )

page

20