Control (textbox) update from thread invisible

B

brett.mack

Hello! I'm trying to update a text box from a thread. It works fine
until my thread is created from a seperate class. To illustrate, here's
my form code (the textbox is txtOutput.text) and for the class:

(This all works fine if the "Count" class is declared simply as a
function within form1 - but outside of that the control updates are
invisible!

Imports System.Threading
Delegate Sub SetTextCallback(ByVal [text] As String)
Public Class Form1
Dim tClass As count
Dim t As Thread
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnStart.Click
tClass = New count
t = New Thread(AddressOf tClass.bProcess)
t.Start()
While t.IsAlive
Thread.Sleep(20)
End While
End Sub

Public Sub SetText(ByVal [text] As String)
' InvokeRequired required compares the thread ID of the
' calling thread to the thread ID of the creating thread.
' If these threads are different, it returns true.
If Me.txtOutput.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.txtOutput.Text = [text] & txtOutput.Text
Me.txtOutput.Refresh()
End If
End Sub

Private Sub btnAbort_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnAbort.Click
t.Abort()
End Sub
End Class

And here's the class for the count class:

Public Class count
Inherits Form1
Public i As Integer

Sub New()
i = 0
End Sub
Sub bProcess()
For i = 1 To 1000
'If i Mod 25 = 0 Then
' SetText(i.ToString & " " & ControlChars.CrLf)
' 'txtOutput.Text = i.ToString & " " & ControlChars.CrLf
'Else
SetText(i.ToString & " ")
'End If
Next
End Sub
End Class
 
H

Henning Krause [MVP - Exchange]

Hello,

UI updated are only possible from the thread which created the UI control...

The solution for your problem here is to call the Invoke method from the
Form or Textbox where the form resides and update the Textbox via a
delegate.

Best regards,
Henning Krause
 
B

brett.mack

Yes, but how do I do that?
Hello,

UI updated are only possible from the thread which created the UI control...

The solution for your problem here is to call the Invoke method from the
Form or Textbox where the form resides and update the Textbox via a
delegate.

Best regards,
Henning Krause

Hello! I'm trying to update a text box from a thread. It works fine
until my thread is created from a seperate class. To illustrate, here's
my form code (the textbox is txtOutput.text) and for the class:

(This all works fine if the "Count" class is declared simply as a
function within form1 - but outside of that the control updates are
invisible!

Imports System.Threading
Delegate Sub SetTextCallback(ByVal [text] As String)
Public Class Form1
Dim tClass As count
Dim t As Thread
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnStart.Click
tClass = New count
t = New Thread(AddressOf tClass.bProcess)
t.Start()
While t.IsAlive
Thread.Sleep(20)
End While
End Sub

Public Sub SetText(ByVal [text] As String)
' InvokeRequired required compares the thread ID of the
' calling thread to the thread ID of the creating thread.
' If these threads are different, it returns true.
If Me.txtOutput.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.txtOutput.Text = [text] & txtOutput.Text
Me.txtOutput.Refresh()
End If
End Sub

Private Sub btnAbort_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnAbort.Click
t.Abort()
End Sub
End Class

And here's the class for the count class:

Public Class count
Inherits Form1
Public i As Integer

Sub New()
i = 0
End Sub
Sub bProcess()
For i = 1 To 1000
'If i Mod 25 = 0 Then
' SetText(i.ToString & " " & ControlChars.CrLf)
' 'txtOutput.Text = i.ToString & " " & ControlChars.CrLf
'Else
SetText(i.ToString & " ")
'End If
Next
End Sub
End Class
 
H

Henning Krause [MVP - Exchange]

Hell,

looking at your code...

This effectively block your Message queue on your window from being
processed....

And I don't understand why you do this...

Best regards,
Henning Krause

why do you
Yes, but how do I do that?
Hello,

UI updated are only possible from the thread which created the UI
control...

The solution for your problem here is to call the Invoke method from the
Form or Textbox where the form resides and update the Textbox via a
delegate.

Best regards,
Henning Krause

Hello! I'm trying to update a text box from a thread. It works fine
until my thread is created from a seperate class. To illustrate, here's
my form code (the textbox is txtOutput.text) and for the class:

(This all works fine if the "Count" class is declared simply as a
function within form1 - but outside of that the control updates are
invisible!

Imports System.Threading
Delegate Sub SetTextCallback(ByVal [text] As String)
Public Class Form1
Dim tClass As count
Dim t As Thread
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnStart.Click
tClass = New count
t = New Thread(AddressOf tClass.bProcess)
t.Start()
While t.IsAlive
Thread.Sleep(20)
End While
End Sub

Public Sub SetText(ByVal [text] As String)
' InvokeRequired required compares the thread ID of the
' calling thread to the thread ID of the creating thread.
' If these threads are different, it returns true.
If Me.txtOutput.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.txtOutput.Text = [text] & txtOutput.Text
Me.txtOutput.Refresh()
End If
End Sub

Private Sub btnAbort_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnAbort.Click
t.Abort()
End Sub
End Class

And here's the class for the count class:

Public Class count
Inherits Form1
Public i As Integer

Sub New()
i = 0
End Sub
Sub bProcess()
For i = 1 To 1000
'If i Mod 25 = 0 Then
' SetText(i.ToString & " " & ControlChars.CrLf)
' 'txtOutput.Text = i.ToString & " " & ControlChars.CrLf
'Else
SetText(i.ToString & " ")
'End If
Next
End Sub
End Class
 
B

brett.mack

I was assuming that if I didn't do this, the main thread would just
suck all of the processor iterating through the while loop; I guess now
that isn't correct? Sorry, I've been teaching myself on the fly, no
formal programming education (I'm sure it shows)!
The below code works.

Question: On the main form code "SetText" routine, should I uncomment
the Monitor.Enter(me) & Monitor.Exit(me) when I have more than one
background thread running at the same time, updating the textbox?

What I've done so far is this. Main form code follows:
Imports System.Threading
Public Delegate Sub SetTextCallback(ByVal [text] As String)

Public Class Form1
Dim tClass As count
Dim t As Thread
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnStart.Click
btnStart.Enabled = False
tClass = New count
t = New Thread(AddressOf tClass.bProcess)
t.Start()
btnStart.Enabled = True
End Sub
Public Sub SetText(ByVal [text] As String)
' InvokeRequired required compares the thread ID of the
' calling thread to the thread ID of the creating thread.
' If these threads are different, it returns true.
'Monitor.Enter(Me)
If Me.txtOutput.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.txtOutput.Text = [text] & txtOutput.Text
Me.txtOutput.Refresh()
End If
'Monitor.Exit(Me)
End Sub
Private Sub btnAbort_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnAbort.Click
t.Abort()
End Sub

End Class

And in the "Count" class code, I've put this:

Imports system.threading

Public Class count
Public i As Integer
Dim f1 As Form1
Dim st As SetTextCallback
Sub New()
i = 0
st = New SetTextCallback(AddressOf Form1.SetText)
End Sub
Sub bProcess()
For i = 1 To 1000
st(i.ToString & " ")
Thread.Sleep(10)
Next
End Sub
End Class
Hell,

looking at your code...

This effectively block your Message queue on your window from being
processed....

And I don't understand why you do this...

Best regards,
Henning Krause

why do you
Yes, but how do I do that?
Hello,

UI updated are only possible from the thread which created the UI
control...

The solution for your problem here is to call the Invoke method from the
Form or Textbox where the form resides and update the Textbox via a
delegate.

Best regards,
Henning Krause

Hello! I'm trying to update a text box from a thread. It works fine
until my thread is created from a seperate class. To illustrate, here's
my form code (the textbox is txtOutput.text) and for the class:

(This all works fine if the "Count" class is declared simply as a
function within form1 - but outside of that the control updates are
invisible!

Imports System.Threading
Delegate Sub SetTextCallback(ByVal [text] As String)
Public Class Form1
Dim tClass As count
Dim t As Thread
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnStart.Click
tClass = New count
t = New Thread(AddressOf tClass.bProcess)
t.Start()
While t.IsAlive
Thread.Sleep(20)
End While
End Sub

Public Sub SetText(ByVal [text] As String)
' InvokeRequired required compares the thread ID of the
' calling thread to the thread ID of the creating thread.
' If these threads are different, it returns true.
If Me.txtOutput.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.txtOutput.Text = [text] & txtOutput.Text
Me.txtOutput.Refresh()
End If
End Sub

Private Sub btnAbort_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnAbort.Click
t.Abort()
End Sub
End Class

And here's the class for the count class:

Public Class count
Inherits Form1
Public i As Integer

Sub New()
i = 0
End Sub
Sub bProcess()
For i = 1 To 1000
'If i Mod 25 = 0 Then
' SetText(i.ToString & " " & ControlChars.CrLf)
' 'txtOutput.Text = i.ToString & " " & ControlChars.CrLf
'Else
SetText(i.ToString & " ")
'End If
Next
End Sub
End Class
 
H

Henning Krause [MVP - Exchange]

Hello,

I would assume that setting a text value is not a thread safe operaiton, so
using a Monitor here is ok. But you should use the SyncLock keyword in
Visual Basic, as it will wrap the Monitor.Exit in a finally block.

Best regards,
Henning Krause


I was assuming that if I didn't do this, the main thread would just
suck all of the processor iterating through the while loop; I guess now
that isn't correct? Sorry, I've been teaching myself on the fly, no
formal programming education (I'm sure it shows)!
The below code works.

Question: On the main form code "SetText" routine, should I uncomment
the Monitor.Enter(me) & Monitor.Exit(me) when I have more than one
background thread running at the same time, updating the textbox?

What I've done so far is this. Main form code follows:
Imports System.Threading
Public Delegate Sub SetTextCallback(ByVal [text] As String)

Public Class Form1
Dim tClass As count
Dim t As Thread
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnStart.Click
btnStart.Enabled = False
tClass = New count
t = New Thread(AddressOf tClass.bProcess)
t.Start()
btnStart.Enabled = True
End Sub
Public Sub SetText(ByVal [text] As String)
' InvokeRequired required compares the thread ID of the
' calling thread to the thread ID of the creating thread.
' If these threads are different, it returns true.
'Monitor.Enter(Me)
If Me.txtOutput.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.txtOutput.Text = [text] & txtOutput.Text
Me.txtOutput.Refresh()
End If
'Monitor.Exit(Me)
End Sub
Private Sub btnAbort_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnAbort.Click
t.Abort()
End Sub

End Class

And in the "Count" class code, I've put this:

Imports system.threading

Public Class count
Public i As Integer
Dim f1 As Form1
Dim st As SetTextCallback
Sub New()
i = 0
st = New SetTextCallback(AddressOf Form1.SetText)
End Sub
Sub bProcess()
For i = 1 To 1000
st(i.ToString & " ")
Thread.Sleep(10)
Next
End Sub
End Class
Hell,

looking at your code...
While t.IsAlive
Thread.Sleep(20)
End While

This effectively block your Message queue on your window from being
processed....

And I don't understand why you do this...

Best regards,
Henning Krause

why do you
Yes, but how do I do that?

Henning Krause [MVP - Exchange] wrote:
Hello,

UI updated are only possible from the thread which created the UI
control...

The solution for your problem here is to call the Invoke method from
the
Form or Textbox where the form resides and update the Textbox via a
delegate.

Best regards,
Henning Krause

Hello! I'm trying to update a text box from a thread. It works fine
until my thread is created from a seperate class. To illustrate,
here's
my form code (the textbox is txtOutput.text) and for the class:

(This all works fine if the "Count" class is declared simply as a
function within form1 - but outside of that the control updates are
invisible!

Imports System.Threading
Delegate Sub SetTextCallback(ByVal [text] As String)
Public Class Form1
Dim tClass As count
Dim t As Thread
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnStart.Click
tClass = New count
t = New Thread(AddressOf tClass.bProcess)
t.Start()
While t.IsAlive
Thread.Sleep(20)
End While
End Sub

Public Sub SetText(ByVal [text] As String)
' InvokeRequired required compares the thread ID of the
' calling thread to the thread ID of the creating thread.
' If these threads are different, it returns true.
If Me.txtOutput.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.txtOutput.Text = [text] & txtOutput.Text
Me.txtOutput.Refresh()
End If
End Sub

Private Sub btnAbort_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnAbort.Click
t.Abort()
End Sub
End Class

And here's the class for the count class:

Public Class count
Inherits Form1
Public i As Integer

Sub New()
i = 0
End Sub
Sub bProcess()
For i = 1 To 1000
'If i Mod 25 = 0 Then
' SetText(i.ToString & " " & ControlChars.CrLf)
' 'txtOutput.Text = i.ToString & " " &
ControlChars.CrLf
'Else
SetText(i.ToString & " ")
'End If
Next
End Sub
End Class
 
?

=?ISO-8859-1?Q?Lasse_V=E5gs=E6ther_Karlsen?=

I was assuming that if I didn't do this, the main thread would just
suck all of the processor iterating through the while loop; I guess now
that isn't correct? Sorry, I've been teaching myself on the fly, no
formal programming education (I'm sure it shows)!
The below code works.

Question: On the main form code "SetText" routine, should I uncomment
the Monitor.Enter(me) & Monitor.Exit(me) when I have more than one
background thread running at the same time, updating the textbox?
Public Sub SetText(ByVal [text] As String)
' InvokeRequired required compares the thread ID of the
' calling thread to the thread ID of the creating thread.
' If these threads are different, it returns true.
If Me.txtOutput.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.txtOutput.Text = [text] & txtOutput.Text
Me.txtOutput.Refresh()
End If
End Sub
<snip>

You do not need the monitor for this method, the way it is above is
fine. Messages to the main thread are processed one at a time.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top