Multithreading et Forms (delegate, class et invoke)

Résolu
shackleton1986 Messages postés 9 Date d'inscription jeudi 12 janvier 2006 Statut Membre Dernière intervention 21 mars 2007 - 12 mars 2007 à 11:45
NHenry Messages postés 15032 Date d'inscription vendredi 14 mars 2003 Statut Modérateur Dernière intervention 26 janvier 2023 - 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 :)
A voir également:

6 réponses

NHenry Messages postés 15032 Date d'inscription vendredi 14 mars 2003 Statut Modérateur Dernière intervention 26 janvier 2023 156
13 mars 2007 à 10:05
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
3
NHenry Messages postés 15032 Date d'inscription vendredi 14 mars 2003 Statut Modérateur Dernière intervention 26 janvier 2023 156
12 mars 2007 à 12:04
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
0
shackleton1986 Messages postés 9 Date d'inscription jeudi 12 janvier 2006 Statut Membre Dernière intervention 21 mars 2007
12 mars 2007 à 13:39
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
0
shackleton1986 Messages postés 9 Date d'inscription jeudi 12 janvier 2006 Statut Membre Dernière intervention 21 mars 2007
12 mars 2007 à 13:42
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 ?
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
DominicLavoie Messages postés 4 Date d'inscription mercredi 9 juin 2004 Statut Membre Dernière intervention 19 mars 2008
19 mars 2008 à 14:05
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
0
NHenry Messages postés 15032 Date d'inscription vendredi 14 mars 2003 Statut Modérateur Dernière intervention 26 janvier 2023 156
19 mars 2008 à 15:32
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
0
Rejoignez-nous