0

I read various posts, and made a practice project, but it does not works. The form have a button and a text box with a default text 'Updated 0 times'. On button click starts the timer and each time update the text with the number of times the text was updated.

The exception of cross thread calls is not thrown, but when calling the text box, its .Text = "", the text is updated but not the text box on the form. And InvokeRequired is always false.

Public Class Form1

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    'Here the textBox.Text = "Updated 0 times."
    Dim checking_text As String = Me.TextBox1.Text
    TimerTest.StartTimer()
End Sub


Delegate Sub UpdateTextInvoke(ByVal new_text As String)
Public Sub UpdateText(ByVal new_text As String)
    'Here the textBox.Text = ""
    Dim txtB As TextBox = Me.TextBox1
    'InvokeRequired always = False.
    If txtB.InvokeRequired Then
        Dim invk As New UpdateTextInvoke(AddressOf UpdateText)
        txtB.Invoke(invk, New Object() {new_text})
    Else
        'The value of this text box is updated, but the text on the form TextBox1 never changes
        txtB.Text = new_text
    End If
End Sub
End Class


Public Class TimerTest
Private Shared tmr As New System.Timers.Timer
Private Shared counter As Integer

Public Shared Sub StartTimer()
    tmr.Interval = 5000
    AddHandler tmr.Elapsed, AddressOf UdpateText
    tmr.Enabled = True
End Sub

Public Shared Sub UdpateText(ByVal sender As Object, ByVal e As System.EventArgs)
    counter += 1
    Form1.UpdateText(String.Format("Updated {0} time(s).", counter))
End Sub
End Class

SOLVED In the Class TimerTest added this code 'Private Shared myform As Form1 = Form1' then changed 'Form1.UpdateText' To 'myform.UpdateText'

  • 2
    Your usage of the default form instance causes issues when it comes to multithreading. See my answer here: Possible duplicate of [Update textbox to a different form from backgroundworker](https://stackoverflow.com/questions/51068754/update-textbox-to-a-different-form-from-backgroundworker) || **TL;DR:** You need to pass the current instance of `Form1` to your `TimerTest` class. – Visual Vincent Jul 10 '18 at 20:27
  • 3
    Form1.UpdateText() does not do what you think it does in a worker thread. It actually creates a new instance of the Form1 class. You can't see it, its Show() method was never called. The much maligned default instance feature of vb.net is quite poisonous when used with threads. This class needs a constructor so you can pass and store the correct object reference. Or consider to just not use System.Timers.Timer. The required Invoke call makes it no better than the Timer component you have in the toolbox. – Hans Passant Jul 10 '18 at 20:30
  • Your SOLVED edit shouldn't actually solve the problem. – djv Jul 12 '18 at 19:02
  • @djv why?? it worked!! – user2150033 Jul 16 '18 at 20:03
  • `Private Shared myform As Form1 = Form1` >> `myForm` points to the default form instance of `Form`. Then `myform.UpdateText` would equate to your original code, `Form1.UpdateText`. Still using default form instance. Unless you somehow also got the default instance to show (i.e. beforehand calling `Form`.Show()` or something along those lines). Then you might at quick glance get your desired result. But using the default instance is still "quite poisonous". – djv Jul 16 '18 at 20:07

1 Answers1

1

As indicated in the comments, you are using the default form instance feature of VB.Net. You could pass an instance of the form to the TimerTest class, and replace the reference to Form1 with the instance.

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim checking_text As String = Me.TextBox1.Text
        TimerTest.StartTimer(Me)
    End Sub
    Public Sub UpdateText(new_text As String)
        If TextBox1.InvokeRequired Then
            Dim invk As New Action(Of String)(AddressOf UpdateText)
        TextBox1.Invoke(invk, {new_text})
    Else
            TextBox1.Text = new_text
        End If
    End Sub
End Class

Public Class TimerTest
    Private Shared tmr As New System.Timers.Timer()
    Private Shared counter As Integer
    Private Shared instance As Form1

    Public Shared Sub StartTimer(formInstance As Form1)
        instance = formInstance
        tmr.Interval = 5000
        AddHandler tmr.Elapsed, AddressOf UdpateText
        tmr.Enabled = True
    End Sub

    Public Shared Sub UdpateText(ByVal sender As Object, ByVal e As System.EventArgs)
        counter += 1
        instance.UpdateText(String.Format("Updated {0} time(s).", counter))
    End Sub
End Class
djv
  • 15,168
  • 7
  • 48
  • 72