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
_Data = Data
Treeview1.Invoke(CType(AddressOf addnewnode, MethodInvoker))
End Sub
Private Sub AddNewNode()
Treeview1.Nodes.Add _Data
End Sub
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)
_NS.BeginRead(_Buffer, 0, READ_BUFFER_SIZE, _ReadCallBack, Nothing)
End Sub
Private Sub DoRead(ByVal Result As IAsyncResult)
Try
Dim BytesRead As Integer = _NS.EndRead(Result)
If BytesRead < 1 Then
Disconnect()
Else
Dim Data(BytesRead - 1) As Byte
_Buffer.Copy(_Buffer, 0, Data, 0, BytesRead)
RaiseEvent DataArrived(Data)
StartAsyncRead()
End If
Catch e As Exception
Disconnect()
End Try
End Sub
End Class
The function StartAsyncRead is the one that starts a new thread for receiving data.
Woka
Re: async threads and adding data to a treeview without using invoke method
If you have a reference to the form or controls then you could marshal the call back to the UI thread before actually raising the event.
Re: async threads and adding data to a treeview without using invoke method
how? any links or things to look into?
WOka
1 Attachment(s)
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.
Re: async threads and adding data to a treeview without using invoke method
You are a star. Thank you :D
Woka
Re: async threads and adding data to a treeview without using invoke method
OK...I see how this code works. Similar to what I have now where it uses Control.Invoke.
VB Code:
Private _ThreadRoot As Control
Now, what I am looking for is to change this to:
VB Code:
Private _ThreadRoot As MyOwnClassObject
Where MyOwnClassObject is just a class that raises events to the UI.
Is this possible?
Woka
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.
Woka
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.
Re: async threads and adding data to a treeview without using invoke method
Yup. I agree with you there...however...could my class1 not implement ISynchronizeInvoke?
VB Code:
Public Class Class1
Implements System.ComponentModel.ISynchronizeInvoke
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 :confused:
WOka
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.
http://www.aisto.com/roeder/dotnet/
Re: async threads and adding data to a treeview without using invoke method
You know I never like to take the easy route ;)
Cheers for your help.
Woof
Re: async threads and adding data to a treeview without using invoke method
Yeah I had a feeling you'd saw that.
1 Attachment(s)
Re: async threads and adding data to a treeview without using invoke method
Hahaha...Hmmmm...OK.
I have found the following, but am not sure how this helps me :(
Can't seem to find any decent help stuff on it.
WOka
1 Attachment(s)
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.