Le problème
Commencez par ouvrir Visual Studio - peu importe la version - et agencez rapidement une interface graphique, par exemple, comme ceci :

Vous pouvez vous passer du label. En fait, seule le champ "TextEdit" et le bouton nous importent.
On va faire en sorte que lorsque nous cliquons sur le bouton, un thread se créé, se lance, et mette à jour le champ TextEdit pour y afficher un "Hello world".
Oui, bon, ok, pas besoin de thread pour ça, mais c'est pour l'exemple.
Créez un champ "BackgroundWorker" en prime. Ensuite, on va dans le code de notre formulaire et on insère la méthode suivante :
Private Sub DoStuff()
' Mise à jour du TextBox
Me.TextBox1.Text = "Hello world!"
End Sub
Cette méthode sera appelée par le BackgroundWorker :
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Me.DoStuff()
End Sub
Et on "lancera" le BackgroundWorker après clic sur notre bouton :
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Me.BackgroundWorker1.RunWorkerAsync()
End Sub
On lance la fenêtre, on clique sur notre bouton et, là, c'est le drâme : une exception type InvalidOperationException est lancée. Le descriptif de celle-ci est :
Opération inter-threads non valide : le contrôle 'TextBox1' a fait l'objet d'un accès à partir d'un thread autre que celui sur lequel il a été créé..
Et c'est là qu'interviennent les "Delegates". Ils permettent aux différents threads d'accéder aux propriétés communes d'une classe. Pour ceux qui connaissent les mutex - exclusions mutuelles - le principe peut sembler le même, sauf qu'ici, nous n'aurons pas verrouiller ou déverrouiller quoi que ce soit.
Tout d'abord, nous allons créer une méthode publique qui mettra à jour notre TextEdit :
Public Sub MajTextBox1(ByVal chaine As String)
Me.TextBox1.Text = chaine
End Sub
Rien de bien sorcier. Ensuite, nous déclarons dans la liste des membres notre "Delegate" qu'on instanciera et à qui on fournira l'adresse de MajTextBox1. Il s'agit, en quelques sortes, d'une référence de fonction. Le delegate doit donc avoir la même signature que notre méthode MajTextBox1, qu'importe la visibilité de cette dernière. On aura donc à écrire :
Private Delegate Sub AccessMajTextEdit(ByVal chaine As String)
Ensuite, dans notre méthode DoStuff(), appelez la méthode "Invoke", qui va servir à appeler notre Delegate et donc, par la même occasion, la méthode MajTextBox1, les conflits en moins :
Private Sub DoStuff()
' Déclaration et instanciation du Delegate
Dim MyDelegate As New AccessMajTextEdit(AddressOf MajTextBox1)
' La propriété InvokeRequired indique si l'on a
' besoin d'appelé un délégué pour modifier une instance
' d'un autre thread
If Me.InvokeRequired Then
' Appel du delegate
Me.Invoke(MyDelegate, "Hello world !")
End If
End Sub
On relance notre petite application, on clique sur le bouton et, normalement, ...

C'était un petit article sans prétention. Je ne maîtrise véritablement pas le sujet, je ne saurais vous dire ce qu'est un Delegate en détail. On sait juste que ça marche. Vous pouvez très bien utiliser les threads au lieu des BackgroundWorker - ces derniers étant néanmoins plus faciles à utiliser, ce pourquoi je m'en suis servi.
Cette astuce m'est très utile au boulot, c'est pour ça que je l'insère ici pour ne pas l'oublier. En espérant que quelques programmeurs VB.NET puissent profiter.
Le code complet :
Public Class Form1
Private Delegate Sub AccessMajTextEdit(ByVal chaine As String)
Private Sub DoStuff()
' Instanciation du Delegate
Dim MyDelegate As New AccessMajTextEdit(AddressOf MajTextBox1)
' La propriété InvokeRequired indique si l'on a
' besoin d'appelé un délégué pour modifier une instance
' d'un autre thread
If Me.InvokeRequired Then
Me.Invoke(MyDelegate, "Hello world !")
End If
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Me.BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Me.DoStuff()
End Sub
Public Sub MajTextBox1(ByVal chaine As String)
Me.TextBox1.Text = chaine
End Sub
End Class
Ge0


