?
Solved

Update label on progress bar from different thread.

Posted on 2011-10-20
24
Medium Priority
?
475 Views
Last Modified: 2012-05-12
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.
0
Comment
Question by:taz8020
  • 12
  • 11
24 Comments
 
LVL 28

Expert Comment

by:strickdd
ID: 37000181
You may have to add an "Application.DoEvents" line to force the GUI to update.
0
 
LVL 3

Author Comment

by:taz8020
ID: 37000332
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
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 37000361
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
Free learning courses: Active Directory Deep Dive

Get a firm grasp on your IT environment when you learn Active Directory best practices with Veeam! Watch all, or choose any amount, of this three-part webinar series to improve your skills. From the basics to virtualization and backup, we got you covered.

 
LVL 3

Author Comment

by:taz8020
ID: 37000428
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
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 37000469
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
 
LVL 3

Author Comment

by:taz8020
ID: 37000613
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
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 37000631
Would need to see more code then to understand where the breakdown is.  =\
0
 
LVL 3

Author Comment

by:taz8020
ID: 37000708
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
 
LVL 86

Accepted Solution

by:
Mike Tomlinson earned 2000 total points
ID: 37000806
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
 
LVL 3

Author Comment

by:taz8020
ID: 37000915
I never knew there was a e.UserState, that sounds like it. Will try it now.
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 37000935
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
 
LVL 3

Author Comment

by:taz8020
ID: 37001069
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
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 37001099
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
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 37001110
Also, do you have any polling loops running somewhere else in the form code?...or anywhere else?
0
 
LVL 3

Author Comment

by:taz8020
ID: 37001251
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
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 37001286
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
 
LVL 3

Author Comment

by:taz8020
ID: 37001459
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
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 37001555
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
 
LVL 3

Author Comment

by:taz8020
ID: 37001992
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
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 37002016
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
 
LVL 3

Author Comment

by:taz8020
ID: 37005531
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
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 37006015
I'm not sure why being in a different file would cause a problem either.  Was it Public in a Module?
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 37006017
I don't work at all with web-forms so I wouldn't be of any help on your other question.  =\
0
 
LVL 3

Author Comment

by:taz8020
ID: 37010946
Hi yes it was a Public in a Module, never mind its going now. Once again thank you for your help.
0

Featured Post

Upgrade your Question Security!

Add Premium security features to your question to ensure its privacy or anonymity. Learn more about your ability to control Question Security today.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

For those of you who don't follow the news, or just happen to live under rocks, Microsoft Research released a beta SDK (http://www.microsoft.com/en-us/download/details.aspx?id=27876) for the Xbox 360 Kinect. If you don't know what a Kinect is (http:…
A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
In a question here at Experts Exchange (https://www.experts-exchange.com/questions/29062564/Adobe-acrobat-reader-DC.html), a member asked how to create a signature in Adobe Acrobat Reader DC (the free Reader product, not the paid, full Acrobat produ…

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question