IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

VB.NET Discussion :

Ordre d’exécution de plusieurs délégués d'un même événement [Débutant]


Sujet :

VB.NET

  1. #1
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut Ordre d’exécution de plusieurs délégués d'un même événement
    Bonjour à tous,
    J'ouvre un 2eme post pour un problème ou plutôt un questionnement sur les événements.
    Si j'ai bien compris le principe, on peux s'abonner à un événement dans plusieurs class et je pense avoir compris que c'est même recommandé lorsque l'on multiplie les class et les objets instanciés. Par contre j'ai pas bien saisie l'ordre dans lequel s’exécute les différents délégués (je crois que c'est comme ça qu'on dit )

    Par exemple dans le code suivant quelle class va exécuter FaitCeci en premier ? Il semble que cela ne soit pas aléatoire (heureusement) mais j'ai pas bien compris la logique de chronologie.

    Est ce qu'une méthode délégué doit avoir fini son exécution pour que la méthode d'un autre délégué d'un même événement puisse commencer. Autrement dit est ce que c'est du code synchrone ?

    Aussi, d'après les quelques tests que j'ai fait il me semble que Application.Doevents arrive à bloquer les événements de la classe en cours pour laisser s’exécuter les événements des autres class, vous pouvez m'en dire plus ?

    Et pour finir il semble impossible de tracer l'ordre d’exécution de plusieurs délégués d'un même événement avec des points d'arrêts .

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Public Class MaClass1
    shared sub New
    AddHandler Objet1.FaitCeci, AddressOf FaitCeci
    End Sub
    Private Shared Sub FaitCeci
    End Sub
    End Class
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Public Class MaClass2
    shared sub New
    AddHandler Objet1.FaitCeci, AddressOf FaitCeci
    End Sub
    Private Shared Sub FaitCeci
    End Sub
    End Class
    Désolé pour toutes ces questions mais je franchis une étape supérieur en essayant de coder selon les principes de POO, et donc de nouvelles questions se posent.
    je peux aussi me tromper sur les termes employés, n’hésitez pas a rectifier .
    En vous remerciant d'avance.

  2. #2
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Bon, j'ai bien fait de faire quelques tests....je suis extrêmement surpris du fonctionnement des événements.
    Je vous laisse le code ca sera plus parlant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
      Public Class bgwManagerEngine
            Private Shared _bgwChaining As Boolean
            Public Shared LoadingDataCompleted As Boolean
            Public Shared BgwList As New List(Of BackgroundWorker)
            Private Shared _Completed As New List(Of Boolean)
            Public Shared Event AllLoadingDataCompleted(ByVal e As EventArgs)
     
            Shared Sub New()
     
            End Sub
            Public Shared Sub Add(ByRef bgw As BackgroundWorker)
                If Not BgwList.Contains(bgw) Then
                    BgwList.Add(bgw)
                    _Completed.Add(False)
                    AddHandler bgw.RunWorkerCompleted, AddressOf bgwCompleted
                End If
            End Sub
            Public Shared Sub RunAllWorkerAsync(Chaining As Boolean)
                LoadingDataCompleted = False
                If Chaining Then
                    _bgwChaining = True
                    BgwList(0).RunWorkerAsync()
                Else
                    For i As Integer = 0 To BgwList.Count - 1
                        BgwList(i).RunWorkerAsync()
                    Next
                End If
            End Sub
     
            Private Shared Sub bgwCompleted(sender As System.Object, e As RunWorkerCompletedEventArgs)
                Console.WriteLine(String.Format("bgwCompletedManager Début"))
                Dim Mybgw As BackgroundWorker = CType(sender, BackgroundWorker)
                Dim Index As Integer = BgwList.IndexOf(Mybgw)
                Console.WriteLine(String.Format("bgwCompleted Index : {0}", Index))
                _Completed(Index) = True
                LoadingDataCompleted = IsAllCompleted()
                If IsAllCompleted() Then
                    LoadingDataCompleted = True
                    RaiseEvent AllLoadingDataCompleted(New EventArgs)
                End If
                If _bgwChaining AndAlso Index < BgwList.Count - 1 Then BgwList(Index + 1).RunWorkerAsync()
                Console.WriteLine(String.Format("bgwCompletedManager Fin"))
            End Sub
            Private Shared Function IsAllCompleted() As Boolean
                For Each Item As Boolean In _Completed
                    If Item = False Then
                        Return False
                    End If
                Next
                Return True
            End Function
        End Class
    Un exemple d'abonnement aux RunWorkerCompleted dans Class1 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    Public Class Class1
    Puplic Sub New
            AddHandler bgw1.RunWorkerCompleted, AddressOf bgw1RunWorkerCompleted
            AddHandler bgw2.RunWorkerCompleted, AddressOf bgw2RunWorkerCompleted
            AddHandler bgw3.RunWorkerCompleted, AddressOf bgw3RunWorkerCompleted
    End Sub
    Private Sub Run
    bgwManagerEngine.RunAllWorkerAsync(True)
    End Sub
    Private Sub bgw1RunWorkerCompleted(sender As System.Object, e As RunWorkerCompletedEventArgs)
    Console.WriteLine("bgw1 Début"))
    Application.DoEvents ' Ligne de code éventuel........
    Console.WriteLine("bgw1 Fin"))
    End Sub
    Private Sub bgw2RunWorkerCompleted(sender As System.Object, e As RunWorkerCompletedEventArgs)
    Console.WriteLine("bgw2 Début"))
    Application.DoEvents ' Ligne de code éventuel........
    Console.WriteLine("bgw2 Fin"))
    End Sub
    Private Sub bgw3RunWorkerCompleted(sender As System.Object, e As RunWorkerCompletedEventArgs)
    Console.WriteLine("bgw3 Début"))
    Application.DoEvents ' Ligne de code éventuel........
    Console.WriteLine("bgw3 Fin"))
    End Sub
    End Class
    Comme vous pouvez le voir cette class me sert de gestionnaire de BackgroundWorker et plus précisement permet d’enchaîner l'exécution de plusieurs BackGroundWorker les uns à la suite des autres. Par exemple bgw1, bgw2, bgw3...etc
    Les BackgroundWorker que j'ajoute à cette class par la méthode Add sont Public est restent accessible pour que mes autres class puissent s'abonner aux ProgressChanged, DoWork et RunWorkerCompleted.
    C'est donc bgwManagerEngine qui doit imposer l'ordre d'exécution des bgw et plus particulièrement des RunWorkerCompleted auxquels je m'abonne dans les autres Class.

    Et donc voici ce qui se passent :
    - Si je n'ai pas de lignes de code avec Application.Doevents voici ce que j'obtiens :
    bgw1 Début
    bgw1 Fin
    bgw2 Début
    bgw2 Fin
    bgw3 Début
    bgw3 Fin

    - Si j'ai des lignes de code avec Application.DoEvents :
    bgw1 Début
    bgw1 Fin
    bgw2 Début
    bgw3 Début
    bgw3 Fin
    bgw2 Fin

    J'ai un peu simplifié mais dans l'idée c'est ça. Sur la 2eme liste on voit que bgw2RunWorkerCompleted n'a pas fini de s’exécuter quand bgw2RunWorkerCompleted commence.
    Le code fonctionne, certe, mais de manière asynchrone et ça ne me plait pas vraiment (surtout sur ce type d'application).

    Vous en pensez quoi ? et est ce qu'il existe une alternative ?. Je n'ai jamais utilisé le mot clé WithEvents par exemple, je ne sais pas si ca peux m'aider .

  3. #3
    Expert confirmé
    Avatar de wallace1
    Homme Profil pro
    Administrateur systèmes
    Inscrit en
    Octobre 2008
    Messages
    1 966
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Administrateur systèmes
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Octobre 2008
    Messages : 1 966
    Points : 4 005
    Points
    4 005
    Billets dans le blog
    7
    Par défaut
    Bonsoir,

    Je te rappelle que le composant BackgroundWoker effectue des taches asynchrones !!!
    Il n est pas judicieux de vouloir tracer l'ordre d'exécution puisque aucune tache n'est bloquante...
    Si tu veux obtenir les événements de progression il faut le faire avec l'event ProgressChanged (appel de la méthode ReportProgress) qui possède au sein de sa signature 2 arguments : ProgressPercentage (int) et UserState (Object)
    L'event Completed est déclenché à la toute fin du traitement de la tache asynchrone.

    Le composant BackgroundWorker est à mon sens l'un des plus simples gestionnaire concernant l'utilisation des taches async.
    Les puristes diront que les thread sont plus puissants et d'autres diront async et await ont remplacés le BGW et d'autres évoqueront les Task...... : m'en fiche le BGW est bien plus pratique c'est tout !

    On évite d'utiliser : Application.DoEvents c'est une très mauvaise pratique de programmation car on maîtrise encore moins la file d'attente des messages qui véhiculent au sein de ton projet.....

    Considères que lorsque tu appelles BGW.DoWork sa tache se termine obligatoirement dans BGW.RunWorkerCompleted le cas contraire il faut utiliser du thread pool synchronisé grâce à la méthode .Join le 2ème thread exécuté attends que le 1er ai terminé sa tache avant de se lancer (idem pour les autres thread suivants à volonté). Attention je ne dis pas que ce soit impossible de le faire avec un BGW mais ça reste de la bidouille !

    EDIT : tient c'est marrant qu'on en parle car dans mon projet actuel j'ai bataillé avec du Thread Pool........

    Je sais pas si j'ai été très clair.... ^^

  4. #4
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Bonsoir Wallace1, et merci pour ta réponse.
    je fait parfois des erreurs mais il y a que comme ça qu'on apprends...

    Je te rappelle que le composant BackgroundWoker effectue des taches asynchrones !!!
    J’avoue que j'ai eu un doute sur la pertinence de ma solution en lisant ça...
    Sinon c'est bien le Dowork qui est asynchrone par rapport au Thread depuis lequel on lance le bgw ? (appelons le ThreadUI pour simplifier) et ensuite le RunWorkerCompleted ainsi que le ProgressChanged rejoignent le ThreadUI si je dit pas de bêtises ?

    Les threads je m'en sert un peu mais sans join (j'ai pas le niveau pour prendre ce risque )

    Le DoEvents je m'en sert parfois pour améliorer un peu l'affichage mais c'est vrai que je peux m'en passer, on verra très peu la différence.
    En tout cas je suis pas mécontent d'avoir découvert ce comportement de Dovents sur les événements. Ça va me permettre d'esquiver pas mal de problème par la suite.
    Cela dit je sait pas encore comment je vais faire....Supprimer les DoEvents (sans doute) ou faire encore différemment (jarrette pas de faire et refaire, je vais bien finir par y arriver )

    En tout cas merci beaucoup tes conseils.

  5. #5
    Expert confirmé
    Avatar de wallace1
    Homme Profil pro
    Administrateur systèmes
    Inscrit en
    Octobre 2008
    Messages
    1 966
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Administrateur systèmes
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Octobre 2008
    Messages : 1 966
    Points : 4 005
    Points
    4 005
    Billets dans le blog
    7
    Par défaut
    Pour mettre correctement a jour ton UI, il faut combiner ton BGW avec des appels de delegués (tu feras du ThreadSafe de cette facon).
    Exemple : le controle Label1 et Label2 detiennent le handle sous-jacent de FrmMain :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    FrmMain.Invoke(New Action(Sub()
                    Label1.Text = "Exemple1"
                    Label2.Text = "Exemple2"
         End Sub))
    Une bonne pratique voudrait qu on imbrique Invoke dans une clause :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    If FrmMain.InvokeRequired Then
        FrmMain.Invoke(New Action(Sub()
                        Label1.Text = "Exemple1"
                        Label2.Text = "Exemple2"
              End Sub))
    Else
        Label1.Text = "Exemple1"
        Label2.Text = "Exemple2"
    End If
    PS : il existe aussi BeginInvoke et EndInvoke.... mais il reside ici une notion de callback..... breff ca devient technique la..et je risquerai d en perdre mon latin....^^

  6. #6
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Bonjour Wallace1,
    Merci beaucoup pour tes explications.

    Et voilà comment on apprends par l'erreur :

    Application.DoEvents méthode ()
    Calling this method causes the current thread to be suspended while all waiting window messages are processed. If a message causes an event to be triggered, then other areas of your application code may execute. This can cause your application to exhibit unexpected behaviors that are difficult to debug. If you perform operations or computations that take a long time, it is often preferable to perform those operations on a new thread. For more information about asynchronous programming, see Asynchronous Programming Model (APM).
    L'appel de cette méthode entraîne la suspension du thread en cours pendant le traitement de tous les messages de la fenêtre en attente. Si un message provoque le déclenchement d'un événement, d'autres zones du code de votre application peuvent s'exécuter. Cela peut amener votre application à afficher des comportements inattendus difficiles à déboguer. Si vous effectuez des opérations ou des calculs longs, il est souvent préférable d'effectuer ces opérations sur un nouveau thread. Pour plus d'informations sur la programmation asynchrone, voir Modèle de programmation asynchrone (APM).
    Plus j'y pense et plus je me dit que cette ligne de code est une aberration mais il faudrait que je sache au moins à quoi elle sert vraiment pour juger....
    Donc voilà ou j'en suis aujourd'hui, je me dit que les événements sont difficilement contournables lorsque qu'on veux faire un minimum de POO et que donc le DoEvents est bien à proscrire ou à minima a ne pas mettre dans du code susceptible d'être appelé par un événements.
    En tout cas je vais repenser mon BgwManager autrement .

    Je clôture ce post sauf si il y a encore des choses à dire, je pense que j'ai bien compris le problème.
    En te remerciant.

  7. #7
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    Citation Envoyé par BasicZX81 Voir le message
    je me dit que les événements sont difficilement contournables lorsque qu'on veux faire un minimum de POO
    on peut faire de la poo sans event, un callback par exemple n'est pas un event mais fait à peu près la même chose
    et les events existaient sur des langages non orientés objet


    sinon je confirme doevents est à proscrire
    concernant son fonctionnement, ton processus graphique utilise la pompe à message de windows pour effectuer tout ce qui est interaction et rafraichissement
    ceci est sur le même thread que ton code (par défaut, hors thread/bgw/task ...)
    donc quand du code à toi s'exécute et est long (une grosse requete sur un clic de bouton par exemple), windows peut demander à la fenetre de se redessiner, ceci n'est pas possible à cet instant, donc c'est mis sur la pile
    si tu clics sur un bouton pendant le traitement là aussi le clic est mis sur la pile
    quand ca sort de ton code ca dépile alors les events à traiter (refresh puis clic)

    doevents permet de mettre pause dans ton code pour forcer à vider la pile puis revenir à ton code
    ca permet donc aux débutants de faire du code qui ne freeze par l'interface en cas de boucle
    mais ca leur permet aussi de faire des gros bugs en tout genre, par exemple l'utilisateur clic sur fermer la fenetre pendant le traitement, le doevents permet de fermer la fenetre, puis ca revient dans ton code, et s'il y a des choses genre me.textbox1.text = "a" alors ca plante car la fenetre a été détruite entre temps sans sortir de cette sub, sans le doevents la fenetre n'aurait pas pu se fermer avant d'avoir fini le code et n'aurait pas planté
    et ca peut même planter plusieurs minutes après la fermeture si l'accès à la fenetre se fait après une boucle longue
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  8. #8
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Trés trés interressant Pol63....Merci beaucoup.
    Du coup avec Wallace1 vous m'avez convaincu, DoEvents ne me plait pas du tout.

    Je n'avais jamais fait le rapprochement entre les messages Windows (Requettes utilisateur et affichage) et les événements...J’apprends donc que les Evénements sont gérés par la pompe à message Windows alors.
    Je me risque à poser une autre question (j'en profite ) Est ce que c'est pour ça que parfois je vois "Code externe" dans la pile des appels de VS ? Ça veux dire que dans cette phase c'est Windows qui prend la main pour traiter ses messages ?

    Aussi je retiens que sans DoEvents je n’aurais pas le genre de problème cités. Dans ce cas comment je peux effectuer mes choix entre des événements et un Callback (que je ne connais pas encore) ? Est ce qu'il y a des situations particulières pour utiliser l'un plutôt que l'autre ?

    Je vais me renseigner de mon coté en tout cas car tous ça m'interresse vraiment beaucoup.

  9. #9
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    la pompe à message windows est gérée par le framework, et transforme les messages en events .net (ou pas certains sont gérés directement), mais ce n'est pas la même chose

    code externe oui ca doit être framework ou clr ou autre, mais ce n'est pas ton code
    le framework .net est codé en .net, et on peut activer le chargement des pdb et du code source du framework permettant de faire du pas à pas dedans
    auquel cas quand tu arrives sur button1_click tu peux voir que ca vient de la méthode OnClick de la classe Button (ou Control dont Button hérite), et avant ca dans le code de gestion de la pompe à message de la classe Control

    utilises les évènements c'est plus évident que les callbacks
    après certaines classes du framework n'en ont pas et utilisent à la place des callbacks (genre les sockets avec beginaccept ou beginread), mais il faut juste passer un pointeur vers une sub, avec addressof par exemple
    les callbacks sont utilisés surtout dans des contextes ou un autre thread est créé pour éviter un blocage, ce thread doit alors savoir qui rappeler quand c'est terminé
    (dans le cas du tcp, on demande la lecture des données entrantes, mais on ne sait pas quand elles vont arriver, un thread est créé pour attendre des octets, ca nous rend la main tout de suite, et quand des octets arrivent ca appelle le callback pour qu'on traite les octets)


    concernant WithEvents, c'est un mot clé de vb qui est nécessaire à l'utilisation de Handles (ceci n'existe pas en c#)
    par défaut en winforms les controles sont déclarés avec withevents, et quand on double clic sur un bouton on arrive dans private sub button1_click (...) handles button1.click
    (quand on pose un bouton sur une form ca écrit private withevents button1 as button dans le fichier .designer.vb caché)
    quand on déclare des variables dans notre code on peut ajouter withevents aussi, auquel cas on peut ensuite écrire des sub avec handles cettevariable.tel_event
    à la compilation ca créé une propriété qui pointe vers la variable, et gère des addhandler et removehandler, avec gestion du changement du contenu de la variable pour que la nouvelle soit handlée et l'ancienne déhandlée (bien pratique donc)
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  10. #10
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Merci beaucoup Pol63

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. L'ordre des évènements des pages
    Par zooffy dans le forum ASP.NET
    Réponses: 3
    Dernier message: 16/01/2008, 14h38
  2. [VBA]L'ordre des évènements de suppression
    Par DelphiManiac dans le forum Access
    Réponses: 4
    Dernier message: 10/10/2006, 21h56
  3. [C#] Ordre des évènements
    Par Amara dans le forum ASP.NET
    Réponses: 5
    Dernier message: 02/06/2006, 11h52
  4. [C#] Ordre des évènements
    Par Amara dans le forum Windows Forms
    Réponses: 4
    Dernier message: 02/06/2006, 11h34
  5. ordre d'exécution des événements onblur et onfocus
    Par letycaf dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 23/05/2006, 15h30

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo