-1

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
Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
fellyp.santos
  • 136
  • 2
  • 13
  • You could pass the form as a parameter to the `count` method. – Visual Vincent Feb 21 '17 at 18:17
  • I already tried this way. But access the elements from another thread directly is not recommended, and I know why! My project need to append text more then 200 times, and the application stop to respond and crashs when I pass the form as parameter, because the form runs on it own thread. – fellyp.santos Feb 21 '17 at 18:24
  • Accessing a custom method through the form should still be possible so long that method invokes the accessing of UI elements. – Visual Vincent Feb 21 '17 at 18:25
  • Thus, calling `yourForm.addText()` or `yourForm.threadSafe()` from a background thread should not be a problem. – Visual Vincent Feb 21 '17 at 18:27
  • It may be your thread is trying to update it faster than the thread switching can handle it.... Consider building the text into a buffer string and periodically updating the UI, or do it at the end. – Trevor_G Feb 21 '17 at 18:34
  • You could also pass the form update off to a backgroundworker or timer on the form/. – Trevor_G Feb 21 '17 at 18:37
  • Thanks all of you for help! The solution was more simple that I expected. I was just forgetting to call the same method to add the text to txtLogger – fellyp.santos Feb 21 '17 at 18:42

1 Answers1

2

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
Trevor_G
  • 1,331
  • 1
  • 8
  • 17
  • 1
    Great! It worked! I already tried to pass the Form as parameter, but was forgetting to call the same method to add the text, instead that I was accessing the element using Parent_From.txtLogger.AppendText("something").. Thank you so much! – fellyp.santos Feb 21 '17 at 18:39