.netvb.netcross-thread

How to set text to element created in another thread


Before starting, I know that already have a bunch of answers for that question, but let me explain what's happening.

I basically want append some texts to a RichTextBox element, it serves to me like a logger to inform to user each action from a file processing, but the text is appended to the RichTextBox through a for loop, and if I execute this loop in the same class "Form1.vb" the UI freezes until the loop finish.

I decided to run the loop in a separate thread to avoid UI freezing, and is here that the my problem starts.

Form1.vb

Imports System.Threading


Public Class Form1

    Dim myThread As Thread

    Private Sub appendMyText()
        ' Cross-thread operation not valid: Control txtLogger accessed from a thread other than the thread it was created on.
        txtLogger.AppendText("Hello World" & vbNewLine)
    End Sub

    Private Sub btnTest_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnTest.Click
        myThread = New Thread(New ThreadStart(AddressOf appendMyText))
        myThread.Start()
    End Sub

End Class

I can't access the txtLogger element from another thread, so I tried the MSDN example https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2

It shows me how to access the element making thread-safe calls, using delegate.

So my edited code is

Form1.vb

Imports System.Threading

Public Class Form1

    Dim myThread As Thread
    Delegate Sub AppendMyText(ByVal text As String)

    ' Add the text to RichTextBox
    Private Sub addText(ByVal txt As String)
        If txtLogger.InvokeRequired Then
            Dim myDelegate = New AppendMyText(AddressOf addText)
            Me.Invoke(myDelegate, {txt})
        Else
            txtLogger.AppendText(txt)
        End If
    End Sub

    ' Call the method that add text to RichTextBox
    Private Sub threadSafe()
        Me.addText("Hello World" & vbNewLine)
    End Sub

    Private Sub btnTest_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnTest.Click
        myThread = New Thread(New ThreadStart(AddressOf threadSafe))
        myThread.Start()
    End Sub

End Class

The code really works this way, the text is appended to RichTextBox, but the all code is in the same class Form1.vb

In my original project, the for loop is executed in another class, I'm gonna name it "Class1.vb" here.

That is the code example

Class1.vb

Public Class Class1

    Public Sub count()
        Dim i As Integer

        For i = 0 To 100
            ' this method will be executed by thread "myThread"
            ' how to append text to txtLogger from here?
            Debug.WriteLine("Index: {0}", i)
        Next
    End Sub

End Class

Solution

  • Pass the form reference to the class

    In your form

    Dim MyClass as Class1
    MyClass = New Class1(Me)
    

    In Your class

    Public Class Class1
    
         Private Parent_From as Form1
         Public Sub New(Parent as Form1)
               Parent_From = Form
         End sub
         Public Sub count()
            Dim i As Integer
            For i = 0 To 100
                ' this method will be executed by thread "myThread"
                Parent_Form.addTExt("Whatever")
                Debug.WriteLine("Index: {0}", i)
            Next
        End Sub
    End CLass