Proger_Cbsk Proger_Cbsk - 2 months ago 11
Vb.net Question

UI update and delegate in another class doesnt work in separate thread

In order to keep responsiveness in the UI, I use a separate thread to execute various process, for example some FTP download.

Private Sub Button11_Click(sender As Object, e As EventArgs) Handles Button11.Click
Dim ThreadResync As System.Threading.Thread
ThreadResync = New System.Threading.Thread(AddressOf Bodacc_ResyncFTP)
ThreadResync.Start()
End Sub

Sub Bodacc_ResyncFTP()
Dim MyBodacc As bodacc_data = New bodacc_data
MyBodacc.Label_Status = Form1.Label1
MyBodacc.ResyncFTP()
End Sub


A way to update the UI with threading is the
Delegate
thingy, so in the
bodacc_data
I had to

Public Class bodacc_data
......
Delegate Sub UpdateLabelDelg(text As String, ThisLabel As Label)
Public Delegate_label As UpdateLabelDelg = New UpdateLabelDelg(AddressOf set_label)
Public Label_Status = Label
......
Sub set_label(stext As String, ThisLabel As Label)
ThisLabel.Text = stext
End Sub
.....

Sub ResyncFTP()
//says hello
If Label_Status.InvokeRequired = True Then
Label_Status.Invoke(Delegate_label, New Object() {"Working...", Label_Status})
Else
Label_Status.Text = "Working..."
End If

//do stuff
End Sub
End Class


It works like a charm. But I have many class doing more or less the same (disk update, database update, FTP update) and having to copy/past all the delegate / external label declaration / mini sub / invoke sound silly.

So I created a class to handle those UI update / delegate in order to have a quick access

Public Class Form_UI

Delegate Sub UpdateLabelDelg(text As String, ThisLabel As Label)
Public Delegate_label As UpdateLabelDelg = New UpdateLabelDelg(AddressOf set_label)

Private Labels(2) As Label

Sub New()

Labels(0) = Form1.Label1
Labels(1) = Form1.Label2
Labels(2) = Form1.Label3

End Sub

Sub set_label(stext As String, ThisLabel As Label)
ThisLabel.Text = stext
End Sub


Public Sub ChangeLabel(ByVal LabelNum As Integer, nText As String)
LabelNum = LabelNum - 1

If Labels(LabelNum).InvokeRequired Then
Labels(LabelNum).Invoke(Delegate_label, New Object() {nText, Labels(LabelNum)})
Else
Labels(LabelNum).Text = nText
Labels(LabelNum).Update()
End If

End Sub
End Class


So, now in the revamped
bodacc_data
and all others processing class I have only :

Public Class bodacc_data
......
Private MyUI as Form_UI
.....
Sub New()
MyUI = New Form_UI()
End Sub

Sub ResyncFTP()
//says hello
MyUI.ChangeLabel(1, "Working...")

//do stuff
End Sub
End Class


Question Why is
MyUI.ChangeLabel
not updating when the
ResyncFTP
is called in a thread, but works if called in the main thread (As in the code sample below)

Private Sub Button11_Click(sender As Object, e As EventArgs) Handles Button11.Click
Dim MyBodacc As bodacc_data = New bodacc_data
MyBodacc.ResyncFTP()
End Sub


Note that there is no error thrown. The notable weirdness is that
<Form_UI>.ChangeLabel()
never goes the
.Invoke
route but the normal update route. I strongly suspect a scope issue or insight issue.

Answer

When you create a windows forms app you set up a UI thread that is meant to be the owner of all the UI. The UI thread contains the message pump that is used to update all of the UI.

But what you're doing in Button11_Click is creating a new thread that goes and calls Dim MyBodacc As bodacc_data = New bodacc_data which, in turn, calls MyUI = New Form_UI().

You are creating a form on a non-UI thread. There is no message pump and therefore the UI doesn't update.