私はvb.netを使用しています。私のプログラムでは、このテキストボックスを有効にするバックグラウンドワーカーを実行すると、この「クロススレッド操作が無効です」というエラーが表示されます。私のメインサブは最初に有効をfalseに変更し、バックグラウンドワーカーが実行すると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."}
誰かが私を助けてくれますか?
おかげで、
ケビン
BackgroundWorker
クラスの目的は、GUIの応答性を維持しながらnon-GUIスレッドで作業を実行することです。 Control.CheckForIllegalCrossThreadCalls
をfalse
に設定しない限り(これは推奨されません)、または他の回答で提案されているようにInvoke
を使用しない限り(推奨しません)、次のようになります。不正なスレッド間操作例外。
GUI関連の「もの」を発生させたい場合whileBackgroundWorker
が実行されている場合、通常、BackgroundWorker.ReportProgress
メソッドを使用し、BackgroundWorker.ProgressChanged
イベントに適切なハンドラーをアタッチすることをお勧めします。 BackgroundWorker
がfinishedになったときにGUIで何かを実行したい場合は、ハンドラーをアタッチしてGUIをBackgroundWorker.RunWorkerCompleted
イベントに更新します。
Form1_Load
(またはフォームが何であれ)サブに次のコードを入力します。
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
ブロックされたクロススレッド操作に関するすべての問題を修正します。
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"))
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)
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
Form_Load()plsは、コード部分の下に記述します。すべての問題が解決されます。
'## crossed-thread parts will not be controlled by this option...
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
AutomationPeerの使用を提案します。
以下の例では、F5キーを押すと、実行ボタンが呼び出されます。同様に、スレッドまたはバックグラウンドワーカーでイベントまたは関数を呼び出す場合は、オートメーションピアを使用できます。ボタンの代わりに(ここで使用したもの)、適切なプロパティを持つテキストボックスを使用して呼び出すことができます。
'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