|
-
Jan 31st, 2015, 05:19 AM
#1
Thread Starter
Member
[RESOLVED] [VS 2010] [Multi-Threading] Invoke textbox to apply message, from another class.
[Visual Studio 2010]
Hello again!
I'm doing a TCP server project which works great. However, the chat output won't display in my textbox in the main form. I've analyzed the code a little, but I don't seem to find a workaround. Here's the following code I'm using.
[NOTE] The TCP server runs on a different thread than the UI main thread.
Code:
Public Class cmd
Public Class doCode
Public Shared Sub Chat(ByVal [Message] As String)
Functions.OutputString = [Message]
Functions.EditTextDocument()
End Sub
End Class
Public Class Functions
Public Shared OutputString As String
Private Delegate Sub DoStuffDelegate()
Public Shared Sub EditTextDocument()
If (Form1.InvokeRequired) Then 'Checks if the form needs to be invoked.
Form1.Invoke(New DoStuffDelegate(AddressOf EditTextDocument)) 'Invokes the form
Else
Form1.txt_Output.Text &= Environment.NewLine & OutputString 'Adds the output stream from the Chat
End If
End Sub
End Class
End Class
When a message from my TCP server is received, it will call the cmd.doCode.Chat(MessageReceived) sub. The function cmd.doCode.Chat will then try to invoke the textbox and add the message for server monitor display.
What I've found out so far:
*When I'm using this code in the main class, with all private, the textbox will get updated.
*When I use it from a different class than the main class, it won't.
The conclusion I'm drawing from this, is the way I call it Form1.Invoke might create a new thread, instead of using the main UI thread. I then tested with creating some variables to take the Form1 (Me) object on the journey from the beginning, to the end. Still no luck. In another project I fixed this issue by calling a variable at startup,
Code:
Dim myForm as Form = Form1
but it didn't work in this case. I'm not an expert at threading, and might overlooked some important notes with multi-threading. From what I believe so far, if I use multi-threading in another class, objects on invoke won't get updated.
My question is therefore, how can I update my objects on main UI thread, while doing multi-threading in a different class?
Thanks for hearing me out, and I hope you can lead me on the right track!
- Frek
Last edited by Frekvens1; Feb 2nd, 2015 at 05:48 AM.
Reason: More Info
-
Jan 31st, 2015, 07:04 AM
#2
Re: [VS 2010] [Multi-Threading] Invoke textbox to apply message, from another class.
The controls should be private. Nothing has any business knowing they exist other then the form they were created on. Invoke does not create any new thread. Your issue is you are using default instances. You should read JMC's blog on default instances.
For this example i WILL make the controls public. We can go more into detail later.
vb Code:
Imports System.Threading Public Class Form1 Private m_form2 As Form2 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click If Me.m_form2 Is Nothing Then Me.m_form2 = New Form2 End If Me.m_form2.Show() Dim t As New Thread(Sub() Foo()) t.IsBackground = True t.Start() End Sub Private Sub Foo() For n = 0 To 5 Me.m_form2.RichTextBox1.Invoke(Sub() Me.m_form2.RichTextBox1.AppendText("Hello UI thread!!!" & vbNewLine) End Sub) Threading.Thread.Sleep(1000) Next End Sub End Class
-
Jan 31st, 2015, 07:40 AM
#3
Re: [VS 2010] [Multi-Threading] Invoke textbox to apply message, from another class.
Private controls...
vb Code:
Imports System.Threading.Tasks Public Class Form1 ''' <summary> ''' Holds an instance of form2 initialized on new. ''' </summary> Private m_form2 As Form2 ''' <summary> ''' Creates a new instance of the <see cref="form1" /> Class. ''' </summary> Public Sub New() InitializeComponent() Me.m_form2 = New Form2(Me) Me.m_form2.Show() End Sub ''' <summary> ''' Represents the method that will handle the <see cref="form1.ClientOutput">PostData</see> event of <see cref="ClientOutput" />. ''' </summary> ''' <remarks > ''' The most recent Tcp client data retrieved from the server. ''' </remarks> Public Event ClientOutput As EventHandler(Of ClientOutputEventArghs) Protected Overridable Sub OnClientOutput(ByVal e As ClientOutputEventArghs) RaiseEvent ClientOutput(Me, e) End Sub Private Sub Button1_Click() Handles Button1.Click Dim t As New task(Sub() ExampleTcpOutput()) t.Start() Me.Hide() End Sub Private Sub ExampleTcpOutput() For n = 0 To 10 Me.OnClientOutput(New ClientOutputEventArghs("Hello world!!!!")) System.Threading.Thread.Sleep(500) Next Me.OnClientOutput(New ClientOutputEventArghs("Completed!!!!")) End Sub End Class
form 2 would look a little like...
vb Code:
Public Class Form2 Private WithEvents m_form1 As Form1 Public Sub New(Form1Instance As Form1) InitializeComponent() Me.m_form1 = Form1Instance End Sub Private Sub ClientOutputEventArghs(sender As Object, e As EventArgs) Handles m_form1.ClientOutput Dim value As String = DirectCast(e, ClientOutputEventArghs).clientOutPuteValue Me.RichTextBox1.Invoke(Sub() Me.RichTextBox1.AppendText(value & vbNewLine) End Sub) End Sub End Class
and the output class
vb Code:
Public Class ClientOutputEventArghs Inherits EventArgs Private ReadOnly m_clientOutPut As String Public ReadOnly Property clientOutPuteValue As String Get Return Me.m_clientOutPut End Get End Property Public Sub New(ByVal clientOutPut As String) Me.m_clientOutPut = clientOutPut End Sub End Class
-
Jan 31st, 2015, 02:13 PM
#4
Thread Starter
Member
Re: [VS 2010] [Multi-Threading] Invoke textbox to apply message, from another class.
 Originally Posted by ident
Private controls...
vb Code:
Imports System.Threading.Tasks Public Class Form1 ''' <summary> ''' Holds an instance of form2 initialized on new. ''' </summary> Private m_form2 As Form2 ''' <summary> ''' Creates a new instance of the <see cref="form1" /> Class. ''' </summary> Public Sub New() InitializeComponent() Me.m_form2 = New Form2(Me) Me.m_form2.Show() End Sub ''' <summary> ''' Represents the method that will handle the <see cref="form1.ClientOutput">PostData</see> event of <see cref="ClientOutput" />. ''' </summary> ''' <remarks > ''' The most recent Tcp client data retrieved from the server. ''' </remarks> Public Event ClientOutput As EventHandler(Of ClientOutputEventArghs) Protected Overridable Sub OnClientOutput(ByVal e As ClientOutputEventArghs) RaiseEvent ClientOutput(Me, e) End Sub Private Sub Button1_Click() Handles Button1.Click Dim t As New task(Sub() ExampleTcpOutput()) t.Start() Me.Hide() End Sub Private Sub ExampleTcpOutput() For n = 0 To 10 Me.OnClientOutput(New ClientOutputEventArghs("Hello world!!!!")) System.Threading.Thread.Sleep(500) Next Me.OnClientOutput(New ClientOutputEventArghs("Completed!!!!")) End Sub End Class
form 2 would look a little like...
vb Code:
Public Class Form2 Private WithEvents m_form1 As Form1 Public Sub New(Form1Instance As Form1) InitializeComponent() Me.m_form1 = Form1Instance End Sub Private Sub ClientOutputEventArghs(sender As Object, e As EventArgs) Handles m_form1.ClientOutput Dim value As String = DirectCast(e, ClientOutputEventArghs).clientOutPuteValue Me.RichTextBox1.Invoke(Sub() Me.RichTextBox1.AppendText(value & vbNewLine) End Sub) End Sub End Class
and the output class
vb Code:
Public Class ClientOutputEventArghs Inherits EventArgs Private ReadOnly m_clientOutPut As String Public ReadOnly Property clientOutPuteValue As String Get Return Me.m_clientOutPut End Get End Property Public Sub New(ByVal clientOutPut As String) Me.m_clientOutPut = clientOutPut End Sub End Class
Interesting facts you have there. I've read JMC's blog as recommended, but it didn't help anything too much. However, I found Niya's multi threading article more of use. I've read it before, but I didn't understand much of it at that time. When I compared and analyzed your code with hers, I somehow made an understanding of how the code worked. My TCP server works fine now, including my richtextbox! I recreated my project to fit the new code, and optimized it. I can mark this thread as resolved, as my main question has been solved, but I got another one.
The way my code works is following:
[LONG VERSION]
Main UI thread has a button, when pressed it will go to my TCP class (Class file), where it will start a new thread to do the 'TCP.Server' handling. My TCP server functions in a command based system, where the client sends commands of either login, chat, chat to user, share screen, send file... you name it. When such a command is received with its unique message ID, the TCP server will call my CommandHandler class (Class file). This class analyzes the commands and does the selected functions. Basically, this is where the magic happens.
The problem I now face, is when a command from my third class has been executed, it needs to be returned to the main UI thread. I think I might set the code up wrong, but in order to make it work; the 'CommandHandler' class needed to be Private only, not shared or something. Which means when I request for the richtextbox output, it will be returned to my TCP class, which runs on a different thread than the main UI thread. For the time being I've added a Public shared variable in the main UI, which the TCP class will update when needed. I currently use a timer to append the output to the richtextbox, as I can't directly touch anything in my UI thread with my TCP class thread. What I'm looking for is something similar, but easier method than Niya's. Like, when the variable's text has been changed, append the text to the richtextbox. I think of that as a way better way than using a timer, which constantly runs and drain memory.
[SHORT VERSION]
I have three classes which does my TCP handling, the main UI thread, the TCP class thread and the CommandHandler class. They work like this:
main UI thread(If server not running) -> Starts the TCP class thread(If message from user received) -> CommandHandler(Execute commands based on messages) class
The CommandHandler class will output ex. chat string from users, which the Main UI thread wants. But it stops half way in the process, the TCP thread. Is there a way to create a sub/function which will run when a variable's text is changed?
CommandHandler(Output information) -> TCP thread(Edits Main UI's thread myVariable) -> Main UI thread(Appends text to richtextbox when variable's text has been changed)
Note, I don't want any timers or unnecessary loops.
Code:
Dim myVariable as string
'When myVariable has been changed: Richtextbox1.text &= Environment.NewLine & myVariable
The Main UI thread can then append new data from the TCP thread, which received its data from the CommandHandler class.
Anyway, you have my deepest thanks @ident for helping with this frustrating problem!
With kind regards,
- Frek
Last edited by Frekvens1; Jan 31st, 2015 at 02:27 PM.
Reason: Spellings
-
Feb 2nd, 2015, 05:47 AM
#5
Thread Starter
Member
Re: [VS 2010] [Multi-Threading] Invoke textbox to apply message, from another class.
 Originally Posted by Frekvens1
Interesting facts you have there. I've read JMC's blog as recommended, but it didn't help anything too much. However, I found Niya's multi threading article more of use. I've read it before, but I didn't understand much of it at that time. When I compared and analyzed your code with hers, I somehow made an understanding of how the code worked. My TCP server works fine now, including my richtextbox! I recreated my project to fit the new code, and optimized it. I can mark this thread as resolved, as my main question has been solved, but I got another one.
The way my code works is following:
[LONG VERSION]
Main UI thread has a button, when pressed it will go to my TCP class (Class file), where it will start a new thread to do the 'TCP.Server' handling. My TCP server functions in a command based system, where the client sends commands of either login, chat, chat to user, share screen, send file... you name it. When such a command is received with its unique message ID, the TCP server will call my CommandHandler class (Class file). This class analyzes the commands and does the selected functions. Basically, this is where the magic happens.
The problem I now face, is when a command from my third class has been executed, it needs to be returned to the main UI thread. I think I might set the code up wrong, but in order to make it work; the ' CommandHandler' class needed to be Private only, not shared or something. Which means when I request for the richtextbox output, it will be returned to my TCP class, which runs on a different thread than the main UI thread. For the time being I've added a Public shared variable in the main UI, which the TCP class will update when needed. I currently use a timer to append the output to the richtextbox, as I can't directly touch anything in my UI thread with my TCP class thread. What I'm looking for is something similar, but easier method than Niya's. Like, when the variable's text has been changed, append the text to the richtextbox. I think of that as a way better way than using a timer, which constantly runs and drain memory.
[SHORT VERSION]
I have three classes which does my TCP handling, the main UI thread, the TCP class thread and the CommandHandler class. They work like this:
main UI thread(If server not running) -> Starts the TCP class thread(If message from user received) -> CommandHandler(Execute commands based on messages) class
The CommandHandler class will output ex. chat string from users, which the Main UI thread wants. But it stops half way in the process, the TCP thread. Is there a way to create a sub/function which will run when a variable's text is changed?
CommandHandler(Output information) -> TCP thread(Edits Main UI's thread myVariable) -> Main UI thread(Appends text to richtextbox when variable's text has been changed)
Note, I don't want any timers or unnecessary loops.
Code:
Dim myVariable as string
'When myVariable has been changed: Richtextbox1.text &= Environment.NewLine & myVariable
The Main UI thread can then append new data from the TCP thread, which received its data from the CommandHandler class.
Anyway, you have my deepest thanks @ident for helping with this frustrating problem!
With kind regards,
- Frek
It seems people don't see this question. Therefore will I create a new thread with my new question, in better detail. If you want to answer to this problem, follow this link: http://www.vbforums.com/showthread.p...49#post4829349
- Frek
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|