grantdaddy
asked on
VB.Net Delegate Question
I have a vb.net class (I'll call Class1) that includes two sub routines. The first sub is called Start, and it does the general work of the class. As it does its thing, if a variable called DoDebug is set to true, it periodically calls the other sub routine called WriteDebug. WriteDebug accepts a string as a parameter and appends the string to a file.
This class normally runs as part of a windows service. For testing purposes though, I created a windows forms project with one form (Form1) and added Class1 to it. I created a button on Form1 that calls the start routine of Class1. I then created a textbox on Form1. What I would like to do is have the text box display the strings that are passed to the WriteDebug method of class1. I don't want Class1 to reference anything on the form though because Form1 won't exist when Class1 runs as part of my windows service.
I assume I would do this with delegates? Can I create a delegate sub in Class1 and invoke it in the WriteDebug routine? I have put some code below that does not work. Somebody set me straight, please! How should this be done?
This class normally runs as part of a windows service. For testing purposes though, I created a windows forms project with one form (Form1) and added Class1 to it. I created a button on Form1 that calls the start routine of Class1. I then created a textbox on Form1. What I would like to do is have the text box display the strings that are passed to the WriteDebug method of class1. I don't want Class1 to reference anything on the form though because Form1 won't exist when Class1 runs as part of my windows service.
I assume I would do this with delegates? Can I create a delegate sub in Class1 and invoke it in the WriteDebug routine? I have put some code below that does not work. Somebody set me straight, please! How should this be done?
Public Class Class1
'The delegate stuff in class 1:
Public Delegate Sub GetMessage(ByVal strData As String)
Private _GetMessage As GetMessage
Public Sub GetMessages(ByVal del As GetMessage)
_GetMessage = del
End Sub
'The main routine in Class1
Public Sub Start()
'code here to do stuff
End Sub
'The routine to invoke the delegate and pass the string to Form1
Private Sub WriteDebug(strData as String)
'code here to write to file
_GetMessage.Invoke(strData)
End Sub
End Class
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim c as new Class1
Dim delegateGetMessages As New Class1.GetMessage(AddressOf ShowMessage)
c.GetMessages(delegateGetMessages)
c.Start
End Sub
Private Sub ShowMessage(ByVal strData As String)
TextBox1.Text += strData
End Sub
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Hello, grantdaddy,
Even if you can get the "delegate" assignment approach you are trying to work, you might want to consider switching to the Event/Handler approach that Idle_Mind is recommending. I think that the two are basically equivalent, but the Event/Handler approach Idle-Mind recommends is going to be a bit easier to understand for others who come after you to maintain the code.
Cheers,
Randy
Even if you can get the "delegate" assignment approach you are trying to work, you might want to consider switching to the Event/Handler approach that Idle_Mind is recommending. I think that the two are basically equivalent, but the Event/Handler approach Idle-Mind recommends is going to be a bit easier to understand for others who come after you to maintain the code.
Cheers,
Randy
ASKER
Thanks! That works great! I was making it too difficult.
Glad you found it useful! I would have given points to omegaomega too, though, for showing how to make it work with the original "specs"... =\
ASKER
I have one more problem with this solution. I created an event in Class1 that accepts a string parameter and had Form1 handle the event, just like Idle_Mind suggested above. However, I left out an important piece of information. The code in Class1 that calls the WriteDebug method actually exectues on a timer elapsed event, which means it is in a separate thread. So I am getting an error message that says "Cross-thread operation not valid" when Form1 tries to update the text box control. Do you know how to handle this?
Yes...you use a Delegate/Invoke() approah. (There are other solutions as well)
Here is a modified example demonstrating the concept:
Here is a modified example demonstrating the concept:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim c1 As New Class1
AddHandler c1.WriteDebugReceived, AddressOf c1_WriteDebugReceived
c1.Start()
End Sub
Public Delegate
Private Sub c1_WriteDebugReceived(ByVal strData As String)
MessageBox.Show(strData, "WriteDebugReceived()", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
End Class
The last code snippet didn't post completely... =\
Here she is:
Here she is:
Public Class Form1
Private Delegate Sub WriteDebugDelegate(ByVal strData As String)
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim c1 As New Class1
AddHandler c1.WriteDebugReceived, AddressOf c1_WriteDebugReceived
c1.Start()
End Sub
Private Sub c1_WriteDebugReceived(ByVal strData As String)
If Me.InvokeRequired Then
Me.Invoke(New WriteDebugDelegate(AddressOf c1_WriteDebugReceived), New Object() {strData})
Else
TextBox1.Text = strData
End If
End Sub
End Class
Public Class Class1
Public Event WriteDebugReceived(ByVal strData As String)
Public Sub Start()
Dim T As New System.Threading.Thread(AddressOf Worker)
T.Start()
End Sub
Private Sub Worker()
System.Threading.Thread.Sleep(5000) ' simulated 5 second long "work"
Dim someCondition As Boolean = True
If someCondition Then
WriteDebug("Hello World!")
End If
End Sub
Private Sub WriteDebug(ByVal strData As String)
'code here to write to file
RaiseEvent WriteDebugReceived(strData)
End Sub
End Class
ASKER
Thank you so much for following up with me. This works perfectly. And I think I finally understand how a delegate works.
As far as I can see, your delegate is OK.
Perhaps the problem that you are seeing is because the form cannot complete loading until c.Start completes. In the attached snippet, I have moved your call to c.Start to a button click event (and given "c" module scope so that it doesn't disappear after the form load event completes). I've added some trivial test code to the Start method at the point of "'code here to do stuff" and find that with those changes everything works fine for me.
If that's not the problem though, post back with more specifics and we'll see what we can figure out.
Cheers,
Randy
Open in new window