taz8020
asked on
Update label on progress bar from different thread.
I need to update a ToolStripStatusLabel from a different thread, Problem is ToolStripStatusLabel does not have a InvokeRequired, however the StatusStrip1 does but don get how I can use that.
Also If I use the following it is as through the InvokeRequired is false.
If MainForm.StatusStrip1.Invo keRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
MainForm.StatusStrip1.Invo ke(d, New Object() {UpdateText})
Else
MainForm.ToolStripStatusUp dateProgre ss.Text = UpdateText
End If
So then I tried MainForm.ToolStripStatusUp dateProgre ss.Text = UpdateText
which does not throw error but does not update text.
Please help.
Also If I use the following it is as through the InvokeRequired is false.
If MainForm.StatusStrip1.Invo
Dim d As New SetTextCallback(AddressOf SetText)
MainForm.StatusStrip1.Invo
Else
MainForm.ToolStripStatusUp
End If
So then I tried MainForm.ToolStripStatusUp
which does not throw error but does not update text.
Please help.
You may have to add an "Application.DoEvents" line to force the GUI to update.
ASKER
Hi strickdd, no just tried this
MainForm.ToolStripStatusUp dateProgre ss.Text = UpdateText
Application.DoEvents()
Does not work either, dont get error but does not update either. Any other ideas?
MainForm.ToolStripStatusUp
Application.DoEvents()
Does not work either, dont get error but does not update either. Any other ideas?
It looks like you are using "default instance" syntax for the main form.
How was MainForm initially displayed? Was it the "startup object" of the project?
...or was it displayed using the "New" keyword?
Dim mf As New MainForm()
mf.Show()
You are probably setting the text of the label for the wrong instance of MainForm (not the one being displayed)...
How was MainForm initially displayed? Was it the "startup object" of the project?
...or was it displayed using the "New" keyword?
Dim mf As New MainForm()
mf.Show()
You are probably setting the text of the label for the wrong instance of MainForm (not the one being displayed)...
ASKER
Hi IdleMind, there is a login form that opens the main form without creating an instance it just uses MainForm.Show()
If I try and update a textbox on the main page from the background worker this works fine:
If MainForm.textbox1.InvokeRe quired Then
Dim d As New SetTextCallback(AddressOf SetText)
MainForm.textbox1.Invoke(d , New Object() {"Test"})
Else
MainForm.textbox1.Text = UpdateText
End If
Any Ideas
If I try and update a textbox on the main page from the background worker this works fine:
If MainForm.textbox1.InvokeRe
Dim d As New SetTextCallback(AddressOf SetText)
MainForm.textbox1.Invoke(d
Else
MainForm.textbox1.Text = UpdateText
End If
Any Ideas
Hmmm...OK.
You don't actually need to Invoke() against the control itself...the Form will always work.
Also, you can use BeginInvoke() to make it asynchronous:
You don't actually need to Invoke() against the control itself...the Form will always work.
Also, you can use BeginInvoke() to make it asynchronous:
Public Sub SetText(ByVal UpdateText As String)
If MainForm.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
MainForm.BeginInvoke(d, New Object() {UpdateText})
Else
MainForm.ToolStripStatusUpdateProgress.Text = UpdateText
End If
End Sub
ASKER
Hi Idle_Mind, just tried that but does not work. May be there is something I am doing wrong that I am not telling you both.
1). Loginform opens main form using MainForm.Show()
2). On the main form there is a button to update website.
3). On the click event of the button it calls the background worker which is on the main form.
4). The background worker calls the updateweb function which, this is where the above code is
Delegate Sub SetTextCallback(ByVal [text] As String)
Private Sub SetText(ByVal [text] As String)
MainForm.ToolStripStatusUp dateProgre ss.Text = [text]
End Sub
Public Sub UpdateWeb()
'Updates tables here !!!!!!!!!!
If MainForm.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
MainForm.BeginInvoke(d, New Object() {UpdateText})
Else
MainForm.ToolStripStatusUp dateProgre ss.Text = UpdateText
End If
End Sub
1). Loginform opens main form using MainForm.Show()
2). On the main form there is a button to update website.
3). On the click event of the button it calls the background worker which is on the main form.
4). The background worker calls the updateweb function which, this is where the above code is
Delegate Sub SetTextCallback(ByVal [text] As String)
Private Sub SetText(ByVal [text] As String)
MainForm.ToolStripStatusUp
End Sub
Public Sub UpdateWeb()
'Updates tables here !!!!!!!!!!
If MainForm.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
MainForm.BeginInvoke(d, New Object() {UpdateText})
Else
MainForm.ToolStripStatusUp
End If
End Sub
Would need to see more code then to understand where the breakdown is. =\
ASKER
Ok here is the code used
Private Sub BackgroundWorkerUpdateWinH ost_DoWork (ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWo rkEventArg s) Handles BackgroundWorkerUpdateWinH ost.DoWork
SyncWinHost()
End Sub
Public Sub SyncWinHost()
' Insert
UpdateProductsToWinHost(Li nkedServer Name, LinkedServerDataBaseName,
LocalServerDataBaseName)
'Delete
End Sub
Public Function UpdateProductsToWinHost(By Val LinkedServerName As String, ByVal LinkedServerDataBaseName As String, ByVal LocalServerDataBaseName As String)
Dim SQLQuery As String
Dim SQLResult As Integer
Dim TotalCount As Integer
Dim StartTime As Date
Dim UpdateText As String
Dim TimeTakenInSecs As Integer
Dim QtyToUpdatePerSQLQuery As Integer = 100
UpdateText = "Product Update Started..."
MainForm.ToolStripStatusUp dateProgre ss.Text = UpdateText
SQLQuery = "UPDATE Top(" & QtyToUpdatePerSQLQuery & ") WebServerProducts " & _
"SET " & _
"WebServerProducts.ShortDe scription = LocalServerProducts.ShortD escription , " & _
"WebServerProducts.FullDes cription = LocalServerProducts.FullDe scription, " & _
"WebServerProducts.ItemWei ght = LocalServerProducts.ItemWe ight, " & _
"WebServerProducts.PriceFr omOnly = LocalServerProducts.PriceF romOnly, " & _
"WebServerProducts.SearchT erm1 = LocalServerProducts.Search Term1, " & _
"WebServerProducts.SearchT erm2 = LocalServerProducts.Search Term2, " & _
"WebServerProducts.SearchT erm3 = LocalServerProducts.Search Term3, " & _
"WebServerProducts.SearchT erm4 = LocalServerProducts.Search Term4, " & _
"WebServerProducts.SearchT erm5 = LocalServerProducts.Search Term5 ," & _
"WebServerProducts.SearchT erm6 = LocalServerProducts.Search Term6, " & _
"WebServerProducts.WebSect ion = LocalServerProducts.WebSec tion, " & _
"WebServerProducts.LastUpD ate = LocalServerProducts.LastUp Date " & _
"FROM [" & LocalServerDataBaseName & "].[dbo].[Products] as LocalServerProducts, [" & LinkedServerName & "]." & LinkedServerDataBaseName & ".dbo.Products as WebServerProducts " & _
"WHERE (WebServerProducts.Product Ref = LocalServerProducts.Produc tRef) And (WebServerProducts.LastUpD ate Is Null)"
'"WHERE (WebServerProducts.Product Ref = LocalServerProducts.Produc tRef) And (WebServerProducts.LastUpD ate < LocalServerProducts.LastUp Date)"
Redo:
StartTime = Date.Now
SQLResult = RunSQLquery(SQLQuery, "")
TotalCount += SQLResult
' ProductListOpen.LabelRowsS elected.Te xt = String.Format("{0} Products Updated So Far", TotalCount)
TimeTakenInSecs = DateDiff(DateInterval.Seco nd, StartTime, Date.Now)
UpdateText = String.Format("{0} Products Updated On Website, Last {1} updates took {2} Seconds", TotalCount.ToString, QtyToUpdatePerSQLQuery, TimeTakenInSecs)
If MainForm.StatusStrip1.Invo keRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
MainForm.StatusStrip1.Invo ke(d, New Object() {UpdateText})
Else
MainForm.ToolStripStatusUp dateProgre ss.Text = UpdateText
End If
If SQLResult > 0 Then
GoTo Redo
End If
Return TotalCount.ToString
End Function
Delegate Sub SetTextCallback(ByVal [text] As String)
Private Sub SetText(ByVal [text] As String)
MainForm.ToolStripStatusUp dateProgre ss.Text = [text]
End Sub
Private Sub BackgroundWorkerUpdateWinH
SyncWinHost()
End Sub
Public Sub SyncWinHost()
' Insert
UpdateProductsToWinHost(Li
LocalServerDataBaseName)
'Delete
End Sub
Public Function UpdateProductsToWinHost(By
Dim SQLQuery As String
Dim SQLResult As Integer
Dim TotalCount As Integer
Dim StartTime As Date
Dim UpdateText As String
Dim TimeTakenInSecs As Integer
Dim QtyToUpdatePerSQLQuery As Integer = 100
UpdateText = "Product Update Started..."
MainForm.ToolStripStatusUp
SQLQuery = "UPDATE Top(" & QtyToUpdatePerSQLQuery & ") WebServerProducts " & _
"SET " & _
"WebServerProducts.ShortDe
"WebServerProducts.FullDes
"WebServerProducts.ItemWei
"WebServerProducts.PriceFr
"WebServerProducts.SearchT
"WebServerProducts.SearchT
"WebServerProducts.SearchT
"WebServerProducts.SearchT
"WebServerProducts.SearchT
"WebServerProducts.SearchT
"WebServerProducts.WebSect
"WebServerProducts.LastUpD
"FROM [" & LocalServerDataBaseName & "].[dbo].[Products] as LocalServerProducts, [" & LinkedServerName & "]." & LinkedServerDataBaseName & ".dbo.Products as WebServerProducts " & _
"WHERE (WebServerProducts.Product
'"WHERE (WebServerProducts.Product
Redo:
StartTime = Date.Now
SQLResult = RunSQLquery(SQLQuery, "")
TotalCount += SQLResult
' ProductListOpen.LabelRowsS
TimeTakenInSecs = DateDiff(DateInterval.Seco
UpdateText = String.Format("{0} Products Updated On Website, Last {1} updates took {2} Seconds", TotalCount.ToString, QtyToUpdatePerSQLQuery, TimeTakenInSecs)
If MainForm.StatusStrip1.Invo
Dim d As New SetTextCallback(AddressOf SetText)
MainForm.StatusStrip1.Invo
Else
MainForm.ToolStripStatusUp
End If
If SQLResult > 0 Then
GoTo Redo
End If
Return TotalCount.ToString
End Function
Delegate Sub SetTextCallback(ByVal [text] As String)
Private Sub SetText(ByVal [text] As String)
MainForm.ToolStripStatusUp
End Sub
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I never knew there was a e.UserState, that sounds like it. Will try it now.
You can pass anything (simple types, structs, classes/references, etc) in the second paramter of ReportProgress().
If you need to pass multiple values out then you can use a custom class that holds all of them and then cast e.UserState back to your custom class in the ProgressChanged() event. =)
If you need to pass multiple values out then you can use a custom class that holds all of them and then cast e.UserState back to your custom class in the ProgressChanged() event. =)
ASKER
No, it still does not work, what you said makes perfect sence but does not work.
I put a break point in the BackgroundWorkerUpdateWinH ost_Progre ssChanged and can see the UserState has the correct value. But not updating on the main form.
I even added me.text = e.UserState but still does not work. So then added
Private Sub BackgroundWorkerUpdateWinH ost_Progre ssChanged( ByVal sender As System.Object, ByVal e As System.ComponentModel.Prog ressChange dEventArgs ) Handles BackgroundWorkerUpdateWinH ost.Progre ssChanged
Me.ToolStripStatusUpdatePr ogress.Tex t = e.UserState
Me.Text = e.UserState
Application.DoEvents()
End Sub
Still no update. I have been on this all day now and pulling my hair out at this one.
If I use this, it updates the label, but its out side the fuction:
Private Sub SyncWinHostToolStripMenuIt em_Click(B yVal sender As System.Object, ByVal e As System.EventArgs) Handles SyncWinHostToolStripMenuIt em.Click
'todo if over 1 hour sync but check last local update first
If BackgroundWorkerUpdateWinH ost.IsBusy = False Then
BackgroundWorkerUpdateWinH ost.RunWor kerAsync()
Me.ToolStripStatusUpdatePr ogress.Tex t = "TEST"
Else
MsgBox("Process is already updating")
End If
End Sub
I put a break point in the BackgroundWorkerUpdateWinH
I even added me.text = e.UserState but still does not work. So then added
Private Sub BackgroundWorkerUpdateWinH
Me.ToolStripStatusUpdatePr
Me.Text = e.UserState
Application.DoEvents()
End Sub
Still no update. I have been on this all day now and pulling my hair out at this one.
If I use this, it updates the label, but its out side the fuction:
Private Sub SyncWinHostToolStripMenuIt
'todo if over 1 hour sync but check last local update first
If BackgroundWorkerUpdateWinH
BackgroundWorkerUpdateWinH
Me.ToolStripStatusUpdatePr
Else
MsgBox("Process is already updating")
End If
End Sub
Ok...something is still tying up the main UI thread despite the BackgroundWorker().
At line #51 you have:
SQLResult = RunSQLquery(SQLQuery, "")
What is in RunSQLquery()?
At line #51 you have:
SQLResult = RunSQLquery(SQLQuery, "")
What is in RunSQLquery()?
Also, do you have any polling loops running somewhere else in the form code?...or anywhere else?
ASKER
The RunSQLquery is below
There is a lot of code, please explain what you mean by polling loops. There is a timer checking phone calls every 15 secs. within the timer is calles another background worker every 5 mins. I turned these off but still get the same error.
If I put MsgBox(e.UserState) it does pop up with the message.
Public Function RunSQLquery(ByVal SQLQuery As String, ByVal ConnectionString As String)
If ConnectionString = "" Then
ConnectionString = ConnectToCompanyData.DataB aseConnect ionString
End If
Dim myConnection As New SqlConnection(ConnectionSt ring)
Dim SQLResult As String
Dim myCommand As SqlCommand
myCommand = New SqlCommand(SQLQuery, myConnection)
Using myConnection
Try
myConnection.Open()
myCommand.CommandTimeout = 5000
SQLResult = myCommand.ExecuteNonQuery
Catch ex As Exception
SQLResult = ex.Message
End Try
End Using
myConnection.Close()
Return SQLResult
End Function
There is a lot of code, please explain what you mean by polling loops. There is a timer checking phone calls every 15 secs. within the timer is calles another background worker every 5 mins. I turned these off but still get the same error.
If I put MsgBox(e.UserState) it does pop up with the message.
Public Function RunSQLquery(ByVal SQLQuery As String, ByVal ConnectionString As String)
If ConnectionString = "" Then
ConnectionString = ConnectToCompanyData.DataB
End If
Dim myConnection As New SqlConnection(ConnectionSt
Dim SQLResult As String
Dim myCommand As SqlCommand
myCommand = New SqlCommand(SQLQuery, myConnection)
Using myConnection
Try
myConnection.Open()
myCommand.CommandTimeout = 5000
SQLResult = myCommand.ExecuteNonQuery
Catch ex As Exception
SQLResult = ex.Message
End Try
End Using
myConnection.Close()
Return SQLResult
End Function
That query code looks ok. *as long as it is run from the backgroundworker*
Polling loops are tight looping structures that stay within the loop until some condition is met:
While Not SomeFlag
End While
A tight loop somewhere else could prevent the main UI thread from processing the update events.
How is the Timer for the Phone Calls implemented? Is that using a loop or a Timer control?
Polling loops are tight looping structures that stay within the loop until some condition is met:
While Not SomeFlag
End While
A tight loop somewhere else could prevent the main UI thread from processing the update events.
How is the Timer for the Phone Calls implemented? Is that using a loop or a Timer control?
ASKER
One I just tried was updating a label which is in a user control on the main form. It needed the Label1.InvokeRequired but worked on the usercontrol so must be some thing wrong with main form.
You have been more than helpfull on this and cannot thank you enough.
Private Sub BackgroundWorkerUpdateWinH ost_Progre ssChanged( ByVal sender As System.Object, ByVal e As System.ComponentModel.Prog ressChange dEventArgs ) Handles BackgroundWorkerUpdateWinH ost.Progre ssChanged
Me.ToolStripStatusUpdatePr ogress.Tex t = e.UserState
If ProductListOpen.LabelRowsS elected.In vokeRequir ed Then
Dim d As New SetTextCallback(AddressOf SetText)
ProductListOpen.LabelRowsS elected.In voke(d, New Object() {e.UserState})
Else
ProductListOpen.LabelRowsS elected.Te xt = e.UserState
End If
End Sub
You have been more than helpfull on this and cannot thank you enough.
Private Sub BackgroundWorkerUpdateWinH
Me.ToolStripStatusUpdatePr
If ProductListOpen.LabelRowsS
Dim d As New SetTextCallback(AddressOf SetText)
ProductListOpen.LabelRowsS
Else
ProductListOpen.LabelRowsS
End If
End Sub
All controls run in the same main UI thread so if you Invoke() off the Form or use the ProgressChanged() event, which is already Invoked/marshalled for you, then it should work.
How/when were those UserControls created?
How/when were those UserControls created?
ASKER
I know most people would give up by now but things like this, bug the hell out of me. When the mainform loads is loads as usercontrol called ProductListOpen this is held in a varible until it is required. A menu button then displays it on the form within a panel.
I have to use the Invoked within the ProgressChanged sub. But i thought I could have just used ProductListOpen.LabelRowsS elected.Te xt = e.UserState as I thought ProgressChanged would have been on the same thread as the mainform. Does this sound right to you?
Tomorrow I will try the same thing on a new form. If that works I will comment out parts of the main form and see if I can get it to work. Once again you have been more than helpful so will accept this and open a new post. If you could help on the new post I will post the link on this one.
I have to use the Invoked within the ProgressChanged sub. But i thought I could have just used ProductListOpen.LabelRowsS
Tomorrow I will try the same thing on a new form. If that works I will comment out parts of the main form and see if I can get it to work. Once again you have been more than helpful so will accept this and open a new post. If you could help on the new post I will post the link on this one.
That's correct. The ProgressChanged() event is already running on the main UI thread so no Invoke() should be necessary. Post a link here or create a "related question" when you setup the new question.
ASKER
Hi Idle_Mind, at last got it going, but dont understand why it would cause the problem. The SyncWinHost funcution was in another code.vb file I moved this to the mainform and all works well. I like to put long bits of code in serperate files to make it cleaner. Why would this have caused a problem as they were public?
I also have a problem with a ASP menu that is taking a long time to load. I load the data into an application varible to save loading the data each time. However as it is a big menu it till loops through each row so wanted to try and put the populated control its self in a varible. I have asked this question on here but did not get vey far.
If you know the answer will open a new question and post a link here.
I also have a problem with a ASP menu that is taking a long time to load. I load the data into an application varible to save loading the data each time. However as it is a big menu it till loops through each row so wanted to try and put the populated control its self in a varible. I have asked this question on here but did not get vey far.
If you know the answer will open a new question and post a link here.
I'm not sure why being in a different file would cause a problem either. Was it Public in a Module?
I don't work at all with web-forms so I wouldn't be of any help on your other question. =\
ASKER
Hi yes it was a Public in a Module, never mind its going now. Once again thank you for your help.