ToolStripStatusLabel and threading

InertiaM

Well-known member
Joined
Nov 3, 2007
Messages
663
Location
Kent, UK
Programming Experience
10+
I need to update a ToolStripStatusLabel from a different thread. As the TSSL has no invoke, I tried the following code :-

VB.NET:
    Delegate Sub SetBarcodeInvoker(ByVal Barcode As String)
    Public Sub SetBarcodeText(ByVal Barcode As String)
        If StatusStrip1.InvokeRequired Then
            StatusStrip1.Invoke(New SetBarcodeInvoker(AddressOf SetBarcodeText), Barcode)
        Else
            tsslBarcode.Text = Barcode
        End If
    End Sub

called by

VB.NET:
     Form1.SetBarcodeText("TEST")
     MessageBox.Show("TEST")

The messagebox shows so I know it is being executed, but the TSSL doesnt get updated :confused: Any ideas?
 
It all executes, with no errors. If I put a breakpoint on

VB.NET:
tsslBarcode.Text = Barcode

it stops there, then goes back and displays the messagebox.
 
No, it doesnt gain the value if run from the other thread. If I execute SetBarcodeText from the same thread, everything is fine and it displays correctly.

A ToolStripStatusLabel does not have a Refresh method. The nearest option is to refresh the StatusStrip.
 
I'm using the following to do what your looking to do:

On form with status bar (I can update multiple panels on the status bar, hence the panel integer):
VB.NET:
Delegate Sub SetStatusBarStatusCallback(ByVal [text] As String, ByVal [panel] As Integer)

    Friend Sub SetStatusBarValue(ByVal [text] As String, ByVal [panel] As Integer)
        ' 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.StatusStriponFormMain.InvokeRequired Then
            Dim d As New SetStatusBarStatusCallback(AddressOf SetStatusBarValue)
            Me.Invoke(d, New Object() {[text], [panel]})
        Else
            Me.StatusStriponFormMain.Items([panel]).Text = [text]
            Me.StatusStriponFormMain.Refresh()
        End If
    End Sub

Then from the other forms:
VB.NET:
MainForm.SetStatusBarValue("Doing whatever...", 0)

You of course could get rid of the [panel] part although to update more then one part of the status strip it's helpful. And yes you need to refresh the status bar to display your change.

-Allan
 
Last edited:
Default forms are thread specific, you need to invoke yourself onto the main thread before calling the default form instance. Either that or you need the reference for a specific form instance for which you invoke the method.

You earlier put a breakpoint on "Barcode.Text = " but you never checked if code breaked on "Strip1.Invoke". Robert_Zenz suggested stepping through the code, which would have showed you that Invoke was never hit. Since you know (or should know) invoke is required from the other thread this would be a good indication that you are not handling the correct form instance.

You don't need Refresh.
 
You don't need Refresh.

You certainly do if you want to actually see the values update. If your doing something processor intensive on the sub form your main form will NOT update until your done processing or you do the refresh in the update. I attached a small app to demonstrate this.
 

Attachments

  • Test App - Invoke.zip
    25.8 KB · Views: 29
Thanks to everyone - given all the replies, I've managed to make it work on a test project. All I have to do now is ensure it works on a production line in the morning :)

Allan - you may want to edit your post above from

VB.NET:
Me.Invoke(d, New Object() {[panel], [text]})

to

VB.NET:
Me.Invoke(d, New Object() {[text], [panel]})

in case this thread is useful to anyone else.

Oh, and for reference, it works with and without the Refresh :D
 
Sprint said:
If your doing something processor intensive on the sub form your main form
Don't! Never do long tasks in UI thread, never make a call in UI thread that may block it. Learn about multi-threading, this is what InertiaM is actually asking about in this thread. The loop you do in SubForm in the project you posted I would regard as a programming error. By blocking the UI thread you are also preventing the windows messages from being processed, so the whole app freeze up and no control repaints.
 
Sometimes you don't have much of a choice. I've also run into a issue where even putting something on another thread that had very fast updates (hundred a second?) would not update the other forms text/status fast enough so using the .Refresh got around that.

Use what works in your situation, I've had to use the .Refresh more often then not when sending status across forms. And yes I know the sample app was a bit far from realistic but it served it's purpose.
 
I've also run into a issue where even putting something on another thread that had very fast updates (hundred a second?) would not update the other forms text/status fast enough
I can't see why that would be a problem, at 10ms (1/100 sec) refresh one is barely able to read the numbers changing, faster than that the UI output of text becomes more or less pointless because you can't read it anyway. Still there is no problem I can see that would require a Refresh when changing the controls Text. As mentioned Refresh has the purpose of invalidating and updating the paint for a control, which is used when custom painting controls where repaint is not caused by other change of state, it can not be used as an excuse for blocking UI thread. "because now I see the text.." is not a reason for a cause.
 
Back
Top