Délégués & évènements
1. Les délégués
1.1 Qu'est ce qu'un délégué ?
Un délégué est un pointeur de méthode. Il permet de passer une méthode en paramètre à une autre méthode. L'interêt est de pouvoir déterminer au moment de l'exécution du programme (et non pas au moment de sa compilation) ce que celui-ci a besoin d'effectuer en fonction des informations fournies au moment de l'exécution.
Par exemple, lorsque vous démarrez votre application, un nouveau thread démarre. Ce thread doit savoir par où commencer le programme. Vous savez bien sûr (en principe) que toute application C# commence par la fonction Main (comme dans d'autres langages également). Et bien c'est cette fonction qui est passé en paramètre au thread. Ainsi il sait où quelle méthode il doit appeler.
L'exemple du fonctionnement d'un évènement est probablement le meilleur qui puisse caractériser et expliquer l'utilité et le fonctionnement des délégués.
Un programme ne gérant pas les évènements est un programme qui exécute à la suite toutes les instructions que le code lui dit de faire. Toutes ces instructions ont été déterminées au moment de la compilation du programme et l'utilisateur ne peut en rien intervenir dans leur ordre d'exécution...
Considérez maintenant un programme traitant les évènements de l'utilisateur. Lorsque l'utilisateur va cliquer sur la souris ou appuyer sur la touche Entrer par exemple, il va fournir au programme et via le système d'exploitation, de nouvelles informations. Ces informations sont inconnues au moment de la compilation (par exemple, l'endroit ou le contrôle sur lequel l'utilisateur a cliqué). Lorsque l'application est informée de l'évènement qui s'est produit, celle-ci doit ou non traiter cet évènement. Il faut donc que l'utilisateur ait définit quelque part une méthode permettant de le traiter mais il faut également que l'utilisateur ait fournit au programme le moyen de savoir vers quelle méthode celui-ci doit se tourner lorsque l'évènement est déclenché. C'est là l'intérêt des délégués : l'utilisateur définit sa méthode et indique via un délégué, quelle méthode doit être appelée par l'évènement en question.
1.2 Déclaration
Un délégué étant un pointeur de méthode, celui-ci doit pouvoir lui passer les bons arguments et éventuellement retourner la valeur que la méthode retourne. Voyons comment il se déclare :
public delegate long TwoInt32Operations(int a, int b);
public delegate string GetString();
public delegate string[] Search(string word);
Dans tous les cas, on ne sait pas ce que vont faire ces délégués car ils peuvent être instanciés de manière différente. Par exemple, l'opération sur les deux entiers peut aussi bien être une addition qu'une soustraction ou une multiplication... Le délégué GetString peut aussi bien renvoyer "Coucou" que "Au revoir" ou "Arrête de m'appeler"... et le dernier délégué Search peut aussi bien servir à chercher des patates que des carottes...
1.3 Utilisation
Ces délégués ne font rien en l'état. Il faut leur dire ce qu'il doivent faire. Par exemple, avec les délégués déclarés précédemment on peut faire :
public delegate long TwoInt32Operations(int a, int b);
public delegate string GetString();
public Program()
{
TwoInt32Operations myOp = new TwoInt32Operations(opOne);
GetString gs = new GetString(getMeAString);
Console.WriteLine("GetString retourne : " + gs());
gs = getMeAnotherString;
Console.WriteLine("et maintenant, GetString retourne : " + gs());
Console.WriteLine("L'opération effectuée par myOp a donné pour résultat : " + myOp(7, 13));
myOp = opTwo;
Console.WriteLine("et maintenant l'opération donne : " + myOp(7, 13));
}
private string getMeAString()
{
return "Coucou";
}
private string getMeAnotherString()
{
return "Bye...";
}
private long opOne(int a, int b)
{
return a + b;
}
private long opTwo(int a, int b)
{
return a * b;
}
Evidemment l'intérêt est ici très limité vu que toutes les méthodes sont disponibles dans la classe même. Mais dans un autre contexte, les délégués deviennent utiles. Par exemple pour le délégué Search on peut très bien imaginer quelque chose du genre :
using System;
namespace Delegates
{
public delegate string[] Search(string word);
public class Program
{
public static void Main()
{
new Program();
}
private string[] nom = new string[] { "Nicolas", "Clément", "George", "Michel", "Jean", "Bill" };
public Program()
{
Console.WriteLine("Bienvenue...\nVoici la liste des noms : ");
foreach (string n in nom)
Console.WriteLine("\t" + n);
Search s1 = methodeDeRecherche;
Search s2 = new Search(methodeDeRechercheNum2);
Search s3 = delegate(string s) {
Console.WriteLine("Nous sommes ici dans la 3e méthode... Mode exact...\nRésultats :");
foreach (string n in nom)
if (n == s)
Console.WriteLine("\t" + n);
return null;
};
new Recherche(s1);
new Recherche(s2);
new Recherche(s3);
Console.WriteLine("Au revoir...");
Console.ReadLine();
}
private string[] methodeDeRecherche(string mot)
{
Console.WriteLine("Nous sommes dans la méthode 1");
mot = mot.ToLower();
System.Collections.Generic.List<string> resultats = new System.Collections.Generic.List<string>();
foreach (string n in nom)
if (n.ToLower().Contains(mot))
resultats.Add(n);
return resultats.ToArray();
}
private string[] methodeDeRechercheNum2(string mot)
{
Console.WriteLine("Nous sommes dans la méthode 2");
mot = mot.ToLower();
Console.WriteLine("Résultats :");
foreach (string n in nom)
if (n.ToLower().Contains(mot))
Console.WriteLine("\t" + n);
return null;
}
}
public class Recherche
{
public Recherche(Search method)
{
Console.Write("Qui cherchez vous ? >");
string recherche = Console.ReadLine();
string[] resultats = method(recherche);
if (resultats != null)
{
Console.WriteLine("Résultats :");
foreach (string r in resultats)
Console.WriteLine("\t" + r);
}
}
}
}
On voit bien avec cet exemple comment utiliser les délégués.
Depuis la classe Recherche, on n'a pas accès au contenu dans lequel on doit effectuer la recherche. Si le programmeur n'avait accès qu'à cette classe, il n'aurait pas la moindre idée du fonctionnement de la recherche puisque la méthode qui effectue cette recherche appartient à une autre classe. Et pourtant, celle-ci est bien lancée et elle peut être différente en fonction des choix de l'utilisateur.
1.4 Autre exemple
Nous allons ici traiter un exemple où l'utilisation des délégués est incontestable.
Cet exemple comporte une méthode statique de tri de tableau d'objets. C'est cette méthode qui est au coeur du problème. En effet, trier des objets dans un ordre précis est tout simplement impossible, sauf si on connait la nature de ces objets. Mais ici, ces objets restent de nature inconnue et cette méthode doit aussi bien pouvoir trier des tableaux d'entiers que des tableaux de string ou des tableaux contenant nos propres objets.
Les délégués vont donc nous servir ici. En effet, lorsqu'une classe appelera cette méthode statique, celle-ci connaîtra la nature du tableau d'objects qu'elle veut trier et pourra donc fournir une méthode capable de comparer ces objets pour les trier (dans un ordre ascendant ou descendant, cela dépendra de la méthode définie).
Voyons le code source :
using System;
namespace Delegates
{
public delegate bool CompareOp(object a, object b);
public class Program
{
public static void Main()
{
new IntValues();
Console.ReadLine();
Console.WriteLine();
new StringValues();
Console.ReadLine();
Console.WriteLine();
new MyValues();
Console.ReadLine();
}
public static void Sort(object[] array, CompareOp methodeDeComparaison)
{
for (int i = 0; i < array.Length; i++)
{
for (int j = i; j < array.Length; j++)
{
if (methodeDeComparaison(array[i], array[j]))
{
object temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
}
public static void AfficherTableau(object[] array)
{
foreach (object o in array)
Console.WriteLine("\t" + o.ToString());
}
}
public class IntValues
{
private object[] values;
public IntValues()
{
//Un tableau de valeur... Tous des entiers
values = new object[14] { 45, 89, 12, 65, 475, 78, 12, 10, 69, 2, -1, 745, -96, 23 };
//On affiche le tableau avant pour savoir à quoi il ressemble...
Console.WriteLine("Dans la classe IntValues...\nAvant");
Program.AfficherTableau(values);
//On ne perd pas de temps et on lance directement la méthode
Program.Sort(values, new CompareOp(compareAscendant));
Console.WriteLine("Après 1er tri : ");
Program.AfficherTableau(values);
//On ne perd pas de temps et on lance directement la méthode
Program.Sort(values,
delegate(object a, object b)
{
if ((int)a > (int)b)
return false;
return true;
}
); /* Une fois de plus, on peut utiliser les méthodes anonymes pour
* déclarer notre délégué... Cette méthode est exactement opposée à
* compareAscendant */
Console.WriteLine("Après 2nd tri : ");
Program.AfficherTableau(values);
}
public object[] GetValues
{
get { return values; }
}
private bool compareAscendant(object a, object b)
{
/* Si a < b, on renvoie false, comme çà il sera considéré comme
* juste d'avoir une valeur inférieure placée avant les valeurs
* supérieures dans le tableau. */
if ((int)a < (int)b)
return false;
/* Dans tous les autres cas, c'est pas bon... On renvoie true pour
* que la changement de place dans le tableau s'effectue. */
return true;
}
}
public class StringValues
{
private object[] values;
public StringValues()
{
//Un tableau de valeur... Tous de type string
values = new object[8] { "Pierre", "Thomas", "Louis", "Jean", "Maxime", "Alphonse", "Caroline", "Fabrice" };
Console.WriteLine("Dans la classe StringValues...\nAvant :");
Program.AfficherTableau(values);
//On ne perd pas de temps et on lance directement la méthode
Program.Sort(values, new CompareOp(compare));
Console.WriteLine("Après :");
Program.AfficherTableau(values);
}
public object[] GetValues
{
get { return values; }
}
private bool compare(object a, object b)
{
/* Même principe que pour les entiers... mais on va ici se limiter au
* premier caractère du mot. */
if (((string)a)[0] < ((string)b)[0])
return false;
return true;
}
}
public class MyValues
{
private object[] values;
public MyValues()
{
MyValuesType mvt_1 = new MyValuesType();
mvt_1.ID = 1;
mvt_1.Name = "Nom n°1";
mvt_1.Description = "Short description";
MyValuesType mvt_2 = new MyValuesType();
mvt_2.ID = 12;
mvt_2.Name = "Nom n°2";
mvt_2.Description = "Tiny desc.";
MyValuesType mvt_3 = new MyValuesType();
mvt_3.ID = 4;
mvt_3.Name = "Nom n°3";
mvt_3.Description = "Cette description est déjà un peu plus longue...";
MyValuesType mvt_4 = new MyValuesType();
mvt_4.ID = 7;
mvt_4.Name = "Nom n°4";
mvt_4.Description = "lol";
MyValuesType mvt_5 = new MyValuesType();
mvt_5.ID = 2;
mvt_5.Name = "Nom n°5";
mvt_5.Description = "Cette description est beaucoup trop longue à lire. Je refuse de la lire !";
//Un tableau de valeur... Tous de type MyValuesType
values = new object[5] { mvt_1, mvt_2, mvt_3, mvt_4, mvt_5 };
Console.WriteLine("Dans la classe MyValues...\nAvant :");
Program.AfficherTableau(values);
//On ne perd pas de temps et on lance directement la méthode
Program.Sort(values, new CompareOp(compare));
Console.WriteLine("Après :");
Program.AfficherTableau(values);
}
public object[] GetValues
{
get { return values; }
}
private bool compare(object a, object b)
{
/* On considère ici que l'object qui a la plus longue description est un objet supérieur et donc plus grand. */
if (((MyValuesType)a).Description.Length < ((MyValuesType)b).Description.Length)
return false;
return true;
}
}
public struct MyValuesType
{
private int id;
private string name;
private string description;
public int ID
{
get { return id; }
set { id = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
public string Description
{
get { return description; }
set { description = value; }
}
public override string ToString()
{
return String.Format("ID : {0} - Nom : {1} - Description : {2}", id, name, description);
}
}
}
Cet exemple devrait vous permettre de bien saisir le fonctionnement et la puissance des délégués. Pour davantages de précisions, pensez aux breakpoints...
1.5 Les délégués multicast
Jusqu'à présent, les délégués utilisés ne référençaient qu'une seule méthode. Pour en appeler plusieurs, il faudrait faire des appels de méthodes dans la méthode référencée.
Heuresement, il est possible d'indiquer à un délégué d'appeler plusieurs méthodes successivement. Ce genre de délégués, appelées délégués multicast, retournent obligatoirement void (où iraient les valeurs renvoyées sinon ?). Par conséquent, dès que le compilateur voit un délégué retournant void, il le considère directement comme un délégué multicast.
Les méthodes que vous aurez référencées dans ce délégué seront appelées dans l'ordre où vous les avez définies.
Exemple :
public delegate void Search(string word);
public class Program
{
public Program()
{
Search sch = rechercherNom;
sch += rechercherPrenom;
sch += rechercherSurnom;
/* Vous pouvez bien sûr ajouter une méthode anonyme au délégué.
* Cette méthode sera obligatoirement appelée, impossible de la
* retirer par la suite puisqu'il s'agit d'une méthode anonyme
* (vous ne possédez pas le nom de la méthode, nécessaire pour
* la retirer du délégué). */
sch += delegate(string mail) { /* Instructions */ };
sch("coucou"); //Les 4 méthodes seront appelées
//Instructions
//On peut également retirer des méthodes du délégué
sch -= rechercherPrenom;
sch += rechercherSurnom;
sch("coucou"); //Deux méthodes seront appelées
}
private void rechercherNom(string nom)
{
//Instructions
}
private void rechercherPrenom(string prenom)
{
//Instructions
}
private void rechercherSurnom(string surnom)
{
//Instructions
}
}
Lors du premier appel de sch("coucou");, les 4 méthodes sont appelées dans l'ordre où elles ont été ajoutés au délégué. C'est à dire que rechercheNom sera d'abord appelée, puis rechercherPrenom, puis rechercherSurnom et enfin la méthode anonyme.
Après avoir retirer deux de ces méthode, le second appel de sch va faire la même chose qu'auparavant mais en ignorant les deux méthodes retirées du délégué.
1.6 Les délégués génériques
Depuis C# 2.0 et l'arrivée des types génériques, il est possible de déclarer des délégués génériques. Le fonctionnement est le même que pour les autres types génériques. Par exemple, en remaniant l'exemple précédant, on peut faire :
public delegate void Search<T>(T valeur);
public class Program
{
public Program()
{
Search<int> sch_1 = rechercherEntier_1;
sch_1 += rechercherEntier_2;
Search<string> sch_2 = rechercherSurnom;
sch_2 += rechercher<string>;
//etc.
}
private void rechercherEntier_1(int val)
{
//Instructions
}
private void rechercherEntier_2(int val)
{
//Instructions
}
private void rechercherSurnom(string surnom)
{
//Instructions
}
private void rechercher<T>(T valeur)
{
//Instructions
}
}
2. Les évènements
2.1 Qu'est ce qu'un évènement ?
Un évènement est une information, transmise à l'application par le générateur d'évènement, qui indique qu'il s'est produit "quelque chose d'interessant" qui doit ou peut susciter l'attention de l'application.
Sous Windows, le générateur d'évènements est Windows lui-même et lorsque l'utilisateur clique sur la souris, appuie sur une touche, redimensionne une fenêtre, un évènement est déclenché. Cet évènement indique et fournit à l'application toutes les informations nécessaires et liées à ce qui a déclenché l'évènement.
Par exemple, lorsque l'utilisateur clique sur un bouton de la souris, un évènement OnClick va être envoyé par le générateur d'évènement vers l'application ayant subit ce clic. Cet évènement fournira à l'application un minimum d'informations (sur quoi on a cliqué ?) ou de nombreaux détails (où a-t-on cliqué exactement ? quel contrôle ? etc.).
Les évènements sont une forme particulière de délégués. Pour autant, vous pouvez très bien ne rien avoir compris aux délégués et parfaitement intégrer les évènements.
Lorsqu'un évènement parvient à notre application C# et si cet évènement est interesssant, que le programmeur a décidé d'effectuer une action lorsque cet évènement se produit, alors il faut que l'évènement en question puisse, et ceci par le biais de notre application .NET, dire quelle méthode doit être appelée à ce moment précis. C'est là qu'interviennent les délégués : il faudra indiquer à nos évènements vers quelle(s) méthode(s) ils doivent pointer pour que lorsque ceux-ci soient déclencher, ils puissent appeler ces méthodes avec succès.
2.2 Exemples avec l'évènement OnClick
Voyons tout de suite comment utiliser un évènement préexistant. Nous utiliserons ici l'évènement Click que toute classe héritant de Control possède. Par exemple, avec un contrôle personnalisé, on peut faire :
public class MonControl : Control
{
public MonControl()
{
this.Click += new EventHandler(MonControl_Click);
}
/* Les paramètres d'un évènements sont toujours de ce type :
* un object et une classe EventArgs ou héritant de EventArgs */
private void MonControl_Click(object sender, EventArgs e)
{
//Instructions après clic
}
}
Ou sinon, tout simplement avec une Form de base.
public class MyClass
{
public MyClass()
{
Form f = new Form();
f.Click += new EventHandler(MyForm_Click);
f.Click += new EventHandler(MyForm_Click_2);
f.Click += delegate(object sender, EventArgs e) //La signature du délégué doit correspondre à celle de l'évènement
{
//Instructions après clic
};
f.Click += MyForm_Click_3;
/* Un évènement étant une forme particulière de délégué et ne pouvant renvoyer
* aucune valeur (void), il s'agit donc d'un délégué multicast particulier...
* Comme pour les délégué multicast, on peut ajouter plusieurs méthodes sur
* lesquelles celui-ci doit pointer avec += (cf. lignes de code précédentes).
* Comme pour les délégués multicast, on peut retirer des méthodes sur lesquelles
* l'évènement pointait (indirectement : par le biais d'un délégué). Il faut alors
* utiliser l'opérateur -= . */
f.Click -= MyForm_Click;
}
private void MyForm_Click(object sender, EventArgs e)
{
//Instructions après clic
}
private void MyForm_Click_2(object sender, EventArgs e)
{
//Instructions après clic
}
private void MyForm_Click_3(object sender, EventArgs e)
{
//Instructions après clic
}
}
Vous retrouvez donc les comportements des délégués pour les évènements. On peut en effet considérer qu'un évènement comme Click est un délégué multicast (renvoie forcément void) et qu'on peut lui ajouter ou retirer, comme d'habitute, de mutliples méthodes.
2.3 Les évènements personnalisés
Vous n'êtes pas limités aux seuls évènements déjà fournis par l'environnement .NET, vous pouvez créer vos propres évènements.
Nous allons ici créer notre propre évènement (on va en faire juste un), dans un contexte bien précis :
Nous avons une Form principale vide. Cette Form (que nous appelerons MainForm) n'affichera que 9 contrôles. Des contrôles que nous aurons nous même créé.
Ces contrôles sont très simples : il s'agit d'un contrôle de base avec une couleur de fond. Ce contrôle comporte un numéro et un évènement que nous coderons nous mêmes qui permettra de connaître la couleur du contrôle sur lequel l'utilisateur a cliqué. C'est à dire que notre second argument (EventArgs) ressemblera à ceci :
public class ColorEventArgs : EventArgs
{
private Color col;
public ColorEventArgs(Color background)
: base()
{
col = background;
}
public Color GetBackgroundColor
{
get { return col; }
}
}
et que notre contrôle personnalisé ressemblera à cela :
public class MyBox : Control
{
//Déclaration de notre délégué et de notre évènement
public delegate void MyOwnClickEventHandler(object sender, ColorEventArgs e);
public event MyOwnClickEventHandler MyOwnClick;
private int numeroDeBoite;
public MyBox(Color background, int numero)
{
//Des données que l'on estime ici indispensables
this.BackColor = background;
this.numeroDeBoite = numero;
//Cet évènement est l'un des évènements de base offert par la classe Control
this.Click += MyBox_Click;
}
public int NumeroDeBoite
{
get { return numeroDeBoite; }
}
private void MyBox_Click(object sender, EventArgs e)
{
/* On vérifie que notre pointeur de méthode n'est pas nul.
* Cette petite vérification est très importante car vous ne
* voudrez pas forcément implémenter tous les évènements proposés,
* et si ceux-ci sont appelées malgré tout alors qu'ils ne peuvent
* appeler aucune méthode, une erreur se produira... */
if (MyOwnClick != null)
{
/* On appel notre évènement... ce qui va déclencher du même coup
* l'appel du délégué et donc de ou des méthodes vers lesquelles
* celui-ci pointe. */
MyOwnClick(this, new ColorEventArgs(this.BackColor));
}
}
}
Remarquez la façon dont on a créé notre évènement à partir d'un délégué dans cette classe MyBox :
//Déclaration de notre délégué et de notre évènement
public delegate void MyOwnClickEventHandler(object sender, ColorEventArgs e);
public event MyOwnClickEventHandler MyOwnClick;
Finalement, il ne vous manque plus grand chose pour pouvoir exécuter cet exemple...
Code source complet :
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Evenements
{
public class Program
{
[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
public class MainForm : Form
{
//Un tableau de couleurs... pour les backgrounds de nos contrôles MyBox
private Color[] tableauDeCouleurs = new Color[9]{
Color.Red, //Couleur prédéfinie (Red = 0xFF, 0x00, 0x00)
Color.FromArgb(0x99, 0x66, 0x00), //Couleur RGB (Red-Green-Blue) en hexadécimal...
Color.FromArgb(0x66, 0x66, 0x00),
Color.FromArgb(0x33, 0x99, 0x00),
Color.Green, //Couleur prédéfinie (Green = 0x00, 0xFF, 0x00)
Color.FromArgb(0x00, 0x99, 0x33),
Color.FromArgb(0x00, 0x66, 0x66),
Color.FromArgb(0x00, 0x33, 0x99),
Color.Blue //Couleur prédéfinie (Blue = 0x00, 0x00, 0xFF)
};
private MyBox[] myBoxes;
public MainForm()
{
this.Size = new Size(230, 250);
myBoxes = new MyBox[9];
//x et y vont servir ici pour le positionnement de nos objets MyBox
int x = 10, y = 10;
for (int i = 0; i < myBoxes.Length; i++)
{
//On instancie notre boîte en lui passant une couleur de fond et un numéro
myBoxes[i] = new MyBox(tableauDeCouleurs[i], i + 1);
myBoxes[i].Location = new Point(x, y);
myBoxes[i].Size = new Size(60, 60);
//Ici, on instancie notre évènement personalisé de MyBox... avec une méthode anonyme au passage...
myBoxes[i].MyOwnClick += delegate(object sender, ColorEventArgs e)
{
MessageBox.Show(
String.Format("Vous avez cliqué sur la boîte n°{0} de couleur {1}, soit en valeur numérique : 0x{2:X}", (sender as MyBox).NumeroDeBoite, e.GetBackgroundColor.Name, e.GetBackgroundColor.ToArgb())
);
};
//et bien sûr on peut toujours passer plusieurs méthodes à nos propres évènements...
if ((i + 1) % 2 == 0) //Une chance sur deux...
myBoxes[i].MyOwnClick += new MyBox.MyOwnClickEventHandler(MainForm_MyOwnClick);
this.Controls.Add(myBoxes[i]);
//Quelques petits détails pour l'affichage (affichage en un tableau de 3x3 boîtes)
x += 70;
if ((i + 1) % 3 == 0)
{
x = 10;
y += 70;
}
}
}
void MainForm_MyOwnClick(object sender, ColorEventArgs e)
{
MessageBox.Show("Coucou");
}
}
public class MyBox : Control
{
//Déclaration de notre délégué et de notre évènement
public delegate void MyOwnClickEventHandler(object sender, ColorEventArgs e);
public event MyOwnClickEventHandler MyOwnClick;
private int numeroDeBoite;
public MyBox(Color background, int numero)
{
//Des données que l'on estime ici indispensables
this.BackColor = background;
this.numeroDeBoite = numero;
//Cet évènement est l'un des évènements de base offert par la classe Control
this.Click += MyBox_Click;
}
public int NumeroDeBoite
{
get { return numeroDeBoite; }
}
private void MyBox_Click(object sender, EventArgs e)
{
/* On vérifie que notre pointeur de méthode n'est pas nul.
* Cette petite vérification est très importante car vous ne
* voudrez pas forcément implémenter tous les évènements proposés,
* et si ceux-ci sont appelées malgré tout alors qu'ils ne peuvent
* appeler aucune méthode, une erreur se produira... */
if (MyOwnClick != null)
{
/* On appel notre évènement... ce qui va déclencher du même coup
* l'appel du délégué et donc de ou des méthodes vers lesquelles
* celui-ci pointe. */
MyOwnClick(this, new ColorEventArgs(this.BackColor));
}
}
}
//Cette classe est auto-descriptive... pas besoin de plus de commentaires...
public class ColorEventArgs : EventArgs
{
private Color col;
public ColorEventArgs(Color background)
: base()
{
col = background;
}
public Color GetBackgroundColor
{
get { return col; }
}
}
}
Remarque : Les évènements ne s'utilisent pas qu'avec des Windows Forms. Vous pouvez aussi bien vous en servir pour des Web Services que pour une application console ou autre...