即使使用了Invoke,VB.NET交叉线程操作也无效!

时间:2010-09-28 01:30:51

标签: vb.net visual-studio-2010 forms multithreading

我一直在四处寻找,试图找出为什么我会得到这个例外无济于事。我希望有人之前见过这个:

我正在使用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复选框时,出现跨线程违规错误。有任何想法吗?我真的很感激。

2 个答案:

答案 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"))