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.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf SetText)
            MainForm.StatusStrip1.Invoke(d, New Object() {UpdateText})
        Else
            MainForm.ToolStripStatusUpdateProgress.Text = UpdateText
End If


So then I tried MainForm.ToolStripStatusUpdateProgress.Text = UpdateText
which does not throw error but does not update text.

Please help.
LVL 3
taz8020Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

 
strickddCommented:
You may have to add an "Application.DoEvents" line to force the GUI to update.
0
 
taz8020Author Commented:
Hi strickdd, no just tried this

MainForm.ToolStripStatusUpdateProgress.Text = UpdateText
Application.DoEvents()

Does not work either, dont get error but does not update either. Any other ideas?
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
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)...
0
Cloud Class® Course: Microsoft Office 2010

This course will introduce you to the interfaces and features of Microsoft Office 2010 Word, Excel, PowerPoint, Outlook, and Access. You will learn about the features that are shared between all products in the Office suite, as well as the new features that are product specific.

 
taz8020Author Commented:
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.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf SetText)
            MainForm.textbox1.Invoke(d, New Object() {"Test"})
        Else
            MainForm.textbox1.Text = UpdateText
End If

Any Ideas
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
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:
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

Open in new window

0
 
taz8020Author Commented:
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.ToolStripStatusUpdateProgress.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.ToolStripStatusUpdateProgress.Text = UpdateText
    End If

End Sub

0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Would need to see more code then to understand where the breakdown is.  =\
0
 
taz8020Author Commented:
Ok here is the code used

Private Sub BackgroundWorkerUpdateWinHost_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorkerUpdateWinHost.DoWork
        SyncWinHost()
End Sub

Public Sub SyncWinHost()
  ' Insert  

   UpdateProductsToWinHost(LinkedServerName, LinkedServerDataBaseName,
   LocalServerDataBaseName)

   'Delete

End Sub

Public Function UpdateProductsToWinHost(ByVal 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.ToolStripStatusUpdateProgress.Text = UpdateText

        SQLQuery = "UPDATE Top(" & QtyToUpdatePerSQLQuery & ") WebServerProducts " & _
    "SET " & _
"WebServerProducts.ShortDescription = LocalServerProducts.ShortDescription, " & _
"WebServerProducts.FullDescription = LocalServerProducts.FullDescription, " & _
"WebServerProducts.ItemWeight = LocalServerProducts.ItemWeight, " & _
"WebServerProducts.PriceFromOnly = LocalServerProducts.PriceFromOnly, " & _
"WebServerProducts.SearchTerm1 = LocalServerProducts.SearchTerm1, " & _
"WebServerProducts.SearchTerm2 = LocalServerProducts.SearchTerm2, " & _
"WebServerProducts.SearchTerm3 = LocalServerProducts.SearchTerm3, " & _
"WebServerProducts.SearchTerm4 = LocalServerProducts.SearchTerm4, " & _
"WebServerProducts.SearchTerm5 = LocalServerProducts.SearchTerm5 ," & _
"WebServerProducts.SearchTerm6 = LocalServerProducts.SearchTerm6, " & _
"WebServerProducts.WebSection = LocalServerProducts.WebSection, " & _
"WebServerProducts.LastUpDate = LocalServerProducts.LastUpDate " & _
"FROM [" & LocalServerDataBaseName & "].[dbo].[Products] as LocalServerProducts, [" & LinkedServerName & "]." & LinkedServerDataBaseName & ".dbo.Products as WebServerProducts " & _
"WHERE (WebServerProducts.ProductRef = LocalServerProducts.ProductRef) And (WebServerProducts.LastUpDate Is Null)"
        '"WHERE (WebServerProducts.ProductRef = LocalServerProducts.ProductRef) And (WebServerProducts.LastUpDate < LocalServerProducts.LastUpDate)"
Redo:
        StartTime = Date.Now
 
        SQLResult = RunSQLquery(SQLQuery, "")
        TotalCount += SQLResult
        ' ProductListOpen.LabelRowsSelected.Text = String.Format("{0} Products Updated So Far", TotalCount)
        TimeTakenInSecs = DateDiff(DateInterval.Second, StartTime, Date.Now)

        UpdateText = String.Format("{0} Products Updated On Website, Last {1} updates took {2} Seconds", TotalCount.ToString, QtyToUpdatePerSQLQuery, TimeTakenInSecs)
     
     If MainForm.StatusStrip1.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf SetText)
            MainForm.StatusStrip1.Invoke(d, New Object() {UpdateText})
        Else
            MainForm.ToolStripStatusUpdateProgress.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.ToolStripStatusUpdateProgress.Text = [text]
    End Sub
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
First set WorkerReportsProgress to True for your BackgroundWorker:

    Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        BackgroundWorkerUpdateWinHost.WorkerReportsProgress = True
    End Sub

Next, use the ReportProgress() method whenever you want to update:

        UpdateText = "Product Update Started..."
        BackgroundWorkerUpdateWinHost.ReportProgress(-1, UpdateText)

This will cause the ProgressChanged() event to fire:

    Private Sub BackgroundWorkerUpdateWinHost_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorkerUpdateWinHost.ProgressChanged
        Me.ToolStripStatusUpdateProgress.Text = e.UserState
    End Sub

Put all together, it should look something like:
Public Class MainForm

    Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        BackgroundWorkerUpdateWinHost.WorkerReportsProgress = True
    End Sub

    Private Sub BackgroundWorkerUpdateWinHost_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorkerUpdateWinHost.DoWork
        SyncWinHost()
    End Sub

    Public Sub SyncWinHost()
        ' Insert  

        UpdateProductsToWinHost(LinkedServerName, LinkedServerDataBaseName, LocalServerDataBaseName)

        'Delete
    End Sub

    Public Function UpdateProductsToWinHost(ByVal 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..."
        BackgroundWorkerUpdateWinHost.ReportProgress(-1, UpdateText)

        SQLQuery = "UPDATE Top(" & QtyToUpdatePerSQLQuery & ") WebServerProducts " & _
                "SET " & _
            "WebServerProducts.ShortDescription = LocalServerProducts.ShortDescription, " & _
            "WebServerProducts.FullDescription = LocalServerProducts.FullDescription, " & _
            "WebServerProducts.ItemWeight = LocalServerProducts.ItemWeight, " & _
            "WebServerProducts.PriceFromOnly = LocalServerProducts.PriceFromOnly, " & _
            "WebServerProducts.SearchTerm1 = LocalServerProducts.SearchTerm1, " & _
            "WebServerProducts.SearchTerm2 = LocalServerProducts.SearchTerm2, " & _
            "WebServerProducts.SearchTerm3 = LocalServerProducts.SearchTerm3, " & _
            "WebServerProducts.SearchTerm4 = LocalServerProducts.SearchTerm4, " & _
            "WebServerProducts.SearchTerm5 = LocalServerProducts.SearchTerm5 ," & _
            "WebServerProducts.SearchTerm6 = LocalServerProducts.SearchTerm6, " & _
            "WebServerProducts.WebSection = LocalServerProducts.WebSection, " & _
            "WebServerProducts.LastUpDate = LocalServerProducts.LastUpDate " & _
            "FROM [" & LocalServerDataBaseName & "].[dbo].[Products] as LocalServerProducts, [" & LinkedServerName & "]." & LinkedServerDataBaseName & ".dbo.Products as WebServerProducts " & _
            "WHERE (WebServerProducts.ProductRef = LocalServerProducts.ProductRef) And (WebServerProducts.LastUpDate Is Null)"
        '"WHERE (WebServerProducts.ProductRef = LocalServerProducts.ProductRef) And (WebServerProducts.LastUpDate < LocalServerProducts.LastUpDate)"
Redo:
        StartTime = Date.Now

        SQLResult = RunSQLquery(SQLQuery, "")
        TotalCount += SQLResult
        ' ProductListOpen.LabelRowsSelected.Text = String.Format("{0} Products Updated So Far", TotalCount)
        TimeTakenInSecs = DateDiff(DateInterval.Second, StartTime, Date.Now)

        UpdateText = String.Format("{0} Products Updated On Website, Last {1} updates took {2} Seconds", TotalCount.ToString, QtyToUpdatePerSQLQuery, TimeTakenInSecs)
        BackgroundWorkerUpdateWinHost.ReportProgress(-1, UpdateText)

        If SQLResult > 0 Then
            GoTo Redo
        End If
        Return TotalCount.ToString
    End Function

    Private Sub BackgroundWorkerUpdateWinHost_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorkerUpdateWinHost.ProgressChanged
        Me.ToolStripStatusUpdateProgress.Text = e.UserState
    End Sub

End Class

Open in new window

0

Experts Exchange Solution brought to you by ConnectWise

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
 
taz8020Author Commented:
I never knew there was a e.UserState, that sounds like it. Will try it now.
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
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.  =)
0
 
taz8020Author Commented:
No, it still does not work, what you said makes perfect sence but does not work.

I put a break point in the BackgroundWorkerUpdateWinHost_ProgressChanged 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 BackgroundWorkerUpdateWinHost_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorkerUpdateWinHost.ProgressChanged
        Me.ToolStripStatusUpdateProgress.Text = 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 SyncWinHostToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SyncWinHostToolStripMenuItem.Click
        'todo if over 1 hour sync but check last local update first
        If BackgroundWorkerUpdateWinHost.IsBusy = False Then
            BackgroundWorkerUpdateWinHost.RunWorkerAsync()
Me.ToolStripStatusUpdateProgress.Text = "TEST"
        Else
            MsgBox("Process is already updating")
        End If
    End Sub
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
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()?

0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Also, do you have any polling loops running somewhere else in the form code?...or anywhere else?
0
 
taz8020Author Commented:
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.DataBaseConnectionString
        End If
        Dim myConnection As New SqlConnection(ConnectionString)

        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
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
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?
0
 
taz8020Author Commented:
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 BackgroundWorkerUpdateWinHost_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorkerUpdateWinHost.ProgressChanged
        Me.ToolStripStatusUpdateProgress.Text = e.UserState
        If ProductListOpen.LabelRowsSelected.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf SetText)
            ProductListOpen.LabelRowsSelected.Invoke(d, New Object() {e.UserState})
        Else
            ProductListOpen.LabelRowsSelected.Text = e.UserState
        End If
    End Sub
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
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?

0
 
taz8020Author Commented:
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.LabelRowsSelected.Text = 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.
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
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.
0
 
taz8020Author Commented:
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.
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
I'm not sure why being in a different file would cause a problem either.  Was it Public in a Module?
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
I don't work at all with web-forms so I wouldn't be of any help on your other question.  =\
0
 
taz8020Author Commented:
Hi yes it was a Public in a Module, never mind its going now. Once again thank you for your help.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.