Results 1 to 11 of 11

Thread: [RESOLVED] Threading & UI

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Feb 2004
    Location
    India
    Posts
    526

    Resolved [RESOLVED] Threading & UI

    Hi Guys,

    I have written an application in vb.net 2008 (using .net 3.5) which performs several tasks. Now there is a form which does some calculation on data and displays results. While the processing is done I want to display progress and so I picked an animated gif which should be visible & animate while work is going on. After work is done it should be gone.

    Now before posting, I did read about threading and went thru jmcilhinney's post where he explains how threading works. It was great as I did get a hang.

    But unfortuntely inspite of using threading my purpose is not being fulfilled. While the processing is going on my form becomes unresponsive and I feel as if threading is not doing its job properly. Moreover if I need to manage several UI elements (like groupbox,textbox,picturebox etc) then its a pain to do that while in a thread.

    My code is as follows:

    Code:
    Dim threadstart as thread
    
    Private Sub cmdprocess_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdprocess.Click
        
                KGroup2.Visible = True    ' this groupbox has picturebox (ani gif)
                GroupBox1.Enabled = False
                cmdfilter.Enabled = False
                cmdprocess.Enabled = False
                Cursor = Cursors.WaitCursor
    
                ' Show Progress & Start Thread
                ThreadStart = New Thread(AddressOf StartProcess)
    
                ThreadStart.Start()
    
    End Sub
    Code:
    Private Sub StartProcess()
            Try
    ' I have to write so much code so that I can work on UI Items
    
                If GroupBox1.InvokeRequired = True Then
                    Me.GroupBox1.Invoke(New MethodInvoker(AddressOf StartProcess))
                ElseIf KGroup2.InvokeRequired = True Then
                    Me.KGroup2.Invoke(New MethodInvoker(AddressOf StartProcess))
                ElseIf cmdprocess.InvokeRequired = True Then
                    Me.cmdprocess.Invoke(New MethodInvoker(AddressOf StartProcess))
                ElseIf cmdfilter.InvokeRequired = True Then
                    Me.cmdfilter.Invoke(New MethodInvoker(AddressOf StartProcess))
                ElseIf PictureBox1.InvokeRequired = True Then
                    Me.PictureBox1.Invoke(New MethodInvoker(AddressOf StartProcess))
                Else
    
                    PictureBox1.Refresh() ' this has ani gif which should animate
    
                    MResult = ProcessScan(MScan)  ' THIS IS SUB FOR CALCULATION
    
                    Cursor = Cursors.Arrow
                    GroupBox1.Enabled = True
                    KGroup2.Visible = False
                    cmdprocess.Enabled = True
                    cmdfilter.Enabled = True
                    Cursor = Cursors.Arrow
    
                    Call PopulateResult()  ' Populate Result Sub
    
                End If
    
            Catch ex As Exception
                
            End Try
    
        End Sub
    Now inspite of all this running in a thread, program is not responsive. What should I do? I find managing UI items (if they are in decent numbers a pain as we have to write invoke method for each one of them).

    Will be glad if anyone can help me out.

    Thanks a lot,

    Cheers,
    GR

  2. #2
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Threading & UI

    First up, you can get rid of all those ElseIf statements. All your controls are owned by the same thread so if InvokeRequired is True for any of them then it will be True for all of them.

    As for your issue, the problem is that you aren't actually doing anything on the secondary thread. You are creating a new Thread and starting it at the StartProcess method, then the very first thing you do in the StartProcess method is marshal a call back to the UI thread and then you do all your work there.

    The idea of using InvokeRequired and Invoke is that you ONLY marshal a call back to the UI thread to actually update the UI. You should get anything that updates the UI out of that method. All the background work goes into one method and then the UI updated code goes into another, e.g.
    vb.net Code:
    1. Private Sub StartProcess()
    2.     MResult = ProcessScan(MScan)
    3.  
    4.     UpdateUI()
    5. End Sub
    6.  
    7. Private Sub UpdateUI()
    8.     If Me.InvokeRequired Then
    9.         Me.Invoke(AddressOf UpdateUI)
    10.     Else
    11.         'Update UI here.
    12.     End If
    13. End Sub
    So, as you can see, all the background work is done in StartProcess, which is executed on the secondary thread. When that processing is done the call is made to UpdateUI to update the UI. It's there that the invocation is made if required. The method that performs the invocation contains NOTHING other than the If...Else block. The If block contains NOTHING other than the invocation and the Else block contains NOTHING but the code that updates the UI.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  3. #3

    Thread Starter
    Fanatic Member
    Join Date
    Feb 2004
    Location
    India
    Posts
    526

    Wink Re: Threading & UI

    Thanks a ton buddy. I wrote code whereby UI is updated on a different sub. Now it seems better.

    Code:
    Private Sub cmdprocess_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdprocess.Click
                    
                KGroup2.Visible = True ' Pic box with animated gif
                GroupBox1.Enabled = False
                cmdfilter.Enabled = False
                cmdprocess.Enabled = False
                Cursor = Cursors.WaitCursor
    
                ' Show Progress
                ThreadStart = New Thread(AddressOf StartProcess)
                ' RUN / Call Process
                ThreadStart.Start()
    
        End Sub
    Code:
     Private Sub StartProcess()
            Try
    
                MResult = ProcessScan(MScan) '  Does Main Processing
    
                Call EnableUI()      ' seperate sub to enable form's UI controls
                Call PopulateResult() ' populate result
    
            Catch ex As Exception
                frmmsgbox.Message("Error - " & ex.Message, "Error", "E")
                frmmsgbox.ShowDialog()
                frmmsgbox.Dispose()
            End Try
    
        End Sub
    Code:
     Private Sub EnableUI()
            ' enable controls after scan is complete
            If Me.InvokeRequired = True Then
                Me.GroupBox1.Invoke(New MethodInvoker(AddressOf EnableUI))
                Me.KGroup2.Invoke(New MethodInvoker(AddressOf EnableUI))
                Me.cmdprocess.Invoke(New MethodInvoker(AddressOf EnableUI))
                Me.cmdfilter.Invoke(New MethodInvoker(AddressOf EnableUI))
            Else
                GroupBox1.Enabled = True
                KGroup2.Visible = False
                cmdprocess.Enabled = True
                cmdfilter.Enabled = True
                Cursor = Cursors.Arrow
            End If
        End Sub
    When does the thread close or in other words is completed. I mean once the thread is out of procedure startprocess (e.g) i think it would become idle until activated again.

    Moreover if we are able to use the command -
    "Me.GroupBox1.Invoke(New MethodInvoker(AddressOf EnableUI))"

    Then why we need to declare deligate. Is this because our sub does not ask or return value so this works.

    I really appreciate your help dude.

  4. #4
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Threading & UI

    The thread you created terminates when the method you specify as its entry point completes. If you specify StartProcess as the entry point for your thread then your thread terminates when StartProcess completes.

    The delegate you use to invoke a method must have the same signature as the method you're invoking. If the method has no parameters and doesn't return a value then you can invoke it with the MethodInvoker delegate because it has the same signature. If your method has a different signature then you need to use a different delegate. In that case you can declare your own delegate or you can use an existing delegate from the Framework. For methods that don't return a value you can use the Action delegate, which supports 0 to 4 generic parameters. For methods that do return a value you can use the Func delegate, which also supports 0 to 4 generic parameters.

    You should change EnableUI. You don't have to call Invoke on every control you want to update. The Invoek method has nothing specifically to do with the control its called on except that the method call will be marshalled to the thread that owns that control. Your EnableUI method updates four controls, right? Do you really need to update those four controls four times each, because that's what you're doing? You call Invoke on the GroupBox, which calls EnableUI and enables all the controls. You then call Invoke on the second GroupBox and enable all the controls again. Etc., etc. Look at ALL my code examples on this subject. I ALWAYS call Invoke once and once only on the exact same control that I test InvokeRequired on. That's what you should do. Unless you have a specific reason to do otherwise, there's really no point using anything other than the form for that.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  5. #5

    Thread Starter
    Fanatic Member
    Join Date
    Feb 2004
    Location
    India
    Posts
    526

    Lightbulb Re: Threading & UI

    Quote Originally Posted by jmcilhinney View Post
    The thread you created terminates when the method you specify as its entry point completes. If you specify StartProcess as the entry point for your thread then your thread terminates when StartProcess completes.

    The delegate you use to invoke a method must have the same signature as the method you're invoking. If the method has no parameters and doesn't return a value then you can invoke it with the MethodInvoker delegate because it has the same signature. If your method has a different signature then you need to use a different delegate. In that case you can declare your own delegate or you can use an existing delegate from the Framework. For methods that don't return a value you can use the Action delegate, which supports 0 to 4 generic parameters. For methods that do return a value you can use the Func delegate, which also supports 0 to 4 generic parameters.

    You should change EnableUI. You don't have to call Invoke on every control you want to update. The Invoek method has nothing specifically to do with the control its called on except that the method call will be marshalled to the thread that owns that control. Your EnableUI method updates four controls, right? Do you really need to update those four controls four times each, because that's what you're doing? You call Invoke on the GroupBox, which calls EnableUI and enables all the controls. You then call Invoke on the second GroupBox and enable all the controls again. Etc., etc. Look at ALL my code examples on this subject. I ALWAYS call Invoke once and once only on the exact same control that I test InvokeRequired on. That's what you should do. Unless you have a specific reason to do otherwise, there's really no point using anything other than the form for that.
    You said that my method EnableUI is not approp as it updates all four controls four times. Thats true, it sure does call four times and I want it to be called only once. But again how should I do. You said not to use If statement for each control (like you saw in my first code whereby i have if x control.invoke then do this and if y control . invoke then do ..etc). How can I enable all four controls in one shot ? Can you help me with an example.
    Thanks buddy.

  6. #6
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Threading & UI

    You're already enabling all four controls in one shot. You're just doing it four times.

    I already told you exactly what to do in my last post:
    I ... call Invoke once and once only on the exact same control that I test InvokeRequired on. ... there's really no point using anything other than the form for that.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  7. #7

    Thread Starter
    Fanatic Member
    Join Date
    Feb 2004
    Location
    India
    Posts
    526

    Post Re: Threading & UI

    Quote Originally Posted by jmcilhinney View Post
    You're already enabling all four controls in one shot. You're just doing it four times.

    I already told you exactly what to do in my last post:
    Though i did what you said. now my code is :

    Code:
      Private Sub EnableUI()
            ' enable controls after scan is complete that many times
            If Me.InvokeRequired = True Then
                Me.GroupBox1.Invoke(New MethodInvoker(AddressOf EnableUI))
                Else
                GroupBox1.Enabled = True
                KGroup2.Visible = False
                cmdprocess.Enabled = True
                cmdfilter.Enabled = True
                Cursor = Cursors.Arrow
            End If
        End Sub
    So now this sub is being called only once. But what puzzles me is only calling one control using invoke, allows me to access all controls without any explicit declaration. So if there were 20 controls I needed to modify, i only needed to call one of them. Is that right.? Strange I guess.

  8. #8
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Threading & UI

    No, not strange at all. As I explained in a previous post, the purpose of the Invoke method is NOT to give you access to that control specifically. The purpose of the Invoke method is to marshal a method call to the thread that owns that control. Once the method is called on that thread then it can do whatever it wants. It can access every control owned by that thread.

    Consider this. You are in a magic house with two rooms and no doors or windows in either room. There are 10 pieces of chocolate in one of the rooms and you're in the other room, so you can't eat any of the chocolate. Now, there also exists a magic teleporter for each piece of chocolate that will instantly take the person who uses it to the room that contains that piece of chocolate. Luckily for you, all 10 of those teleporters are in the same room you're in. Now, you pick up one of those teleporters and use it and you are instantly transported to the room containing the corresponding piece of chocolate. What are you going to do? Are going to eat just that piece of chocolate and then go back to the other room and use the other 9 teleporters, or are you just going to eat all 10 pieces of chocolate?

    The rooms are the threads, the pieces of chocolate are the controls and the teleporters are the Invoke methods. Once you invoke a method on the UI thread you are on the UI thread so you have access to every UI element. It doesn't matter which control's Invoke method you used to get there, as long as you're there.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  9. #9

    Thread Starter
    Fanatic Member
    Join Date
    Feb 2004
    Location
    India
    Posts
    526

    Thumbs up Re: Threading & UI

    Quote Originally Posted by jmcilhinney View Post
    No, not strange at all. As I explained in a previous post, the purpose of the Invoke method is NOT to give you access to that control specifically. The purpose of the Invoke method is to marshal a method call to the thread that owns that control. Once the method is called on that thread then it can do whatever it wants. It can access every control owned by that thread.

    Consider this. You are in a magic house with two rooms and no doors or windows in either room. There are 10 pieces of chocolate in one of the rooms and you're in the other room, so you can't eat any of the chocolate. Now, there also exists a magic teleporter for each piece of chocolate that will instantly take the person who uses it to the room that contains that piece of chocolate. Luckily for you, all 10 of those teleporters are in the same room you're in. Now, you pick up one of those teleporters and use it and you are instantly transported to the room containing the corresponding piece of chocolate. What are you going to do? Are going to eat just that piece of chocolate and then go back to the other room and use the other 9 teleporters, or are you just going to eat all 10 pieces of chocolate?

    The rooms are the threads, the pieces of chocolate are the controls and the teleporters are the Invoke methods. Once you invoke a method on the UI thread you are on the UI thread so you have access to every UI element. It doesn't matter which control's Invoke method you used to get there, as long as you're there.

    Hats off to you dude. Your explanation is amazing.
    Thanks a lot buddy, I really appreciate the way you help us.

    Before I mark the thread as solved, I have a small query and would be glad if you can advice me a bit.
    I am a fairy decent size application with several forms, modules etc. Now the job of one of the form is to download some file at regular intervals and write the contents of that file to hard drive (update existing ones). And another form's job is to show some part of that files. And finally third form's job is to show a large part of the file that has been written. Now all this takes place without threading. For eg. the following will explain the flow -

    1. Form1 - Downloads file, reads/writes contents to existing files and updates them. Then notifies Form 2 & Form 3.
    2. Form 2 - When it gets notified it shows some part of that data.
    3. Form 3 - When it gets notified it shows a large part of that data (after doing some processing).

    Now when these events/process take place the program becomes less responsive as most things are happening sequentially. So how can I use threading to improve performance/UI responsiveness. I think threads can raise events and can also synchronise (which maybe complex). But to a basic level I can achieve what I want thru threading right. any advice.

    Thanks.

  10. #10
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Threading & UI

    As a general response to your last question, yes you may well be able to improve performance using multiple threads. You're going beyond the scope of the topic of this thread though. If you have a new question you should start a new forum thread.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  11. #11

    Thread Starter
    Fanatic Member
    Join Date
    Feb 2004
    Location
    India
    Posts
    526

    Re: [RESOLVED] Threading & UI

    Thanks a lot for your help.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width