我正在使用vb.net,并且在我的程序中,当我运行我的后台工作程序时,我会得到这个'crossthread操作无效'错误,这将使此文本框启用为true。我的主要sub将首先将enabled设置为false,当backgroundworker运行时,它将返回true,然后退出。为什么它会给我一个错误?仅供参考:有更多的代码,但我不想让它更令人困惑......
这是堆栈跟踪:
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)
以下是确切的错误消息:
{"Cross-thread operation not valid: Control 'mainText' accessed from a thread other than the thread it was created on."}
有人可以帮帮我!
谢谢,
凯文
答案 0 :(得分:21)
BackgroundWorker
类的目的是在GUI保持响应的同时在非GUI 线程上执行工作。除非您将Control.CheckForIllegalCrossThreadCalls
设置为false
(您不应该这样做),或者按照其他答案(我也不建议使用)中的建议使用Invoke
,否则您将会去获取非法的跨线程操作异常。
如果您想要与GUI相关的“内容”发生而您的BackgroundWorker
正在运行,我通常建议使用BackgroundWorker.ReportProgress
方法并附加适当的处理程序BackgroundWorker.ProgressChanged
事件。如果您希望在BackgroundWorker
完成后在GUI上发生某些事情,则只需附加处理程序即可将GUI更新为BackgroundWorker.RunWorkerCompleted
事件。
答案 1 :(得分:12)
在Form1_Load
(或任何形式)sub:
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
它解决了阻塞的跨线程操作的所有问题。
答案 2 :(得分:10)
您在何处设置Enabled
属性?如果你在DoWork
事件处理程序中执行此操作,则此代码运行在与创建按钮不同的线程上,这应该会给出您遇到的异常。要解决此问题,您应该使用BeginInvoke
。为方便起见,它可以包装成一个方法,如:
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
现在您可以安全地调用该方法来启用或禁用任何线程的任何控制:
SetControlEnabled(someButton, False)
答案 3 :(得分:8)
在VB.NET中更好的方法是使用Extension
它为跨线程GUI控制调用制作非常漂亮的代码。
只需将此行代码添加到您拥有的任何模块中。
<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
现在,您可以编写跨线程控制代码,该代码对于任何控制调用只有1行长。
就像这样,假设您要清除一个ComboBox并且它是从线程调用的,或者没有线程,您可以立即使用它来执行此操作
cboServerList.Invoke(Sub() cboServerList.Items.Clear())
想要在清除后添加内容吗?
cboServerList.Invoke(Sub() cboServerList.Items.Add("Hello World"))
答案 4 :(得分:2)
您不能直接从另一个线程设置UI线程上的控件属性。可以这样做,这是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
答案 5 :(得分:1)
你的Form_Load()请写下面的代码部分。你的所有问题都将得到解决。
'## crossed-thread parts will not be controlled by this option...
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
答案 6 :(得分:0)
建议使用AutomationPeer。
例如下面的代码调用当我按F5键时执行按钮。同样,如果您希望线程或后台工作程序调用事件或函数,则可以使用Automation Peer。在您的情况下,您可以使用具有相应属性的文本框来代替按钮(我在此处使用)。
'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
此致 RV
答案 7 :(得分:-1)
CheckForIllegalCrossThreadCalls = False