|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Control (textbox) update from thread invisibleuntil 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 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 > 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 > > Hell,
looking at your code... >> > While t.IsAlive This effectively block your Message queue on your window from being>> > Thread.Sleep(20) >> > End While 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 >> > > 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 > >> > > > 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 >> >> > >> > > brett.m***@gmail.com wrote:
> I was assuming that if I didn't do this, the main thread would just <snip>> 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) <snip>>>>>> ' 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 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 |
|||||||||||||||||||||||