Search code examples
.netvb.netmultithreadingdelegatesthread-synchronization

VB.net updating label from other thread


I have probably read and tried a hundred different ways to do this, and I can't take it any longer or wrap my head around it. I am new to multi-threading and usually make the UI process the code. I found the error of my ways as my program grew. Now I have classes and a thread that does not lag-out/freeze the main UI.

From within that class I am trying to update a label on the main form. (with what progress has been made)

The relevant code is as follows:

There is a label on the main form called UpdateLabel

A button on the main form:

Private Sub btnStartMenu_Click(sender As Object, e As EventArgs) Handles 
btnStartMenu.Click
    Call New Action(AddressOf setupthread1).BeginInvoke(Nothing, Nothing)
End Sub 'creates a new thread which runs great, not freezing the UI!


Private Sub setupthread1()    'this code is still on the main form
                              'all of the label updates work fine from here.
 StartMenu_Folders.startMenuFolders()  'This is the class that gets called 
                                       'from the main form

Public Class StartMenu_Folders
    Public Shared Sub startMenuFolders()
              frmMenu.UpdateLabel(frmMenu.lblLogoff, "...Updating Label text")    'this code is probably incorrect? Though the updatelabel DOES get 
       'successfully called



'This next part is back on the main form.
Public Sub UpdateLabel(ByVal lblLogoff As Label, ByVal Value As String)
    If lblLogoff.InvokeRequired Then
        Dim dlg As New UpdateLabelDel(AddressOf UpdateLabel)
        dlg.Invoke(lblLogoff, Value)
    Else
        lblLogoff.Text = Value
    End If
End Sub

ANY help would be appreciated, it is probably a 2 second explanation from one of you senior coders out there.

At this point it looks like the threads are running perfectly with no freezing, but the label just is not updating. . Thank you. I really appreciate it!

========================== Update EDIT to Code from information provided.

From viewers like YOU! Cue Reading rainbow music

'This is all on Main form 'frmMenu'
'Improved way of starting the class in a new thread
Private Sub btnStartMenu_Click(sender As Object, e As EventArgs) Handles btnStartMenu.Click
    Dim t As Task = Task.Run(Sub()
StartMenu_Folders.startMenuFolders()
  End Sub)
End Sub

Public Delegate Sub UpdateLabelInvoker(ByVal text As String)

Public Sub UpdateLabel(ByVal text As String)
    If Me.lblLogoff.InvokeRequired Then
        Me.lblLogoff.Invoke(New UpdateLabelInvoker(AddressOf UpdateLabel), _
                           text)
        MsgBox("invoked")
    Else
        Me.lblLogoff.Text = text
        MsgBox("DIDNT invoke")
    End If
End Sub

    'This is all in the class

Public Class StartMenu_Folders
Public Shared Sub startMenuFolders()
frmMenu.UpdateLabel("testtestest")
End Sub
End Class
'This code is still creating a separate instance of frmMenu I believe.
'I'm not sure how to target the right thread to update the label. 
'I've tried me.UpdateLabel("testtesttest") to no avail.

If anyone knows how to do that, that would be great! Everything else is working swimmingly. It's a shame that updating a label on a form from another thread is the hardest hoop I've had to jump through for this program in the months I've been working on it.


Solution

  • You don't call Invoke on the delegate and pass the Label. You call Invoke on the Label and pass the delegate:

    lblLogoff.Invoke(dlg, Value)
    

    See my explanation and examples here.

    EDIT:

    OK, I think I've got a handle on it this time. This:

    Public Class StartMenu_Folders
        Public Shared Sub startMenuFolders()
                  frmMenu.UpdateLabel(frmMenu.lblLogoff, "...Updating Label text")    'this code is probably incorrect? Though the updatelabel DOES get 
           'successfully called
    

    becomes this:

    Public Class StartMenu_Folders
    
        Public Shared Sub startMenuFolders(menuForm As frmMenu)
            menuForm.UpdateLabel("...Updating Label text")
    

    This:

    StartMenu_Folders.startMenuFolders()
    

    becomes this:

    StartMenu_Folders.startMenuFolders(Me)
    

    This:

    Public Sub UpdateLabel(ByVal lblLogoff As Label, ByVal Value As String)
        If lblLogoff.InvokeRequired Then
            Dim dlg As New UpdateLabelDel(AddressOf UpdateLabel)
            dlg.Invoke(lblLogoff, Value)
        Else
            lblLogoff.Text = Value
        End If
    End Sub
    

    becomes this:

    Public Sub UpdateLabel(ByVal Value As String)
        If lblLogoff.InvokeRequired Then
            Dim dlg As New UpdateLabelDel(AddressOf UpdateLabel)
            lblLogoff.Invoke(dlg, Value)
        Else
            lblLogoff.Text = Value
        End If
    End Sub
    

    In future, please don't post code from two different classes in the same block as it has caused confusion here. If you have two classes then post code from them in two different blocks.