Click here to Skip to main content
14,921,361 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hello,

I made a small sample project in order to learn the use of a delegate to update the UI (older way / pre-Net4.0). I have no idea what might be wrong, since the UI is sometimes updating and sometimes not - but never in the expected way of counting upwards (visible), only the result is (mostly) shown.

As soon as I uncomment the "Thread.Sleep" line in ThreadClass, the UI freezes immediately after clicking the button.

Since I get such unpredictable behaviour, I'm already afraid I might have a completely unrelated problem... maybe with the VS setup. Would you be so kind as to have a look at the code and reassure me if it's correct or not? I don't comprehend why "Thread.Sleep(50)" would lead to a frozen UI.

Thank you very much,
Michael

EDIT: UI is frozen, but when I set a breakpoint I can see the counter increases. At the end of the loop, the UI is free again. So I must have twisted something...

What I have tried:

The code is:
VB
   Private Sub Button1_Click(sender As System.Object, e As System.EventArgs)
       Dim foo As New ThreadClass
       foo.LongLastingProcess(10000, AddressOf SetUIText) 'long running
End Sub

   Friend Sub SetUIText(ByVal statusText As String, ByVal labelText As String)
       If Me.InvokeRequired Then
           Dim d As New UITestDelegate(AddressOf SetUIText)
           Me.Invoke(d, New Object() {statusText, labelText})
       Else
           Me.ToolStripStatusLabel1.Text = statusText
           Me.Label1.Text = labelText
       End If
   End Sub
in the form code, and another class
VB
Friend Delegate Sub UITestDelegate(ByVal d As String, ByVal labelText As String)

Friend Class ThreadClass
	Public Sub New()
	End Sub

	Friend Sub LongLastingProcess(ByVal number As Integer, ByVal UpdateMethod As UITestDelegate)
		For i As Integer = 0 To number
				Dim msg As String = String.Format("Loop {0}", i)
				'Threading.Thread.Sleep(50)
				UpdateMethod.Invoke(msg, msg)
		Next
	End Sub
End Class
Posted
Updated 31-Jan-19 12:34pm
v2

You are using the UI thread even though it seems as if you are new'ing up a new thread. So when you sleep the thread you are actually sleeping the main UI thread.

Invoke used like this is rarely the way to go.
It is quite a bit easier to do this via BackgroundThreadWorker which you can assign work to be done (function) and then tell it to Start().

Here's a complete sample I wrote up in the distant past:
C# Backgroundworker download progress in label, get bytes in label - Stack Overflow[^]
   
Comments
Sonhospa 31-Jan-19 14:20pm
   
Thank you, Raddevus. I want to understand the delegate/invoke method, that's why I didn't just use the BW. However, I wasn't aware that I'm sending my UI thread to sleep! But there must be more wrong, as the UI isn't updating at all during the loop.
raddevus 31-Jan-19 14:56pm
   
If you successfully create a separate thread it will not be allowed to write to the UI since the UI-thread (main thread) is managing that work too. In most instances if you get it to do so your app will crash. However, I believe you are saying you are attempting to do a delegate for that exact reason.
Unfortunately, you're probably not going to find a lot about it in VB.NET resources, but here is a fantastic write-up that should help you understand these concepts better: http://www.albahari.com/threading/ -- This is the original author of C# In A Nutshell O'Reilly book. It'll help you get through the challenges if you ignore that it is C# centric.
Sonhospa 31-Jan-19 16:02pm
   
Cool, thank you again - I'll give it a read!
And: I've got it to work :-)
Dave Kreskowiak 31-Jan-19 16:44pm
   
In the code you posted, you never create another thread to run the LongLastingProcess code on. It's runnning on the UI thread.
Setting up a function on your form that does the work of switching to the UI thread and updating the UI for you is not a bad idea, but setting up multiple functions to update individual controls isn't necessary. You can pass your function an action delegate instead, and then use the same function to update your UI elements every time. This is what I usually do:

VB
Private Sub UI(code As Action)
    ' thisClosing is a Boolean you set to true in your formclosing event.
    If Not Me.IsDisposed AndAlso Not thisClosing Then
        Try
            If Me.InvokeRequired Then
                Me.Invoke(DirectCast(Sub() code(), MethodInvoker))
                Return
            End If
        Catch ex As Exception
        End Try
        code()
    End If
End Sub


You use the function like this:

VB
' Somewhere in your code, you create a thread:
Dim thread As New Thread(AddressOf threadSub)
thread.Start()

' This would be the Sub where your thread is running:
private Sub threadSub()
    ' This stuff is all on the background thread.
    ' When you are ready to update a UI control, just call
    ' the UI() Sub using an anonymous delegate (you should
    ' read up on those if you don't recognize the term), 
    ' like this:
    UI(Sub
           ' Any code you write here is on the UI thread, and
           ' you can update any control you want. All the objects
           ' in your threadSub function will be available to you.
       End Sub)
End Sub


Hope that helps,

- Pete
   
v2
Comments
Sonhospa 1-Feb-19 4:25am
   
That's much more elegant, Peter - thank you very much!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900