async threads and adding data to a treeview without using invoke method
I have a class, which raises an evert to the UI.
VB Code:
Private Withevents _Conn As MyConnection
Private Sub _Conn_Data(ByVal Data As String) Handles _Conn.Data
Treeview1.Nodes.Add Data
End Sub
the thing is that _conn calls another thread, which wiats for data. When data arrives the above event is raised, but .NET doesn't like adding the data to a treeview as the event has come from another thread.
To get this to work I must do:
VB Code:
Private _Data As String
Private Sub _Conn_Data(ByVal Data As String) Handles _Conn.Data
this is not good.
What I would like is the event to be raised in the correct thread.
Someone mentioned that queues could be used.
The that I use is this:
VB Code:
Imports System.Net.Sockets
Imports System.IO
Imports System.Text
Public Class TCPConnection
Public Event DataArrived(ByVal Data() As Byte)
Public Event Disconnected()
Private Const READ_BUFFER_SIZE As Integer = 255
Private _ReadCallBack As AsyncCallback
Private _HostAddress As String
Private _Port As Integer
Private _TCPClient As TcpClient
Private _NS As NetworkStream
Private _SW As StreamWriter
' Private _SR As StreamReader
Private _Buffer(READ_BUFFER_SIZE - 1) As Byte
Public Sub New(ByVal HostAddress As String, ByVal Port As Integer)
_HostAddress = HostAddress
_Port = Port
Connect()
End Sub
Public ReadOnly Property HostAddress() As String
Get
Return _HostAddress
End Get
End Property
Public ReadOnly Property Port() As Integer
Get
Return _Port
End Get
End Property
Public Sub Connect()
_TCPClient = New TcpClient(_HostAddress, _Port)
_NS = _TCPClient.GetStream()
'_SR = New StreamReader(_NS, Encoding.ASCII)
_SW = New StreamWriter(_NS, Encoding.ASCII)
_SW.AutoFlush = True
StartAsyncRead()
End Sub
Public Sub Disconnect()
_TCPClient.Close()
_TCPClient = Nothing
RaiseEvent Disconnected()
End Sub
Public Sub SendData(ByRef Data As String)
_SW.Write(Data)
End Sub
Private Sub StartAsyncRead()
_ReadCallBack = New AsyncCallback(AddressOf DoRead)
Re: async threads and adding data to a treeview without using invoke method
Sorry about that I meant to include when with my original post but got distracted. Here you go. I tried to highlight the real 'meat' of it because I had to do some other crap to simulate your threading situation and that part isn't something you need.
To make it work in your situation you'd need to add some sort of ThreadRoot to the TCPConnection class and make it required. Then you would need to make a delegate for the DataArrived event so it can be called via Invoke. Then you'd need to make a hidden/protected method to simply raise the event. Last you would just change your RaiseEvent code to use something like this instead of just RaiseEvent DataArrived(Data):
VB Code:
'check if the threadroot is in a different thread
If _ThreadRoot.InvokeRequired Then
'the threadroot IS in a different thread so create a delegate
'and then invoke that delegate on the control's thread
Dim del As New DataArrivedHandler(AddressOf Me.FireDataArrivedEvent)
_ThreadRoot.Invoke(del, New Object() {Data})
Else
'the threadroot is NOT in a different thread so fire it locally
FireDataArrivedEvent(Data)
End If
Then nothing would be different in the consumer code and the event would be like any other non-threaded event.
Last edited by Edneeis; Sep 4th, 2005 at 05:55 PM.
Re: async threads and adding data to a treeview without using invoke method
What I have is 2 classes.
Class1 and Class2
Class1 is declared on a form using:
VB Code:
Private Withevents Woof As Class1
Class2 is declared in exactly the same way, but inside class1. So basically Class2 raises events into class1, then depending on these events class1 raises further events to the UI.
Now I ALWAYS want class1 in the same thread as the form.
It's class2 that executes code async.
So, I want to "marshal", invoke, or whatever it's called, so that class2 raises the event into class1 in the correct thread? make sense?
All the examples I have seen involve using a form, or in your example, a control to work out what thread the event needs to be invoked 2.
Am I making sense?
Sorry if my terminology is incorrect.
Re: async threads and adding data to a treeview without using invoke method
The code I gave would then need to be in your class2. The reason a form (which is a control) or control is used is because it has the Invoke and InvokeRequired methods which make all the magic happen. So if you don't pass a control reference to Class2 then you'll need to reproduce that functionality. The best bet on that is to use Reflector to see what the Framework is doing in these methods BUT I think it'll be more trouble then its worth and I'd recommend just passing a control.
So Class1 would need the ThreadRoot but it wouldn't use it except to pass to Class2 which would alos have it.
One shortcut that might help is to have Class2 inherit from Control to get the functionality.
Last edited by Edneeis; Sep 5th, 2005 at 01:39 PM.
Public Function BeginInvoke(ByVal method As System.Delegate, ByVal args() As Object) As System.IAsyncResult Implements System.ComponentModel.ISynchronizeInvoke.BeginInvoke
End Function
Public Function EndInvoke(ByVal result As System.IAsyncResult) As Object Implements System.ComponentModel.ISynchronizeInvoke.EndInvoke
End Function
Public Function Invoke(ByVal method As System.Delegate, ByVal args() As Object) As Object Implements System.ComponentModel.ISynchronizeInvoke.Invoke
End Function
Public ReadOnly Property InvokeRequired() As Boolean Implements System.ComponentModel.ISynchronizeInvoke.InvokeRequired
Get
End Get
End Property
End Class
Then I pass class1 to class2...
Now all I need to figure out is what code to add into those subs etc
Re: async threads and adding data to a treeview without using invoke method
Yes that is the tricky part. I took a look at what Control is doing and it is using Application.GetParkedWindow and the Thread.Context to find out what thread the hWnd handle for the control was created on. I still think it'll be more hassle then its worth but if you want to know how Control implements the ISynchronizeInvoke ust download Reflector and check it out.
Re: async threads and adding data to a treeview without using invoke method
That is the info on the Interface itself but you want the something that implements it. Try looking up Control and check out its Invoke and InvokeRequired methods then press space to get the code. Also if you see a method that is used inside one of those methods you can click it to go to its code as well. Warning though all of the methods that Control uses take a control as a parameter because the control is where it is getting the window handle from. Control has a whole infrastructure for handling handles and the like.
Last edited by Edneeis; Sep 5th, 2005 at 05:09 PM.