Threading & Status Problem in VB.Net 2005

Denaes

Senior Contributor
Joined
Jun 10, 2003
Messages
956
I have an app that is very basic so far. Its current goal is to read a lot of data about files in a particular directory and it's sub directories. This can take a while if it's like the C:\ directory.

So far it's just has:
Toolstrip (has a combo with directories and a button to start the scan)
StatusStrip (with a ToolStripStatusLabel on it)

Pretty basic. As you process each directory I want it to display text indicating which directory is being scanned on the ToolStripStatusLabel.

The First Problem:

Scanning ties up the thread, so it never actually writes any text to the ToolStripStatusLabel.

I solved this problem by adding a BackgroundWorker to run the scan and this works pretty well. The scan runs, text is displayed on the ToolStripStatusLabel as it should. The user will see directories being worked on as the app works through them.

I understand that I'm access the ToolStripStatusLabel from another thread and the application seems fine with it... normally.

The New Problem:

Now if I change the size of the window (which I could see a user doing to see more of the directory names or to make the app smaller) I get an exception:

System.InvalidOperationException: Cross-Thread operation not valid: Control 'AppToolStrip' accessed from a thread it was created on. and it continues to refer me to the line the exception is on, which is where I'm setting the text for the ToolStripStatusLabel.

Now the part that throws me for a loop is that the line is assigning text to the ToolStripStatusLabel (which is on the StatusStrip) while the exception says that there is a cross-thread problem with the ToolStrip control, which there isn't any code manipulating.

I don't get why this only happens when the application is resized at runtime during a scan and not when it's left alone.

Any clues or workarounds?
 
Seems a strange problem! Is the exception being thrown from your code? If so it might be worth checking if InvokeRequired is true and then either Invoking yourself or just exiting the routine anyway...
 
Hmm, I seem to have that solved.

I think part of the problem is that ToolStripStatusLabel doesn't have the InvokeRequired property, so the app was never picking up that I was assigning it cross thread or something and just letting me do it unsafely.

I created a delegate and a Set method that can work cross thread like so:

Visual Basic:
Delegate Sub SetTextCallback(ByVal [text] As String)

    Private Sub SetStatusLabel(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.StatusStrip1.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf SetStatusLabel)
            Me.Invoke(d, New Object() {[text]})
        Else
            Me.ToolStripStatusLabel1.Text = [text]
        End If
    End Sub

This is how Microsoft says to access a Form.Control cross thread, but ToolStripStatusLabel doesn't have an InvokeRequired property, so I used it's parent controls InvokeRequired. I'm not sure if this is the best thing to do, but it seems to be working
 
I believe that you can check the InvokeRequired of the form containing the toolstrip (or any control, for that matter) and it should return an appropriate value.
 
Okay, sounds good. I replaced "StatusStrip1.InvokeRequired" with "Me.InvokeRequired" for the Form.

I guess the exception pointing to the wrong control falls under MS's explaination of unexpected errors when referencing a control on another thread. But so long as I have things working, thats cool :)
 
Back
Top