• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 820
  • Last Modified:

Thread Safe Calls

I have a progress bar set to marquee on an mdi parent status bar. To make it run smoothly and continuously while updating I thought it may be time to try somehting new rather than Application.DoEvents.

So I have a procedure in a child form which is called by the following

           ....enable the progress bar

            Dim newthread As New Threading.Thread(AddressOf MyProcedure)
            newthread.Start()

         
The procedure fills a datatable from various tables, then a DataView which is sorted, which is then used to populate a DataGridView control. I have found out that (via debug message) I have to check the DataGridView.InvokeRequired property when i try to set the DataGridView datasource, and then do something else if the answer is True, when doing something to a control not invoked by the same thread. I am not sure what code I need if the answer is true.

so..

    Private Sub MyProcedure()


        ....do some stuff

         If MyDataGridView.InvokeRequired Then

            ......WHAT GOES HERE

          else

             MyDataGridView.DataSource = MyDataView

        End If

        'Disable progress bar
        me.cursor = cursors.default
        MyMdiParent.ProgressBar.Enabled=false
       

PS. I have also discovered i will get the same problem when i try to reset the cursor for the form back to default and also try to disable the progress bar as they were set on different threads

End Sub

I am assuming of course this is the way to go for smoothly operating progress bars
0
JeffvClayton
Asked:
JeffvClayton
  • 5
  • 3
  • 3
2 Solutions
 
doraiswamyCommented:
Interesting as I was tinkering with a progress bar in a separate thread myself earlier today.
However, I'm not sure what exactly you are looking for. Do you want the progress bar in the background, marquee-ing smoothly? Then that should be in the separate thread.

Something like this:
Dim newthread As New Threading.Thread(AddressOf MyProcedure)
            newthread.Start()

   ' Do whatever you have to do
  '
   newthread.abort()     ' kill it


Private sub MyProcedure()
            Dim p As New ProgressBar
            p.Maximum = 10
            p.Minimum = 1
            Dim waitform As New Form
            waitform.Text = "Doing Something... Wait"
            waitform.MinimizeBox = False
            waitform.MaximizeBox = False
            waitform.Controls.Add(p)
            p.Left = (waitform.Width - p.Width) / 2
            p.Top = (waitform.Height - p.Height) / 2
            p.Visible = True
            waitform.Show()
            While True
                Thread.Sleep(250)   ' will update the progress bar every 250 milli seconds
                If p.Value = p.Maximum Then
                    p.Value = p.Minimum
                Else
                    p.Value += 1
                End If
            End While

        End Sub


0
 
JeffvClaytonAuthor Commented:
Yes i do you want the progress bar in the background, marquee-ing smoothly, but I do not want to create a new form, i have a progress bar in a status strip at the bottom of an Mdi parent which is in the original thread, I want to use that. Does it mean i basically have to add the progress bar to the status strip at runtime? Or can i manage the invokerequired properties as suggested by microsoft?
0
 
doraiswamyCommented:
I'm not sure about the "invokerequired" property. You can try creating a progress bar at runtime and accessing it from the thread.
0
Cloud Class® Course: C++ 11 Fundamentals

This course will introduce you to C++ 11 and teach you about syntax fundamentals.

 
JeffvClaytonAuthor Commented:
OK, that is something to try.

I have also tried this as the last line in the procedure with no error messages, but no data in the DataGridView either!

 MyDataGridView.Invoke(MyDataGridView.DataSource, MyDataView.ToTable)
0
 
doraiswamyCommented:
Sorry, in the earlier post I meant create a progress bar at design time, not runtime
0
 
DjDezmondCommented:
You need to supply a delegate sub, kind of like a template that your code passes through. You then invoke through that delegate. If you have parameters that you need to pass, the delegate sub contains those same params. The delegate sub is only one line...

----------------------------------------------
Delegate Sub InvokeToControl(AnyParam1 as Type, AnyParam2 as Type)
----------------------------------------------

Then specify the sub routine that holds your code...

----------------------------------------------
Sub YourSub(AnyParam1 as Type, AnyParam2 as Type)
'Do whatever needs doing
End sub
----------------------------------------------

Now...

----------------------------------------------
If MyDataGridView.InvokeRequired Then
MyDataGridView.Invoke(New InvokeToControl(AddressOf YourSub), New Object() {AnyParam1, AnyParam2})
Else
YourSub(AnyParam1, AnyParam2)
----------------------------------------------

In this example i have specified 2 paramaters, obviously adjust as you need to, but if you have none, then the invoke line just becomes:

MyDataGridView.Invoke(New InvokeToControl(AddressOf YourSub))

Hope this helps

Dez

0
 
JeffvClaytonAuthor Commented:
I have solved the problem, you cant change an object from a different thread, you need all this....


Instead of   'MyDataGridView.DataSource = MyDataView' as the last line of the procedure in the new thread

                              you need a new procedure call as the last line e.g.   PassNewDataView(MyDataView.ToTable)

                                                                                                         End Sub
                               'Another sub which runs in the new thread
                             '--------------------------------------------------
                                Sub PassNewDataView(ByVal MyDataView As DataTable)
                                          Me.BeginInvoke(New DelegateUpdateSource(AddressOf Me.ChangeSource), New Object() {MyDataView})
                               End Sub

                              ' then a new Delegate Sub as this....
                               ' ----------------------------------------
                               Public Delegate Sub DelegateUpdateSource(ByVal DV As DataTable)


  'Finally a sub which runs in the original thread to change the datasource and stop the progress bar                                  
   '-----------------------------------------------------------------------------------------------------------------

    Sub ChangeSource(ByVal DV As DataTable)

        Me.MyDataGridView.DataSource = DV

        Me.Cursor = Cursors.Default
         MParent.PB1.Enabled = False
     

    End Sub

Thanks anyway

Jeff
0
 
JeffvClaytonAuthor Commented:
Thanks Dez, I was typing as you sent the message.
0
 
DjDezmondCommented:
I would of typed it a hell of a lot sooner (just as you posted the msg) but i was on my phone the whole time! Sorry mate!

Glad its working now anyway.

Cheers,

Dez
0
 
DjDezmondCommented:
one more thing...

                               Sub PassNewDataView(ByVal MyDataView As DataTable)
                                          Me.BeginInvoke(New DelegateUpdateSource(AddressOf Me.ChangeSource), New Object() {MyDataView})
                               End Sub

There is no need for a seperate sub here... you can just call:

Me.BeginInvoke(New DelegateUpdateSource(AddressOf Me.ChangeSource), New Object() {MyDataView})

... from anywhere on the thread

Cheers,
Dez
0
 
JeffvClaytonAuthor Commented:
Even Better!

Thanks Dez.
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.

Join & Write a Comment

Featured Post

The 14th Annual Expert Award Winners

The results are in! Meet the top members of our 2017 Expert Awards. Congratulations to all who qualified!

  • 5
  • 3
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now