次の方法で共有


Mise en place d'événements/delegates en VB.NET et C#.NET

Une question qui revient assez souvent dans les newsgroups est :

  • Comment mettre en place une communication entre mes classes métiers et mes IHM ?
  • Comment faire ça avec la philosophie .NET ?

(les exemples de code utilisés dans cet article sont en libre téléchargement ici )

Un exemple assez classique pour illustrer le propos reste la copie de fichier. L'idéal est de créer une classe métier qui va s'occuper de la copie du fichier puis de créer une IHM qui va consommer cette classe.

Cependant, si le fichier fait 10Mo, il peut être utile de présenter à l'utilisateur une gauge de progression pour le faire patienter. De plus, si il décide d'annuler la copie, il doit pouvoir le faire, ce qui veut dire que l'interface graphique ne doit pas être bloquée durant cette copie. A titre d'exemple, voici une classe "classique" (Librairie_CS.GestionFichiers ou Librairie_VB.GestionFichiers) permettant de réaliser cette opération de copie :

using System;
using System.IO;

namespace Librairie_CS {

 public class GestionFichiers {

  private string cheminFichier = string.Empty;
public string CheminFichier {

   get {

    return(this.cheminFichier);
}
}

  public GestionFichiers(string cheminFichier) {

   this.cheminFichier = cheminFichier;
}

  public void CopieFichier(string cheminFichierDestination, bool ecrasement) {

   // On vérifie si le fichier de destination n'existe pas déjà
   if (File.Exists(cheminFichierDestination)) {

// Si demandé, on écrase le fichier de destination
if (ecrasement) {

     File.Delete(cheminFichierDestination);
}
else {

     throw new IOException(string.Format("Le fichier de destination '{0}' existe déjà.", cheminFichierDestination));
}
}

   // On ouvre le fichier de destination en écriture
using (FileStream fileStreamDestination = new FileStream(cheminFichierDestination, FileMode.Create, FileAccess.Write)) {

    // On ouvre le fichier d'origine en lecture
using (FileStream fileStreamOriginal = new FileStream(this.cheminFichier, FileMode.Open, FileAccess.Read)) {

     // On lit le prochain octet
     int octet = fileStreamOriginal.ReadByte();
while (octet != -1) {

// On écrit l'octet
fileStreamDestination.WriteByte((byte)octet);

      octet = fileStreamOriginal.ReadByte();
}

     fileStreamOriginal.Close();
}
fileStreamDestination.Close();
}
}
}
}

Voici le même code écrit en VB.NET:

Imports System
Imports System.IO

Namespace Librairie_VB

    Public Class GestionFichiers

        Private privateCheminFichier As String = String.Empty
Public ReadOnly Property CheminFichier() As String
Get
Return (Me.privateCheminFichier)
End Get
End Property

        Public Sub New(ByVal fichier As String)
Me.privateCheminFichier = fichier
End Sub

        Public Sub CopieFichier(ByVal cheminFichierDestination As String, ByVal ecrasement As Boolean)

            ' On vérifie si le fichier de destination n'existe pas déjà
If File.Exists(cheminFichierDestination) Then

                ' Si demandé, on écrase le fichier de destination
If ecrasement Then
File.Delete(cheminFichierDestination)
Else
Throw New IOException(String.Format("Le fichier de destination '{0}' existe déjà.", cheminFichierDestination))
End If
End If

            ' On ouvre le fichier de destination en écriture
Dim fileStreamDestination As New FileStream(cheminFichierDestination, FileMode.Create, FileAccess.Write)

            ' On ouvre le fichier d'origine en lecture
Dim fileStreamOriginal As New FileStream(Me.privateCheminFichier, FileMode.Open, FileAccess.Read)

            ' On lit le prochain octet
Dim octet As Integer = fileStreamOriginal.ReadByte()

            While octet <> -1
' On écrit l'octet
fileStreamDestination.WriteByte(CType(octet, Byte))

                octet = fileStreamOriginal.ReadByte()
End While

            fileStreamOriginal.Close()
fileStreamDestination.Close()

        End Sub

    End Class

End Namespace

Ceci étant fait, il faudrait maintenant implémenter trois événements dans nos classes:

  • CopieDemarrage : cet événement devra être émis juste avant le démarrage proprement dit de l'opération de copie. Il devra permettre notament d'informer le client de la taille du fichier à copier
  • CopieEnCours : cet événement devra être émis à intervalle régulier de manière à indiquer la progression de la copie. Il devra également permettre au client d'annuler l'opération en cours
  • CopieFin : cet événement devra être émis en fin de processus en indiquant le statut final de l'opération (Succès, Echec, Annulation)

Pou mettre en place ce genre de choses, il faut passer par des Delegates:

  • CopieDemarrageEventHandler
  • CopieEnCoursEventHandler
  • CopieFinEventHandler

Notez que le plan de nommage utilisé en standard par le .NET Framework consiste à terminer le nom de ses Delegates par EventHandler. Une fois les Delegates déclarés, il faut ensuite déclarer les événements eux-mêmes. Les événements que nous allons déclarés auront pour type les Delegates précédents. Voici les déclarations que nous allons utiliser :

namespace Librairie_CS {

 public delegate void CopieDemarrageEventHandler(object sender, CopieDemarrageEventArgs e);
public delegate void CopieEnCoursEventHandler(object sender, CopieEnCoursEventArgs e);
public delegate void CopieFinEventHandler(object sender, CopieFinEventArgs e);

 public class GestionFichiers {

  public event CopieDemarrageEventHandler CopieDemarrage = null;
public event CopieEnCoursEventHandler CopieEnCours = null;
public event CopieFinEventHandler CopieFin = null;

Namespace Librairie_VB

    Public Delegate Sub CopieDemarrageEventHandler(ByVal sender As Object, ByVal e As CopieDemarrageEventArgs)
Public Delegate Sub CopieEnCoursEventHandler(ByVal sender As Object, ByVal e As CopieEnCoursEventArgs)
Public Delegate Sub CopieFinEventHandler(ByVal sender As Object, ByVal e As CopieFinEventArgs)

    Public Class GestionFichiers

        Public Event CopieDemarrage As CopieDemarrageEventHandler
Public Event CopieEnCours As CopieEnCoursEventHandler
Public Event CopieFin As CopieFinEventHandler

Notons au passage que la création de Delegate n'est pas obligatoire mais que le compilateur VB.NET en créera un de toute façon pour vous. Autant bien comprendre cette notion et les écrire nous même... Exemple :

<Aparté>
Imaginons que nous déclarions un Event en VB.NET comme suit :

        Public Event Test(ByVal monParam As Integer)

L'introspection de la DLL générée grâce à ILDASM , nous permet de découvrir que le compilateur a effectivement bossé pour nous en créant le Delegate directement au niveau du code IL :

   | | |___[CLS] TestEventHandler
| | | | .class nested public auto ansi sealed
| | | | extends [mscorlib]System.MulticastDelegate

puis

    | | |___[FLD] TestEvent : private class Librairie_VB.GestionFichiers/TestEventHandler

< /Aparté>

Revenons maintenant à nos moutons. Vous avez certainement remarqué que la déclaration des signatures de mes Delegates (et donc de mes événements in fine) avait l'air de suivre une règle commune:

  • (object sender, xxxEventArgs e)
  • (ByVal sender As Object, ByVal e As xxxEventArgs)

Le premier argument appelé sender permet à l'abonné de l'événement de connaître précisément l'instance de l'objet qui a émis l'événement. A quoi cela peut-il servir me direz-vous ?

Et bien imaginons un instant que je possède deux instances d'un même objet, Instance1 et Instance2, et que je souhaite m'abonner à l'événement NouveauButPourLOM. Comme dans mon cas le traitement que j'ai à faire dans cet événement côté abonné est identique pour les deux instances, je me dis que cela serait sympa de n'utiliser qu'une seule et même méthode pour traiter les deux événements. Et bien l'argument sender m'a justement me permettre de savoir si l'événement a été émis par Instance1 ou Instance2. A noter que si nous décidions que notre méthode CopieFichier était statique (static ou Shared), ce premier argument n'aurait pas de sens et ne figurerait pas dans la signature.

Généralement, l'événement émis par une classe à destination d'une autre classe abonnée transporte avec lui un certain nombre d'informations qui pourraient être utiles à l'abonné. On pourrait être tenté de créer autant de paramètres à la signature de notre Delegate/Event que nécessaire. Ce n'est pas l'approche qui a été choisie par les développeurs du framework .NET.

Lorsqu'on souhaite déclarer un évenement qui n'a aucun paramètre particulier à transmettre, on utilise comme deuxième paramètre du Delegate/Event la classe System.EventArgs. Voici ce que la documentation indique sur cette classe :

EventArgs est la classe de base des classes contenant des données d'événement.Cette classe ne contient pas de données d'événement; elle est utilisée par des événements qui ne passent pas d'informations d'état à un gestionnaire d'événements lorsqu'un événement est déclenché. Si le gestionnaire d'événements nécessite des informations d'état, l'application doit dériver une classe à partir de cette classe pour contenir les données.

Et voilà tout est dit, dès qu'on veut passer un certain nombre d'informations à un Delegate/Event, on écrit une classe qui dérive de la classe EventArgs à laquelle on ajoute les informations supplémentaires. Vous avez noté que le nom de ces classes se terminent généralement par EventArgs.

Pour l'événement CopieDemarrage, ce dont on a besoin est simplement la taille du fichier à copier. Ainsi cela permettra d'être notifié de l’imminence du démarrage de la copie et de pouvoir ajuster les valeurs de notre gauge de progression en fonction de la taille du fichier à copier. De plus, il pourrait être utile de pouvoir récupérer les chemins complets des fichiers source et destination. Cela nous donne :

namespace Librairie_CS {

 public class CopieDemarrageEventArgs : System.EventArgs {

  private string fichierSource = string.Empty;
public string FichierSource {

   get {

    return(this.fichierSource);
}
}

  private string fichierDestination = string.Empty;
public string FichierDestination {

   get {

    return(this.fichierDestination);
}
}

  private long tailleFichier = 0;
public long TailleFichier {

   get {

    return(this.tailleFichier);
}
}

  public CopieDemarrageEventArgs(string fichierSource, string fichierDestination, long tailleFichier) {

   this.fichierSource = fichierSource;
this.fichierDestination = fichierDestination;
this.tailleFichier = tailleFichier;
}
}
}

Namespace Librairie_VB

    Public Class CopieDemarrageEventArgs

        Inherits System.EventArgs

        Private privateFichierSource As String = String.Empty
Public ReadOnly Property FichierSource() As String
Get
Return (Me.privateFichierSource)
End Get
End Property

        Private privateFichierDestination = String.Empty
Public ReadOnly Property FichierDestination() As String
Get
Return (Me.privateFichierDestination)
End Get
End Property

        Private privateTailleFichier As Long = 0
Public ReadOnly Property TailleFichier() As Long
Get
Return (Me.privateTailleFichier)
End Get
End Property

        Public Sub New(ByVal source As String, ByVal destination As String, ByVal taille As Long
            Me.privateFichierSource = source
Me.privateFichierDestination = destination
Me.privateTailleFichier = taille
End Sub

    End Class

End Namespace

Pour l'événement CopieFin, ce dont on a besoin est simplement le statut final de la copie. On sait que la copie peut s'achever de trois manières différentes : Succes, Echec ou encore Annulation (par l'utilisateur). C'est un cas idéal de création d'une énumération :

namespace Librairie_CS {

 public enum StatutCopie {

  Aucun,
Succes,
Echec,
Annulation
}
}

Namespace Librairie_VB

    Public Enum StatutCopie
Aucun
Succes
Echec
Annulation
End Enum

End Namespace

De plus, il peut être utile d'indiquer dans cet événement en combien de temps s'est déroulé l'opération de copie. Cela nous donne finalement ceci:

namespace Librairie_CS {

 public class CopieFinEventArgs : System.EventArgs {

  private StatutCopie statutCopie = StatutCopie.Aucun;
public StatutCopie StatutCopie {

   get {

    return(this.statutCopie);
}
}

  private int duree = 0;
public int Duree {

   get {

    return(this.duree);
}
}

  public CopieFinEventArgs(StatutCopie statutCopie, int duree) {

   this.statutCopie = statutCopie;
this.duree = duree;
}
}
}

Namespace Librairie_VB

    Public Class CopieFinEventArgs

        Inherits System.EventArgs

        Private privateStatutCopie As StatutCopie = StatutCopie.Aucun
Public ReadOnly Property StatutCopie() As statutCopie
Get
Return (Me.privateStatutCopie)
End Get
End Property

        Private privateDuree As Integer = 0
Public ReadOnly Property Duree() As Integer
Get
Return (Me.privateDuree)
End Get
End Property

        Public Sub New(ByVal statutCopie As StatutCopie, ByVal duree As Integer)
Me.privateStatutCopie = statutCopie
Me.privateDuree = duree
End Sub

    End Class

End Namespace

Finalement, pour CopieEnCours, il faudrait fournir le nombre d'octets déjà copiés au moment de l'émission de l'événement. Aucune difficulté particulière ici. Par contre, on souhaite également donner la possibilité à l'abonné de pouvoir annuler la copie à tout le moment. L'utilisation de cet événement pour permettre l'annulation de l'opération est alors l'idéal car appelé très régulièrement durant le processus de copie. Voici ce que cela donne :

namespace Librairie_CS {

 public class CopieEnCoursEventArgs : System.EventArgs {

private long position = 0;
public long Position {

   get {

    return(this.position);
}
}

  private bool cancel = false;
public bool Cancel {

   get {

    return(this.cancel);
}
set {

    this.cancel = value;
}
}

  public CopieEnCoursEventArgs(long position) {

   this.position = position;
}
}
}

Namespace Librairie_VB

    Public Class CopieEnCoursEventArgs

        Inherits System.EventArgs

        Private privatePosition As Long = 0
Public ReadOnly Property Position() As Long
Get
Return (Me.privatePosition)
End Get
End Property

        Private privateCancel As Boolean = False
Public Property Cancel() As Boolean

            Get
Return (Me.privateCancel)
End Get
Set(ByVal Value As Boolean)
Me.privateCancel = Value
End Set
End Property

        Public Sub New(ByVal position As Long)
Me.privatePosition = position
End Sub

    End Class

End Namespace

Notez que dans ce cas, la propriété Cancel doit être en lecture ET écriture pour donner une chance à l'abonné de pouvoir modifier sa valeur.

A ce niveau là, on a fait le plus dur (et ce n'était pas bien dur, il faut bien l'avouer ;-) ). Il nous reste à modifier le code de la fonction CopieFichier pour déclencher l'émission des événements comme il faut. Cela donne ceci :

public void CopieFichier(string cheminFichierDestination, bool ecrasement) {

 // On vérifie si le fichier de destination n'existe pas déjà
if (File.Exists(cheminFichierDestination)) {

  // Si demandé, on écrase le fichier de destination
if (ecrasement) {

   File.Delete(cheminFichierDestination);
}
else {

   throw new IOException(string.Format("Le fichier de destination '{0}' existe déjà.", cheminFichierDestination));
}
}

 StatutCopie statutCopie = StatutCopie.Aucun;
int dureeCopie = System.Environment.TickCount;

 try {

  statutCopie = StatutCopie.Succes;

  // On ouvre le fichier de destination en écriture
using (FileStream fileStreamDestination = new FileStream(cheminFichierDestination, FileMode.Create, FileAccess.Write)) {

   // On ouvre le fichier d'origine en lecture
using (FileStream fileStreamOriginal = new FileStream(this.cheminFichier, FileMode.Open, FileAccess.Read)) {

    // On informe l'abonné de l'imminence de la copie
if (CopieDemarrage != null) {

     CopieDemarrage(this, new CopieDemarrageEventArgs(this.cheminFichier, cheminFichierDestination, fileStreamOriginal.Length));
}

    // On lit le prochain octet
int octet = fileStreamOriginal.ReadByte();
while (octet != -1) {

     // On écrit l'octet
fileStreamDestination.WriteByte((byte)octet);

     // On informe l'abonné de la progression de la copie
if (CopieEnCours != null) {

      CopieEnCoursEventArgs copieEnCoursEventArgs = new CopieEnCoursEventArgs(fileStreamOriginal.Position);
CopieEnCours(this, copieEnCoursEventArgs);

      // L'utilisateur a-t'il demandé l'annulation de la copie ?
if (copieEnCoursEventArgs.Cancel) {

       statutCopie = StatutCopie.Annulation;
break;
}
}

     octet = fileStreamOriginal.ReadByte();
}

    fileStreamOriginal.Close();
}
fileStreamDestination.Close();
}
}
catch {

  // On renvoit telle quelle l'exception qui a lieu,
// l'objectif étant simplement de pouvoir indiquer
// correctement le statut de la copie
statutCopie = StatutCopie.Echec;
throw;
}
finally {

  dureeCopie = System.Environment.TickCount - dureeCopie;

  // On informe l'abonné de la fin de la copie
if (CopieFin != null) {

   CopieFin(this, new CopieFinEventArgs(statutCopie, dureeCopie));
}
}
}

Public Sub CopieFichier(ByVal cheminFichierDestination As String, ByVal ecrasement As Boolean)

 ' On vérifie si le fichier de destination n'existe pas déjà
If File.Exists(cheminFichierDestination) Then

  ' Si demandé, on écrase le fichier de destination
If ecrasement Then
File.Delete(cheminFichierDestination)
Else
Throw New IOException(String.Format("Le fichier de destination '{0}' existe déjà.", cheminFichierDestination))
End If
End If

 Dim statutCopie As statutCopie = statutCopie.Aucun
Dim dureeCopie As Integer = System.Environment.TickCount

 Try

  statutCopie = statutCopie.Succes

  ' On ouvre le fichier de destination en écriture
Dim fileStreamDestination As New FileStream(cheminFichierDestination, FileMode.Create, FileAccess.Write)

  ' On ouvre le fichier d'origine en lecture
Dim fileStreamOriginal As New FileStream(Me.privateCheminFichier, FileMode.Open, FileAccess.Read)

  ' On informe l'abonné de l'imminence de la copie
RaiseEvent CopieDemarrage(Me, New CopieDemarrageEventArgs(Me.privateCheminFichier, cheminFichierDestination, fileStreamOriginal.Length))

  ' On lit le prochain octet
Dim octet As Integer = fileStreamOriginal.ReadByte()

  While octet <> -1
' On écrit l'octet
fileStreamDestination.WriteByte(CType(octet, Byte))

   ' On informe l'abonné de la progression de la copie
Dim copieEnCoursEventArgs As copieEnCoursEventArgs = New copieEnCoursEventArgs(fileStreamOriginal.Position)
RaiseEvent CopieEnCours(Me, copieEnCoursEventArgs)

   ' L'utilisateur a-t'il demandé l'annulation de la copie ?
If copieEnCoursEventArgs.Cancel Then
statutCopie = statutCopie.Annulation
Exit While
End If

   octet = fileStreamOriginal.ReadByte()
End While

  fileStreamOriginal.Close()
fileStreamDestination.Close()

 Catch

  ' On renvoit telle quelle l'exception qui a lieu,
' l'objectif étant simplement de pouvoir indiquer
' correctement le statut de la copie
statutCopie = statutCopie.Echec
Throw

 Finally

  dureeCopie = System.Environment.TickCount - dureeCopie

  ' On informe l'abonné de la fin de la copie
RaiseEvent CopieFin(Me, New CopieFinEventArgs(statutCopie, dureeCopie))

 End Try

End Sub

Ceci étant fait, il ne nous reste plus qu'à compiler l'ensemble de ce code dans une assembly (Librairie_CS.dll Librairie_VB.dll).

Enfin, pour illustrer l'utilisation de ces événements depuis une IHM, nous allons créer un projet de type Application Windows et déposer deux Label, deux TextBox, deux Button et une ProgressBar :

Puis voici le code client :

namespace ClientWindows_CS {

 public class Form1 : System.Windows.Forms.Form {

  ...

  private Librairie_CS.GestionFichiers gestionFichiers = null;
private float taille = 0;
private bool cancel = false;

  private void buttonCopie_Click(object sender, System.EventArgs e) {

gestionFichiers = new Librairie_CS.GestionFichiers(textBoxFichierSource.Text);

   gestionFichiers.CopieDemarrage += new Librairie_CS.CopieDemarrageEventHandler(gestionFichiers_CopieDemarrage);
gestionFichiers.CopieEnCours += new Librairie_CS.CopieEnCoursEventHandler(gestionFichiers_CopieEnCours);
gestionFichiers.CopieFin += new Librairie_CS.CopieFinEventHandler(gestionFichiers_CopieFin);

   gestionFichiers.CopieFichier(textBoxFichierDestination.Text, true);
}

  private void buttonAnnuler_Click(object sender, System.EventArgs e) {

   cancel = true;
buttonAnnuler.Enabled = false;
Application.DoEvents();
}

  private void gestionFichiers_CopieDemarrage(object sender, Librairie_CS.CopieDemarrageEventArgs e) {

   cancel = false;
taille = Convert.ToSingle(e.TailleFichier);
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
progressBar1.Value = 0;
buttonAnnuler.Enabled = true;

   Application.DoEvents();
}

  private void gestionFichiers_CopieEnCours(object sender, Librairie_CS.CopieEnCoursEventArgs e) {

   e.Cancel = cancel;
progressBar1.Value = Convert.ToInt32(Convert.ToSingle(e.Position) / taille * 100D);

   Application.DoEvents();
}

  private void gestionFichiers_CopieFin(object sender, Librairie_CS.CopieFinEventArgs e) {

   progressBar1.Value = 0;
buttonAnnuler.Enabled = false;
MessageBox.Show(this, string.Format("Statut : {0} - La durée du traitement a été de {1}", e.StatutCopie.ToString(), (new TimeSpan(0, 0, 0, 0, e.Duree)).ToString()), "Copie finie");
}

}

Public Class Form1
Inherits System.Windows.Forms.Form

    Private WithEvents gestionFichiers As Librairie_VB.GestionFichiers
Private taille As Single = 0
Private cancel As Boolean = False

    Private Sub buttonCopie_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles buttonCopie.Click
gestionFichiers = New Librairie_VB.GestionFichiers(textBoxFichierSource.Text)

        ' Autre moyen pour s'abonner aux évenements :
' -------------------------------------------
'AddHandler gestionFichiers.CopieDemarrage, AddressOf gestionFichiers_CopieDemarrage
'AddHandler gestionFichiers.CopieEnCours, AddressOf gestionFichiers_CopieEnCours
'AddHandler gestionFichiers.CopieFin, AddressOf gestionFichiers_CopieFin

        gestionFichiers.CopieFichier(textBoxFichierDestination.Text, True)
End Sub

    Private Sub ButtonAnnuler_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonAnnuler.Click
cancel = True
ButtonAnnuler.Enabled = False
Application.DoEvents()
End Sub

    Private Sub gestionFichiers_CopieDemarrage(ByVal sender As Object, ByVal e As Librairie_VB.CopieDemarrageEventArgs) Handles gestionFichiers.CopieDemarrage
cancel = False
taille = Convert.ToSingle(e.TailleFichier)
progressBar1.Minimum = 0
progressBar1.Maximum = 100
progressBar1.Value = 0
ButtonAnnuler.Enabled = True

        Application.DoEvents()
End Sub

    Private Sub gestionFichiers_CopieEnCours(ByVal sender As Object, ByVal e As Librairie_VB.CopieEnCoursEventArgs) Handles gestionFichiers.CopieEnCours
e.Cancel = cancel
progressBar1.Value = Convert.ToInt32(Convert.ToSingle(e.Position) / taille * 100D)

Application.DoEvents()
End Sub

    Private Sub gestionFichiers_CopieFin(ByVal sender As Object, ByVal e As Librairie_VB.CopieFinEventArgs) Handles gestionFichiers.CopieFin
progressBar1.Value = 0
ButtonAnnuler.Enabled = False
MessageBox.Show(Me, String.Format("Statut : {0} - La durée du traitement a été de {1}", e.StatutCopie.ToString(), (New TimeSpan(0, 0, 0, 0, e.Duree)).ToString()), "Copie finie")
End Sub

End Class

A noter l'utilisation du mot-clé WithEvents en VB.NET lors de la déclaration de la variable gestionFichiers pour permettre d'utiliser la technique d'abonnement Handles :

Private Sub gestionFichiers_[Event](ByVal sender As Object, ByVal e As Librairie_VB.EventArgs) Handles gestionFichiers.[Event]

A noter également qu'il y'a une autre possibilité pour s'abonner à un événement en VB.NET :

AddHandler gestionFichiers.[Event], AddressOf gestionFichiers

et

Private Sub gestionFichiers_[Event](ByVal sender As Object, ByVal e As Librairie_VB.EventArgs)

 

Voilà, j'espère que cet article vous a été utile pour comprendre la notion Delegate/Event. Si vous avez des commentaires à faire sur cet article, positifs comme négatifs, n'hésitez pas en m'en faire part grâce à la section commentaire ci-dessous.

 

A bientôt pour un autre sujet technique !

 

(les exemples de code utilisés dans cet article sont en libre téléchargement ici )

[Initialement posté le 21/09/2004 à 21:52 ici]

Comments

  • Anonymous
    September 12, 2006
    Bonjour,
    Un bien bel exemple ;)
    Juste une question pourquoi la durée est-elle en format int et non directement renvoyée en TimeSpan y a t'il une raison particulière à cela ? ou est-ce juste pour simplifier l'exemple ? (car c'est quand même exceptionnel que l'on tombe dans la limitation des 24.9 Jours ;)).
    En tout cas je garderais l'url dans un coin de mes favoris.
    Bien à vous,
    Icem@n

  • Anonymous
    September 12, 2006
    Salut,

    Non c'était plus pour garder le format d'origine de l'information (TickCount).

    Après y avoir refléchi un peu plus (suite à votre commentaire), je pense que je referais pareil, c'est-à-dire utiliser le type simple (int) pour stocker le nombre de milli-seconds, puis l'utilisation de TimeSpan pour manipuler cette notion de durée de manière un peu plus riche quand j'en ai besoin.

    Merci en tout cas pour vos encouragements,

  • Anonymous
    September 12, 2006
    Félicitations !

    Un article trés clair et bien commenté. Du miel en barre quoi :)
    Chapeau Bas ;)

    Bonne initiative de traduire les Exemples en VB et C#

    Ah si, juste un truc, à quand le prochain article ? :)

  • Anonymous
    September 12, 2006
    Vraiment un bon article sur les événements et delegates ! Du concentré de bonheur ...!

    Si tout le monde prenait le temps d'expliquer comme c'est fait ici, je crois que l'ignorance deviendrai rare !

  • Anonymous
    September 12, 2006
    Très bon article. Hop, je le save sur mon disque, il me sera utile.

  • Anonymous
    September 12, 2006
    Bonjour, très interessant comme article. Mais lorsque je test l'application je trouve que c'est extrèmenent lent comme transfert, est ce normal?

  • Anonymous
    September 12, 2006
    Super article!

    Félicitation,

    Cordialement

  • Anonymous
    September 12, 2006
    Merci pour vos encouragements !

  • Anonymous
    September 12, 2006
    Bravo pour cet article...
    je debute en vb.net...
    je voulais mettre en place des evenements... Ben j'ai trouvé mon bonheur...
    Bon vent pour la suite

  • Anonymous
    September 12, 2006
    [quote]Bonjour, très interessant comme article. Mais lorsque je test l'application je trouve que c'est extrèmenent lent comme transfert, est ce normal?
    [/quote]

    Je n'ai pas testé l'exemple mais peut être peux tu tester de faire une notification de copieEnCours tous les 5 octets au lieu de tous les octets ? (ca divise par 5 le nombre de communication Controle<->IHM)

    Ceci dit je ne pense quand même pas que copier un fichier (même de 10Mo) puisse réellement ralentir un système....

    D'autres personnes ont-elles ressenties le probleme ?

    Ps : en passant félicitations à l'auteur, je me suis mis A c# y'a quelques semaines et ma première question au bout de dix minutes était comment implémenter ce type de communication (selon le modèle MVC, Pac-amodeus et compagnie)... J'ai galéré sur le net pour trouver ma réponse alors cet article est extrémement bien fait ! Je reviendrais !

  • Anonymous
    September 12, 2006
    Merci pour tes encouragements !

    L'exemple que j'ai pris ne sert qu'à cela, c'est à dire un exemple. Je ne garantis pas que c'est la manière la plus rapide de copier un fichier :-)

    La copie d'un fichier était simplement un très bon concept pour illustrer les Events/Delegates.

  • Anonymous
    September 12, 2006
    Merci beaucoup, C super, ça m'a bien aidé pour mon projet!!!

  • Anonymous
    September 12, 2006
    Tu m'en vois ravi !

  • Anonymous
    September 12, 2006
    Bonjour,

    Pourrais tu me dire si une dll peut s'abonner a un evenement

    j'ai besoin d'un moyen pour que deux programmes communiquent, un qui est resident en memoire et l'autre qui est lance par une commande shell d'un autre programme.
    Je pensais donc utiliser une dll dans le style de ce que tu as proposé et de l'abonner a un evenement de ce programme cette dll generera ensuite un evenement pour le second programme resident en memoire
    Cela est il possible ou il y a t il une autre solution ?

    Merci

  • Anonymous
    September 12, 2006
    J'ai trouve une solution sans passer par les dll

    En reflechissant, l'idee des dlln'est pas tres bonne car meme si c'est possible, je vais faire deux appels distincts (un par le prog shell et l'autre par le prog resident) je vais donc avoir deux instances (je sais pas si c'est le bon nom) de dll dans la memoire, ce n'est pas tres propre

    je vais donc utiliser une technique avec l'evenement filewatch

    mais je suis toujours interesse par votre avis sur la communication avec dll ...

  • Anonymous
    March 20, 2007
    Très bon article, j'y vois plus clair maintenant. Merci

  • Anonymous
    June 20, 2007
    comment on peut une function event en vb.net dans une instruction if , j'ai un probleme avec ça , il me dit qu'il faut utilise raiseevent avent le fuction event , et quant je le fait il me donne une erreur sur aiseevrnt , si qqun a un exemple , je serai tres reconnaissant de m'en envoyer , merci mah_zouihed@yahoo.fr

  • Anonymous
    January 09, 2008
    PingBack from http://www.zizglu.com/?p=7

  • Anonymous
    January 23, 2008
    Pourquoi implémenter systématiquement des classes dérivées d'EventArgs pour passer des paramètres? Est-ce une raison technique ? J'ai choisi l'option de "caster" le sender pour obtenir l'émetteur et toutes ces infos. En utilisant des interfaces, on peut restreindre l'info disponible et garantir le couplage faible entre couche. Est-ce un mauvais choix ?

  • Anonymous
    January 30, 2008
    Superbe, Merci pour ton exemple qui est super clair, je crois même que j'ai compris.

  • Anonymous
    January 20, 2010
    Merci beaucoup pour cet exemple. c'est vraiment très intéressant et très clair!!! j'aurai juste une questiom sur le passage de paramètre à la form. dans le sub qui prend en charge l'évènement "CopieEnCours" (dans la form): Private Sub gestionFichiers_CopieEnCours(ByVal sender As Object, ByVal e As Librairie_VB.CopieEnCoursEventArgs) Handles gestionFichiers.CopieEnCours        e.Cancel = cancel        progressBar1.Value = Convert.ToInt32(Convert.ToSingle(e.Position) / taille * 100D)        Application.DoEvents()    End Sub Comment se fait-il que la valeur du e.cancel soit renvoyé à l'instance gestionFichier étant donné que le "e" qui est un  CopieEnCoursEventArgs est passé par Valeur et pas par Référence? la valeur e.cancel est renvoyée et utilisée dans la sub "Public Sub CopieFichier" et prend bien en compte une possible modification de sa valeur(dans la classe GestionFichiers):   Dim copieEnCoursEventArgs As copieEnCoursEventArgs = New copieEnCoursEventArgs(fileStreamOriginal.Position)   RaiseEvent CopieEnCours(Me, copieEnCoursEventArgs)   ' L'utilisateur a-t'il demandé l'annulation de la copie ?   If copieEnCoursEventArgs.Cancel Then J'avoue ne pas bien comprendre comment cela est possible, pourtant cela fonctionne! Est-ce que tu pourrais m'éclairer?

  • Anonymous
    January 20, 2010
    The comment has been removed

  • Anonymous
    January 26, 2010
    Merci beaucoup Pascal! là je comprends beaucoup mieux. encore bravo pour ton code vraiment très utile et très bien expliqué. Florent