VB.net, Invoke, delegates, and threading. Can't figure out how to use them across classes
Solution 1
I'm not certain I understand what you are trying to do, but building upon your code, you can set the label safely ("thread-safely") by using the following code:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim t1 As New Threading.Thread(AddressOf Count)
t1.IsBackground = True
t1.Start(100)
End Sub
Private Sub Count(ByVal Max As Object)
If TypeOf Max Is Integer Then
Dim class2 As New Class2
class2.Count(CInt(Max), AddressOf SetLabelText)
End If
End Sub
Private Sub SetLabelText(ByVal text As String)
If Label1.InvokeRequired Then
Label1.Invoke(New SetText(AddressOf SetLabelText), text)
Else
Label1.Text = text
End If
End Sub
End Class
Public Class Class2
Sub Count(ByVal Max As Integer, SetTextMethod As SetText)
For i = 1 To Max
SetTextMethod.Invoke((CStr(i)))
Threading.Thread.Sleep(200)
Next
End Sub
End Class
Public Delegate Sub SetText(text As String)
I created a Delegate called "SetText"; when the form calls the count function in your class, you can pass an instance of the delegate that references the SetLabelText method. Within that method you can then safely set the label text either directly or indirectly via Invoke along with a new instance of the delegate.
Something you definitely don't want to do is reference your form from your class(i.e. "form1.SetLabelText(CStr(i))"); that can create a real nightmare as the project grows in size and requirements change!
If I've misunderstood your question or not answered it properly, please do post back.
Solution 2
First off I would suggest using the Task Parrallel Library instead of threads. It's easier to understand and work with. For example,
Dim countTask as New Task(Sub() Count(10))
Dim displayTask = countTask.ContinueWith(Sub()
Me.Invoke(Sub() Label.Text = "10"
End Sub)
countTask.Start()
This example assumes you are calling this from the form itself. You can use this method to return values from the first task to the second. If you need a more detail example and want more examples check out http://msdn.microsoft.com/en-us/library/hh228603.aspx. If you need further help I can always throw something up on GitHub or blog about it. Good Luck.
Finch042
Updated on January 19, 2020Comments
-
Finch042 over 4 years
Long story short, I'm having a hell of a time trying to figure out how to use invoke and/or delegates to update the userform from a separate class when using threading. I'm quite sure it's something silly and obvious to someone with more experience. I know a delegate is probably required, but all my efforts seem to only work when it's being called from main thread. I've been looking around the internet for half the day, and there's just something I'm not getting.
Here's some pseudo-code as an example:
This option works:
Public Class Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim t1 As New Threading.Thread(AddressOf Count) t1.IsBackground = True t1.Start(100) End Sub Private Sub Count(ByVal Max As Object) If TypeOf Max Is Integer Then Count(CInt(Max)) End If End Sub Private Sub SetLabelText(ByVal text As String) If Label1.InvokeRequired Then Label1.Invoke(New Action(Of String)(AddressOf SetLabelText), text) Else Label1.Text = text End If End Sub Private Sub Count(ByVal Max As Integer) For i = 1 To Max SetLabelText(CStr(i)) Threading.Thread.Sleep(200) Next End Sub End Class
While this (one of my 1000 efforts of slightly different variation) does not. Practically speaking, I just tried to separate one of the subs into its own class for this example, but it's otherwise the same as I could make it:
Public Class Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim t1 As New Threading.Thread(AddressOf Count) t1.Start(100) End Sub Private Sub Count(ByVal Max As Object) If TypeOf Max Is Integer Then Dim class2 As New class2 class2.Count(CInt(Max)) End If End Sub Private Delegate Sub SetTextBoxTextInvoker(text As String) Sub SetLabelText(ByVal text As String) 'or me.label1, form1.label1 or anything else I can try! If Me.InvokeRequired Then Me.Invoke(New SetTextBoxTextInvoker(AddressOf SetLabelText), _ text) Else Me.Label1.Text = text End If End Sub End Class Public Class class2 Sub Count(ByVal Max As Integer) For i = 1 To Max form1.SetLabelText(CStr(i)) Threading.Thread.Sleep(200) Next End Sub End Class
From what I can tell, it appears that the if statement for invokerequired in the Sub "SetLabelText" never gets triggered. My best guess is that I'm not referring to the userform correctly when checking for the invokerequired parameter? Or I need to feed something else to the delegate? I'm just getting frustrated with messing around with the million little variables I might be getting wrong. Thanks in advance for any help you can provide and let me know if you need more info.
-
Finch042 over 10 yearsMostly just wanted to give the simplest example I could find (using some code I came across earlier, while searching for an answer earlier). This is exactly what I needed and hopefully I should be able to adapt it accordingly. Thanks very much!
-
Finch042 over 10 yearsOdd the first I've heard of this, and it may end up helping me out QUITE a bit with the actual project my question regards. I'm using threadpool along with synclock to gather information about files and write it to a text file. Long story short, the threads don't necessary return in the order I need to write the information to the text file, and I wasn't really sure of a more elegant way to deal with it than Monitor.Wait/Minitor.Pulse etc. It might take me a while, but the link you provided seems like it might be faster/better. If you do have the time, I'd genuinely appreciate some guidance.
-
Finch042 over 10 yearsEither way, even if you don't end up getting around to anything else, thanks very much for the additional information/alternative.
-
jcwrequests over 10 yearsYou might also like to check out Reactive Extensions jensbits.com/2012/01/23/…. I have been using TPL and RX quite extensively in projects and it alows me to write more declarative code. I will try and put a blog entry together or example on github. On github I am at github.com/jcwrequests or my blog is at wysnet.blogspot.com/. If you have a github account and post some questions and examples there I can always do a pull request with my responses. Any way good luck.