Multithreading et Forms (delegate, class et invoke) [Résolu]

shackleton1986
Messages postés
9
Date d'inscription
jeudi 12 janvier 2006
Dernière intervention
21 mars 2007
- 12 mars 2007 à 11:45 - Dernière réponse : NHenry
Messages postés
14295
Date d'inscription
vendredi 14 mars 2003
Dernière intervention
12 novembre 2018
- 19 mars 2008 à 15:32
Je vais essayer mon problème de la manière la plus simple ...
Mon but est de pouvoir modifier un contrôle TextBox de ma fenêtre principale  à partir de threads lancés dans des instances d'objets de ma fenêtre principale. Je pense qu'un bout de code vous éclairera un peu plus sur mes objectifs :

Imports System

Imports System.Threading

Imports System.Text

Public Class Form1
    Dim threadFils As Class1
    Dim threadNb As Integer = 0

    Delegate Sub SetTextDelegate(ByVal Text As String)
    Public Dim SetTextMarshaler As SetTextDelegate = AddressOf Me.setText

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        TextBox1.Text = "Hello !"
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        threadNb += 1
        setText("Création de l'instance " & CStr(threadNb))
        threadFils = New Class1(threadNb)
        threadFils.launch()
    End Sub

    Private Sub setText(ByVal Text As String)
        TextBox1.Text &= vbCrLf & Text
    End Sub
End Class

Public Class Class1
    Private id As Integer

    Public Sub New(ByVal monId As Integer)
        id = monId
    End Sub

    Public Sub main()
        SyncLock Me
            'Ces deux lignes ont pour but d'afficher dans la forme principale
            'les traces d'éxécution de ce thread
            Dim args() As Object = {"Thread " & CStr(id) & " lancé ..."}
            Form1.Invoke(Form1.SetTextMarshaler, args)

        End SyncLock
    End Sub

    Public Sub launch()
        Dim thread As New Thread(AddressOf main)
        thread.Start()
    End Sub

End Class

J'ai donc utilisé des délégués et la méthode invoke de la classe Form1 mais sans succès. J'ai vu des exemples qui fonctionnent losque c'est le même objet qui appel la méthode invoke et qui crée le thread. Hors là, mon thread est crée dans une autre classe :s
Je pense qu'il ne me manque pas grand chose mais pour l'instant je suis bloqué.
Merci d'avance :)
Afficher la suite 

Votre réponse

6 réponses

Meilleure réponse
NHenry
Messages postés
14295
Date d'inscription
vendredi 14 mars 2003
Dernière intervention
12 novembre 2018
- 13 mars 2007 à 10:05
3
Merci
Bonjour


Logique, tu tente d'utiliser ta fenetre sans l'avoir chargée :


Essaye ça :
Public Sub main()

    My.Forms.Form1.Show

    Application.DoEvents()

        SyncLock Me
           
Dim args() As Object = {"Thread " & CStr(id) & " lancé ..."}
            My.Forms.Form1.Invoke(My.Forms.Form1.SetTextMarshaler, args)
        End SyncLock
    End Sub

Il est plus facile de batiser quelqu'un que de la convertir.(surtout en programmation)
VB (6, A excel, .NET), C++, C#.Net

Merci NHenry 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 94 internautes ce mois-ci

Commenter la réponse de NHenry
NHenry
Messages postés
14295
Date d'inscription
vendredi 14 mars 2003
Dernière intervention
12 novembre 2018
- 12 mars 2007 à 12:04
0
Merci
Bonjour


ATTENTION, VB.NET ne permet pas d'appeller les forms comme en VB6.

Si tu es sous .NET1 (2003), il faut créer une variable accessible à tout ton projet et inititalisé par l'event Load de la form

Si tu es sous .NET2 (2005), utilise My.Forms.Form1


Pense aussi à donner des noms cohérents à tes objets (évite de garder les noms pas défauts).

Il est plus facile de batiser quelqu'un que de la convertir.(surtout en programmation)
VB (6, A excel, .NET), C++, C#.Net
Commenter la réponse de NHenry
shackleton1986
Messages postés
9
Date d'inscription
jeudi 12 janvier 2006
Dernière intervention
21 mars 2007
- 12 mars 2007 à 13:39
0
Merci
Merci pour ta réponse mais je n'arrive toujours pas à faire fonctionner mon programme.
Je suis sous .NET2 (2005) j'ai donc essayé d'utiliser My.Forms.Form1 sans succès :

--> Méthode exécutée par le thread dans Class1 :

   Public Sub main()
        SyncLock Me
            My.Forms.Form1.SetTextMarshaler("Thread " & CStr(id) & " lancé ...")
        End SyncLock
    End Sub

J'ai aussi essayé de cette manière là :

   Public Sub main()

        SyncLock Me
            Dim args() As Object = {"Thread " & CStr(id) & " lancé ..."}
            My.Forms.Form1.Invoke(My.Forms.Form1.SetTextMarshaler, args)
        End SyncLock

    End Sub

Aurais-tu la syntaxe exact a utiliser ou la méthode pour réaliser ce que je veu ?

Merci
Commenter la réponse de shackleton1986
shackleton1986
Messages postés
9
Date d'inscription
jeudi 12 janvier 2006
Dernière intervention
21 mars 2007
- 12 mars 2007 à 13:42
0
Merci
En fait avec cette méthode ci :

   Public Sub main()
        SyncLock Me
            Dim args() As Object = {"Thread " & CStr(id) & " lancé ..."}
            My.Forms.Form1.Invoke(My.Forms.Form1.SetTextMarshaler, args)
        End SyncLock
    End Sub

J'obtiens l'exception suivante "InvalidOperationException : Impossible d'appeler Invoke ou BeginInvoke sur un contrôle tant que le handle de fenêtre n'a pas été créé."

Est ce que tu sais d'où cela vient ?
Commenter la réponse de shackleton1986
DominicLavoie
Messages postés
4
Date d'inscription
mercredi 9 juin 2004
Dernière intervention
19 mars 2008
- 19 mars 2008 à 14:05
0
Merci
Bonjour NHenry,


J'ai sensiblement le même problème (même erreur du moins) lorsque j'essai
d'appeller <st1:personname productid="la méthode Invoke" w:st="on">la méthode
Invoke</st1:personname> à partir d'un module, mais où le thread a été
initialiser à partir d'une form. Je vois aussi que l'erreur va être évitée si
on charge la fenêtre avant de faire le Invoke.


Mais j'ai quand même un problème. Comme mon application est de type Windows
Form, le formulaire principale est chargée à l'ouverture de l'application.
L'usager choisi différentes opérations qu'il désire effectuées et clique sur un
bouton qui va prendre beaucoup de temps à s'exécuter. C'est là que j'ai besoin
de faire du multithreading afin de montrer à l'écran l'évolution (progress bar
et label) de l'opération qui prend du temps.


Mais comme le formulaire est chargé, <st1:personname productid="la ligne My.Forms" w:st="on">la ligne My.Forms</st1:personname>.Form1.Show montre
à nouveau le formulaire, ce qui ne doit, évidemment pas, survenir.


Comment je fais pour que le formulaire n'apparaisse pas tout en ayant la
possibilité de séparer le code dans le formulaire et dans des modules.


Merci de votre réponse
Commenter la réponse de DominicLavoie
NHenry
Messages postés
14295
Date d'inscription
vendredi 14 mars 2003
Dernière intervention
12 novembre 2018
- 19 mars 2008 à 15:32
0
Merci
Bonjour

Soit tu utilises un BackGroundWorker (ne me demande pas d'aide, je ne l'ai jamais utilisé), soit tu mets dans une variable globale la Form qui sert de synchro et ton thread va l'utiliser pour intervenir.

Sinon, une autre approche (celle que je préfère), Tu mets en global une variable structurée qui permet au thread de signaler son avancée, puis avec un Timer, tu récupère ces infos que tu affiches (donc pas besoin de synchro d'affichage).

Pour masquer un Form sans la déchargée, tu peux utiliser la méthode Hide().

Nous captons le cockpit coupable qui a capoté
VB (6, .NET1&2), C++, C#.Net1
Commenter la réponse de NHenry

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.