J'utilise vb.net et, dans mon programme, le message d'erreur '' Crossthread Operation Not Valid '' s'affiche lorsque j'exécute le travail de fond qui rendra cette zone de texte activée. Mon sous-traitant principal commence par activer la valeur false, puis, une fois le travail d'arrière-plan exécuté, il le rendra à nouveau puis à la sortie. Pourquoi ça me donne une erreur? FYI: Il y a plus de code à cela mais je ne veux pas le rendre plus déroutant ...
Voici la trace de la pile:
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.OnEnabledChanged(EventArgs e)
at System.Windows.Forms.Control.set_Enabled(Boolean value)
at Helium.Form1.BackgroundWorker1_DoWork(Object sender, DoWorkEventArgs e) in C:\Users\Kevin\documents\visual studio 2010\Projects\Helium\Helium\Form1.vb:line 167
at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
et voici le message d'erreur exact:
{"Cross-thread operation not valid: Control 'mainText' accessed from a thread other than the thread it was created on."}
Quelqu'un peut-il m'aider s'il vous plaît!
Merci,
KEvin
L'objectif de la classe BackgroundWorker
est d'effectuer un travail sur un thread non-GUI tant que l'interface graphique reste sensible. Sauf si vous définissez Control.CheckForIllegalCrossThreadCalls
sur false
(ce que vous ne devriez pas faire), ou utilisez Invoke
comme suggéré dans les autres réponses (ce que je ne recommanderais pas non plus), vous obtiendrez une exception d'opération de groupe croisé illégale.
Si vous voulez que les "commandes" liées à l'interface graphique se produisent pendant que votre BackgroundWorker
est en cours d'exécution, je vous recommanderais généralement d'utiliser la méthode BackgroundWorker.ReportProgress
et d'associer un gestionnaire approprié à l'événement BackgroundWorker.ProgressChanged
. Si vous souhaitez que quelque chose apparaisse sur l'interface graphique une fois que BackgroundWorker
est terminé , attachez simplement votre gestionnaire pour mettre à jour l'interface graphique à l'événement BackgroundWorker.RunWorkerCompleted
.
Tapez le code suivant dans le Form1_Load
(ou quel que soit votre formulaire):
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
Il corrige tous les problèmes liés aux opérations inter-thread bloquées.
Où définissez-vous exactement la propriété Enabled
? Si vous le faites dans le gestionnaire d'événements DoWork
, ce code s'exécute sur un thread différent de celui sur lequel le bouton a été créé, ce qui devrait donner l'exception que vous rencontrez. Pour résoudre ce problème, vous devez utiliser BeginInvoke
. Pour plus de commodité, il pourrait être intégré à une méthode, comme suit:
Private Sub SetControlEnabled(ByVal ctl As Control, ByVal enabled As Boolean)
If ctl.InvokeRequired Then
ctl.BeginInvoke(New Action(Of Control, Boolean)(AddressOf SetControlEnabled), ctl, enabled)
Else
ctl.Enabled = enabled
End If
End Sub
Maintenant, vous pouvez appeler cette méthode en toute sécurité pour activer ou désactiver n'importe quel contrôle depuis n'importe quel thread:
SetControlEnabled(someButton, False)
Une meilleure façon de procéder dans VB.NET consiste à utiliser une variable Extension
, ce qui donne un très beau code pour les appels de contrôle d'interface graphique multithreading.
Ajoutez simplement cette ligne de code à n’importe quel module que vous avez.
<System.Runtime.CompilerServices.Extension()> _
Public Sub Invoke(ByVal control As Control, ByVal action As Action)
If control.InvokeRequired Then
control.Invoke(New MethodInvoker(Sub() action()), Nothing)
Else
action.Invoke()
End If
End Sub
Vous pouvez maintenant écrire du code de contrôle multithread ne comportant qu'une ligne pour tout appel de contrôle.
Comme cela, disons que vous voulez effacer une ComboBox et qu'elle s'appelle à partir de threads ou sans threads, vous pouvez simplement utiliser le faire maintenant
cboServerList.Invoke(Sub() cboServerList.Items.Clear())
Voulez-vous ajouter quelque chose après l'avoir effacé?
cboServerList.Invoke(Sub() cboServerList.Items.Add("Hello World"))
Vous ne pouvez pas définir directement la propriété d'un contrôle qui se trouve sur le thread d'interface utilisateur à partir d'un autre thread. Cela peut être fait cependant, voici un exemple tiré de msdn.
Private Sub SetText(ByVal [text] As String)
' InvokeRequired required compares the thread ID of the'
' calling thread to the thread ID of the creating thread.'
' If these threads are different, it returns true.'
If Me.textBox1.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.textBox1.Text = [text]
End If
End Sub
Votre Form_Load () pls écrit ci-dessous la partie de code. Tous vos problèmes seront résolus.
'## crossed-thread parts will not be controlled by this option...
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
Suggérez d'utiliser AutomationPeer.
Par exemple, ci-dessous, le code appelle le bouton Exécuter lorsque j'appuie sur la touche F5. De même, si vous souhaitez qu'un thread ou un travailleur en arrière-plan appelle un événement ou une fonction, vous pouvez utiliser Automation Peer. Dans votre cas, au lieu du bouton (que j'ai utilisé ici), vous pouvez utiliser une zone de texte avec sa propriété appropriée à appeler.
'Button name=btnExecute
'Imports System.Windows.Automation.Peers
'Imports System.Windows.Automation.Provider
If e.Key = Key.F5 Then
Dim peer As New ButtonAutomationPeer(btnExecute)
Dim invokeProv As IInvokeProvider = TryCast(peer.GetPattern(PatternInterface.Invoke), IInvokeProvider)
invokeProv.Invoke()
End If
Cordialement RV