Home All Groups Group Topic Archive Search About

Control (textbox) update from thread invisible

Author
8 Jan 2007 4:21 PM
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

Author
8 Jan 2007 4:56 PM
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

<brett.m***@gmail.com> wrote in message
Show quote
news:1168273287.420933.130940@42g2000cwt.googlegroups.com...
> 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
>
Author
8 Jan 2007 5:17 PM
brett.mack
Yes, but how do I do that?

Henning Krause [MVP - Exchange] wrote:
Show quote
> 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
>
> <brett.m***@gmail.com> wrote in message
> news:1168273287.420933.130940@42g2000cwt.googlegroups.com...
> > 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
> >
Author
8 Jan 2007 6:14 PM
Henning Krause [MVP - Exchange]
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
<brett.m***@gmail.com> wrote in message
Show quote
news:1168276639.920032.39750@i15g2000cwa.googlegroups.com...
> 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
>>
>> <brett.m***@gmail.com> wrote in message
>> news:1168273287.420933.130940@42g2000cwt.googlegroups.com...
>> > 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
>> >
>
Author
9 Jan 2007 1:49 PM
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

Henning Krause [MVP - Exchange] wrote:
Show quote
> 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
> <brett.m***@gmail.com> wrote in message
> news:1168276639.920032.39750@i15g2000cwa.googlegroups.com...
> > 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
> >>
> >> <brett.m***@gmail.com> wrote in message
> >> news:1168273287.420933.130940@42g2000cwt.googlegroups.com...
> >> > 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
> >> >
> >
Author
9 Jan 2007 2:39 PM
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


<brett.m***@gmail.com> wrote in message
Show quote
news:1168350547.724654.28410@s34g2000cwa.googlegroups.com...
>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
>
> Henning Krause [MVP - Exchange] wrote:
>> 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
>> <brett.m***@gmail.com> wrote in message
>> news:1168276639.920032.39750@i15g2000cwa.googlegroups.com...
>> > 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
>> >>
>> >> <brett.m***@gmail.com> wrote in message
>> >> news:1168273287.420933.130940@42g2000cwt.googlegroups.com...
>> >> > 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
>> >> >
>> >
>
Author
10 Jan 2007 12:51 PM
Lasse_Vågsæther_Karlsen
brett.m***@gmail.com wrote:
> 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?
<snip>
>>>>>    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.

--
Lasse Vågsæther Karlsen
mailto:la***@vkarlsen.no

AddThis Social Bookmark Button