vb.NET 不允许跨线程执行操作

vb.NET You are not allowed to perform an operation across different threads

我是 运行 从 pdf 文件中读取文本的代码,而不是创建一个 WordCloud。为了通知用户该过程正在进行中,我在我的表单中添加了一个 BackgroundWorker,显示 image 表示正在加载。我在跨不同线程操作时遇到错误。

Private Sub ButtonCreate_Click(sender As Object, e As EventArgs) Handles ButtonCreate.Click

        bTextEmpty = False
        ListView1.Items.Clear()
        ResultPictureBox.Image = Nothing

        If ListBox1.SelectedIndex > -1 Then
            For Each Item As Object In ListBox1.SelectedItems
                Dim ItemSelected = CType(Item("Path"), String)
                Dim myTempFile As Boolean = File.Exists(ItemSelected + "\Words.txt")
                If myTempFile = False Then
                    'when we load the form we first call the code to count words in all files in a directory
                    'lets check if the folder exists
                    If (Not System.IO.Directory.Exists(ItemSelected)) Then
                        Dim unused = MsgBox("The archive " + ItemSelected.Substring(ItemSelected.Length - 5, Length) + " was not found",, Title)
                        Exit Sub
                    Else
                        Call CreateWordList(ItemSelected)
                    End If
                End If
                'if the words file is empty we cant create a cloud so exit the sub
                If bTextEmpty = True Then Exit Sub
                'then we fill the wordcloud and Listview from the created textfile
                Call CreateMyCloud(ItemSelected + "\Words.txt")
            Next
        Else
            Dim unused = MsgBox("You have to choose an Archive to create the Word Cloud",, Title)
        End If
        Size = New Drawing.Size(1400, 800)

    End Sub

我把上面的代码放在一个 Private Sub 中并从我的 BackgroundWorker 调用它。错误发生在这一行:If ListBox1.SelectedIndex > -1 Then

在尝试了建议的代码后,我再次遇到同样的错误:

Public Sub CreateMyCloud(ByVal sourcePDF As String)

        Dim WordsFreqList As New List(Of WordsFrequencies)
        For Each line As String In File.ReadLines(sourcePDF)
            Dim splitText As String() = line.Split(","c)
            If splitText IsNot Nothing AndAlso splitText.Length = 2 Then
                Dim wordFrq As New WordsFrequencies
                Dim freq As Integer
                wordFrq.Word = splitText(0)
                wordFrq.Frequency = If(Integer.TryParse(splitText(1), freq), freq, 0)
                WordsFreqList.Add(wordFrq)
            End If
        Next
        If WordsFreqList.Count > 0 Then
            ' Order the list based on the Frequency
            WordsFreqList = WordsFreqList.OrderByDescending(Function(w) w.Frequency).ToList
            ' Add the sorted items to the listview
            WordsFreqList.ForEach(Sub(wf)
                            error ->  ListView1.Items.Add(New ListViewItem(New String() {wf.Word, wf.Frequency.ToString}, 0))
                                  End Sub)
        End If

        Dim wc As WordCloudGen = New WordCloudGen(600, 400)
        Dim i As Image = wc.Draw(WordsFreqList.Select(Function(wf) wf.Word).ToList, WordsFreqList.Select(Function(wf) wf.Frequency).ToList)
        ResultPictureBox.Image = i


    End Sub

您不能简单地访问当前线程之外的控件。您首先需要在目标控件上调用 Invoke 方法,然后传递一个包含用于在当前线程之外修改控件的指令的委托。有关如何执行此操作的信息,请参阅 MSDN 上的这篇文章:https://docs.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls?view=netframeworkdesktop-4.8

应该看起来更像:

Private Sub ButtonCreate_Click(sender As Object, e As EventArgs) Handles ButtonCreate.Click
    If ListBox1.SelectedItems.Count > 0 Then
        Dim data As New List(Of Object)
        For Each Item As Object In ListBox1.SelectedItems
            data.Add(Item)
        Next

        bTextEmpty = False
        ListView1.Items.Clear()
        ResultPictureBox.Image = Nothing

        BackgroundWorker1.RunWorkerAsync(data)
    Else
        MessageBox.Show("You have to choose an Archive to create the Word Cloud",, Title)
    End If
End Sub

Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    Dim data As List(Of Object) = DirectCast(e.Argument, List(Of Object))
    For Each Item As Object In data
        Dim ItemSelected = CType(Item("Path"), String)
        Dim myTempFile As Boolean = File.Exists(ItemSelected + "\Words.txt")
        If myTempFile = False Then
            'when we load the form we first call the code to count words in all files in a directory
            'lets check if the folder exists
            If (Not System.IO.Directory.Exists(ItemSelected)) Then
                MessageBox.Show("The archive " + ItemSelected.Substring(ItemSelected.Length - 5, Length) + " was not found",, Title)
                Exit Sub
            Else
                Call CreateWordList(ItemSelected)
            End If
        End If
        'if the words file is empty we cant create a cloud so exit the sub
        If bTextEmpty = True Then Exit Sub
        'then we fill the wordcloud and Listview from the created textfile
        Call CreateMyCloud(ItemSelected + "\Words.txt")
    Next
End Sub

Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    Size = New Drawing.Size(1400, 800)
End Sub

为了修复你的第二个错误,编辑了 post:

WordsFreqList.ForEach(Sub(wf)
                          ListView1.Invoke(Sub()
                                               ListView1.Items.Add(New ListViewItem(New String() {wf.Word, wf.Frequency.ToString}, 0))
                                           End Sub)
                      End Sub)