我一直在四处寻找,试图找出为什么我会得到这个例外无济于事。我希望有人之前见过这个:
我正在使用Visual Basic 2010。
简单地说,我有一个“设置面板”表单需要一段时间来创建(它包含很多标签和文本框),所以我在另一个线程中创建它。
加载后,可以通过单击按钮将其查看更改为True来查看。我使用以下子例程来处理我的控件的调用:
Public Sub InvokeControl(Of T As Control)(ByVal Control As T, ByVal Action As Action(Of T))
If Control.InvokeRequired Then
Control.Invoke(New Action(Of T, Action(Of T))(AddressOf InvokeControl), New Object() {Control, Action})
Else
Action(Control)
End If
End Sub
这是我的主要代码的相关部分(SettingsTable继承TableLayoutPanel和HelperForm继承Form):
Public Class ch4cp
Public RecipeTable As SettingsTable
Public WithEvents SettingsWindow As HelperForm
Private Sub ch4cp_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
PanelCreatorThread = New Threading.Thread(AddressOf CreateStartupPanels)
PanelCreatorThread.Start()
End Sub
Private Sub CreateStartupPanels()
SettingsWindow = New HelperForm("Settings Panel")
SettingsTable = New SettingsTable
SettingsTable.Create()
SettingsWindow.Controls.Add(SettingsTable)
End Sub
Private Sub ViewSettingsPanel_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ViewSettingsPanel.CheckedChanged
InvokeControl(SettingsWindow, Sub(x) x.Visible = ViewSettingsPanel.Checked)
End Sub
SettingsTable.Create()方法根据应用程序设置的内容生成一堆标签和文本框,并将它们添加到SettingsTable中。
当我单击ViewSettingsPanel复选框时,出现跨线程违规错误。有任何想法吗?我真的很感激。
答案 0 :(得分:1)
我明白了。如果其他人可能遇到类似的问题,这就是秘密:
在SettingsTable类中,我有一个MakeTable方法,如下所示:
Private Sub MakeTable()
Me.Visible = False
Me.Controls.Clear()
... add some controls ...
Me.Visible = True
End Sub
我这样做是为了在可见时重新制作表格时控件不会闪烁。我不完全理解为什么(从阅读中,我猜测它是像子控件的句柄没有被创建,因为它们在创建后没有显示,所以当它应该是True时,IsInvokeRequired被评估为False )。修复是这样做的:
Private Sub MakeTable()
If Not IsNothing(Me.Parent) Then If Me.Parent.Visible Then Me.Visible = False
Me.Controls.Clear()
... add some controls ...
Me.Visible = True
End Sub
这样,子控件在不可见的SettingsWindow表单上“显示”,因此创建了它们的句柄。现在工作得很好!
答案 1 :(得分:0)
在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"))