Results 1 to 21 of 21

Thread: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. Alternative?

  1. #1

    Thread Starter
    Hyperactive Member
    Join Date
    Aug 2014
    Posts
    313

    Resolved [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. Alternative?

    I want to have a countdown on the screen.
    I used to do something like this:
    Code:
    For x = 3 To 1 Step -1
    
                lblMsg.Text = x
    
                'Delay
                For y = 1 To 40000
                    doevents()
                Next y
    
            Next x
    
      lblMsg.text = "go!"
    without the delay, the "countdown" is instantaneous.
    I need to inject a delay to simulate "3.....2.....1.....Go"

  2. #2
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,715

    Re: I used to use Huge Loop & DoEvents() to create an artificial delay. Alternative?

    Do NOT use DoEvents() in this situation!

    There are a few alternatives for you:
    1. Use a timer at a set interval and set the text of your label in the Tick event
    2. Use the Threading.Thread.Sleep() method to create a short "pause" in your code


    Because you're doing a simple countdown then I'd suggest option 1, using the Threading.Thread.Sleep() method causes the UI thread to become unresponsive during it's sleeping period which makes the program look as if it's frozen. Here is an example of using a countdown timer:
    Code:
    Public Class Form1
        Private countdown As Integer
        Private lblMsg As Label
        Private tmr As System.Windows.Timer
    
        Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
            'Globals
            countdown = 3
    
            'Controls
            lblMsg = New Label With {.AutoSize = True, .Location = New Point(5, 5), .Text = countdown.ToString}
            tmr = New System.Windows.Timer With {.Interval = 1000} '1 second timer
    
            'Events
            AddHandler tmr.Tick AddressOf tmr_Tick() 'Add the tick event
    
            'We don't want an empty form
            Me.Controls.Add(lblMsg)
    
            'Go go go!
            tmr.Start()
        End Sub
    
        Private Sub tmr_Tick(ByVal sender As object, ByVal e As EventArgs)
            countdown -= 1
            If countdown = 0 Then tmr.Stop()
            lblMsg.Text = countdown.ToString()
        End Sub
    
    End Class
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: I used to use Huge Loop & DoEvents() to create an artificial delay. Alternative?

    Spinning around DoEvents is called a busy wait, which is a terribly bad thing to do for a variety of reasons. While the busy wait is looping, the CPU is maxed out, which noticeably increases power consumption by the computer, and subsequently increases heat output, while also slowing down all other processes on the system. That's why it's a bad thing to do.
    My usual boring signature: Nothing

  4. #4

    Thread Starter
    Hyperactive Member
    Join Date
    Aug 2014
    Posts
    313

    Re: I used to use Huge Loop & DoEvents() to create an artificial delay. Alternative?

    dday9, minor point, but I think your code won't display the "3" ....

    Here is how I coded it up with the timer!

    Code:
            If count = 0 Then
                lblMsg.Text = "Go!"
                tmrCountdown.Enabled = False
            Else
                lblMsg.Text = count
                count = count - 1
            End If

  5. #5

    Thread Starter
    Hyperactive Member
    Join Date
    Aug 2014
    Posts
    313

    Re: I used to use Huge Loop & DoEvents() to create an artificial delay. Alternative?

    PS: It's interesting to see dday9 do his entire UI in run-time.
    I guess this is how .NET has really become a true OOP lang like Java, C++, etc.

  6. #6
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: I used to use Huge Loop & DoEvents() to create an artificial delay. Alternative?

    He does that simply so that the code can be copied and pasted without needing to tell the OP, "First, put these components on a form then name them like so." Normally, the only reason to build the interface dynamically is if you need to add the controls dynamically, which is rare, but DDay likes to provide examples that contain both the controls he is using AND the code to manipulate them.

    Having said that, there's nothing special about the designer. Every form has a .designer.vb file associated with it. Unless you have created a custom constructor, this file will have the contructor in it (Sub New), and the definition of the InitializeComponents() method, which has to be the first call in any form constructor. If you look at InitializeComponents(), you will see all the controls being constructed and their properties being set. That's all the designer does: Manipulates the code in that function. The controls are just objects like any other, and they are created like any other object. Perhaps the only oddity to them is that they are all added to a .Controls collection for them to be visible, but they are just objects the same as every other object. Generally, you wouldn't want to mess around with the .designer.vb file, because if you do the wrong thing, the designer will cease to work, though you can recover it by fixing your mistake...as long as you can identify your mistake.

    Something like that was done in VB6, except that the controls were all part of a file that was effectively written in a different language that was interpreted. Those files weren't XML, but in concept, it was similar to XAML in that the file described what was needed in a language unlike the language the rest of the code was written in. The real difference with .NET is that the controls, their layout, and the form they are on, are all written in the same language as the rest of the code, which makes them much easier to tinker with, since you only need to know one language.
    My usual boring signature: Nothing

  7. #7
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,398

    Re: I used to use Huge Loop & DoEvents() to create an artificial delay. Alternative?

    Quote Originally Posted by RipVoidWinkle View Post
    PS: It's interesting to see dday9 do his entire UI in run-time.
    I guess this is how .NET has really become a true OOP lang like Java, C++, etc.
    He would not normally.

  8. #8
    PowerPoster ThEiMp's Avatar
    Join Date
    Dec 2007
    Location
    Take The PCI Bus Across To The CPU!!
    Posts
    3,899

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    DoEvents only makes the program then goto the next lines after a certain section of source code has been processed by the IDE, while in RunTime or even CompileTime. So then that wouldn't work. I guess that you are looking for the main idea of something holding back the lines of source code being processed by the program, while in either RunTime or even CompileTime. So then the best bet for this problem is to have the Timer on a Interval loop, counting down the milliseconds in the Timer's presetup Interval property that has been setup in DesignTime, however you are also able to setup the values in the property, also at RunTime and CompileTime, however it just makes more sense to do that if you can in DesignTime, it just makes a lot less programming to do, while the computer is processing the program source coded lines...
    I have a huge free products range, of computer software in which you can download using any kind of 64-Bit Web Browser. Also there is coming a Social Networking section that I am making on my Website...

    |Ambra Productions Inc. | The Black Sun Society | The Black Shield | Ambra College | Church of the Black Sun | Ambra Productions Inc's Homepage | Boomtick Event's Venues: Ambar Nightclub, Jack Rabbit Slim's, Villa Nightclub and Lucy's Bar | Pasta Ambra | Fish Feast Company | Wallet Wizard | Ambrose Liquor | Ambar Tavern | Ambra University |

    Do you wish to do unpaid work for me??? If so, the PM me on this Forum, and then we can get to work, programming for the future of computers go by the name of ThEiMp. This is my ghost writers name. Also my nickname, means that I am: The Imperial of the Technology Industry, so then to make it really short, I just then wrote: The Imp, which is where I get the nickname from...

  9. #9
    PowerPoster ThEiMp's Avatar
    Join Date
    Dec 2007
    Location
    Take The PCI Bus Across To The CPU!!
    Posts
    3,899

    Re: I used to use Huge Loop & DoEvents() to create an artificial delay. Alternative?

    Quote Originally Posted by dday9 View Post
    Do NOT use DoEvents() in this situation!

    There are a few alternatives for you:
    1. Use a timer at a set interval and set the text of your label in the Tick event
    2. Use the Threading.Thread.Sleep() method to create a short "pause" in your code


    Because you're doing a simple countdown then I'd suggest option 1, using the Threading.Thread.Sleep() method causes the UI thread to become unresponsive during it's sleeping period which makes the program look as if it's frozen. Here is an example of using a countdown timer:
    So sorry I didn't see your post when I had sent mine, in...
    I have a huge free products range, of computer software in which you can download using any kind of 64-Bit Web Browser. Also there is coming a Social Networking section that I am making on my Website...

    |Ambra Productions Inc. | The Black Sun Society | The Black Shield | Ambra College | Church of the Black Sun | Ambra Productions Inc's Homepage | Boomtick Event's Venues: Ambar Nightclub, Jack Rabbit Slim's, Villa Nightclub and Lucy's Bar | Pasta Ambra | Fish Feast Company | Wallet Wizard | Ambrose Liquor | Ambar Tavern | Ambra University |

    Do you wish to do unpaid work for me??? If so, the PM me on this Forum, and then we can get to work, programming for the future of computers go by the name of ThEiMp. This is my ghost writers name. Also my nickname, means that I am: The Imperial of the Technology Industry, so then to make it really short, I just then wrote: The Imp, which is where I get the nickname from...

  10. #10

    Thread Starter
    Hyperactive Member
    Join Date
    Aug 2014
    Posts
    313

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    I just ran into another situation where I need a short pause...
    I don't think I can use a timer, b/c this is not like a countdown.
    There is no action to be taken except a short pause.

    How would I use Threading.Thread.Sleep() ?
    How long of a pause will that give me?
    Will that vary across different CPU machines?

  11. #11
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    Sleep takes a number of milliseconds as an argument, so you can set how long it pauses, and the pause will be pretty precise regardless of CPU. However, when a thread sleeps it gives up its processing time, which means that if you sleep the UI thread then it won't paint the screen. This will mean that the app will appear frozen and unresponsive during the time it is sleeping. For this reason, Sleep is almost never the right answer for the UI thread.

    For pauses greater than 50 milliseconds, the best solution is a timer, but that requires thinking about code a bit differently. People often want a routine to be contained in a single method, as you had in the original post, where it does something, pauses for a time, then does something else. Sleep does that, except that the UI is frozen for the duration. With a timer, there would be two methods. The first method would start the timer (which might be ALL that it does), then the timer code fires every time the interval elapses. You can stop the timer or change the interval in that timer code, so it's pretty flexible, but the code to start the timer is not located in the same method as the code that occurs when the timer ticks.
    My usual boring signature: Nothing

  12. #12
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,715

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    There is no action to be taken except a short pause.
    JMcIlhinney has a codebank contribution... here. It does exactly what you're wanting to do and it doesn't have the negative side effects like Threading.Thread.Sleep() can have.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  13. #13
    Frenzied Member IanRyder's Avatar
    Join Date
    Jan 2013
    Location
    Healing, UK
    Posts
    1,232

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    Hi,

    To expand on what has already been said by SH and ONLY if you are using .NET4.5 you can use Async and Await to write "in-line" code which can implement a Pause, using Tasks which execute on another Thread, thereby keeping the UI thread responsive.

    Have a play with these two examples of creating Tasks and see if you can expand on them to make them work for what you need:-

    vb.net Code:
    1. Public Class Form1
    2.  
    3.   Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    4.     For Counter = 10 To 1 Step -1
    5.       TextBox1.Text = Counter.ToString
    6.       Await SomePauseUsingTaskWithSub()
    7.     Next
    8.     MsgBox("Done!")
    9.  
    10.     For Counter = 10 To 1 Step -1
    11.       TextBox1.Text = Counter.ToString
    12.       Await SomePauseUsingTaskWithAddressOf()
    13.     Next
    14.     MsgBox("Done!")
    15.   End Sub
    16.  
    17.   Private Function SomePauseUsingTaskWithSub() As Task
    18.     Return Task.Run(
    19.       Sub()
    20.         Threading.Thread.Sleep(1000)
    21.       End Sub)
    22.   End Function
    23.  
    24.   Private Function SomePauseUsingTaskWithAddressOf() As Task
    25.     Return Task.Run(AddressOf CreatePause)
    26.   End Function
    27.  
    28.   Private Sub CreatePause()
    29.     Threading.Thread.Sleep(1000)
    30.   End Sub
    31. End Class

    Hope that helps.

    Cheers,

    Ian

  14. #14

    Thread Starter
    Hyperactive Member
    Join Date
    Aug 2014
    Posts
    313

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    I actually want the UI to freeze.

    Here is my context:
    I have a pictureBox the user clicks.
    When clicked, the picture changes to pic #2, the viewer sees it for 1-2 seconds, and then the pictureBox is hidden.
    If these 2 lines of code are run back to back, the card is hidden before the user can see pic#2.
    It just looks like the card is hidden when clicked, b/c the code runs so fast.

    I tried this, but it doesn't work.
    The delay happens before the picture is shown.
    It seems like the thread sleeps (Step #2) before the picture is ever loaded in step #1.
    Yet, how did the code get to the sleep part if the image is not yet loaded from Step #1?

    Code:
            
    ' Step 1:  Change pic
    index = cardsArray.ToList().IndexOf(sender)
    cardsArray(index).Image = pics(index)
    
    ' Step 2: NEED A DELAY
    Threading.Thread.Sleep(1000)
    
    'Step 3: Hide card.
    cardsArray(index).Visible = False
    Last edited by RipVoidWinkle; Aug 11th, 2014 at 09:36 AM.

  15. #15
    Frenzied Member IanRyder's Avatar
    Join Date
    Jan 2013
    Location
    Healing, UK
    Posts
    1,232

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    Hi,

    I actually want the UI to freeze.
    No you don't because that's what's causing your problem!, This implies that you probably do not really understand the concept of Multi Threading? In your case you Need the UI Thread to Keep being responsive to complete the Drawing of the image but also add a Pause as the same time. This simply CANNOT be done on the same Thread so you need to try one of the examples posted by the members that have responded to this Thread.

    Cheers,

    Ian

  16. #16
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,715

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    I have a pictureBox the user clicks.
    When clicked, the picture changes to pic #2, the viewer sees it for 1-2 seconds, and then the pictureBox is hidden.
    That's simple enough. What you should do is whenever the user clicks the PictureBox, change it's picture to picture #2 and start the timer, then after the elapsed time, set it back to what it was. A timer is still suitable for this:
    Code:
    Public Class Form1
        Private pbCard As PictureBox
        Private tmr As Timer
    
        Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
            'Controls
            pbCard = New PictureBox With {.Location = New Point(5, 5), .Image = My.Settings.Picture1}
            tmr = New Timer With {.AutoReset = True, .Interval = 1500} '1 and 1/2 second timer
    
            'Events
            AddHandler pbCard.Click, AddressOf pbCard_Click() 'Add the click event for the PictureBox
            AddHandler tmr.Elapsed AddressOf tmr_Tick() 'Add the tick event for the Timer
    
            'We don't want an empty form
            Me.Controls.Add(pbCard)
    
        End Sub
    
        Private Sub pbCard_Click(ByVal sender As Object, ByVal e As EventArgs)
            'Start the timer if it's not already running
            'Also change the image
            If Not tmr.Enabled Then
                pbCard.Image = My.Settings.Picture2
                tmr.Start()
            End If
        End Sub
    
        Private Sub tmr_Tick(ByVal sender As object, ByVal e As EventArgs)
            'Change the image back to what it was    
            pbCard.Image = My.Settings.Picture1
        End Sub
    
    End Class
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  17. #17
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    I agree with DDay, but want to expand a bit on what IanRyder said. The problem you are encountering is that changing the picture doesn't actually change the picture you see. When you change the image, the control invalidates its display area, which means that a paint event is posted to the message queue. The painting won't actually happen until that paint message is pumped from the queue, and pumping the queue can't happen while the UI thread is doing other things (including sleeping). So, while you did change the image with that line of code, the new image won't actually be drawn onto the screen until after the method ends, which means after the sleep has finished and the other lines have completed (one of which hides the picturebox, so the net result is that the picturebox is hidden). Normally, the method will finish so fast that it looks like the painting happens as part of the method, but it really doesn't happen until after the method.

    So, while DDay has the best solution (since I really dislike freezing the UI), there is another alternative that should work, which is to force the picturebox to redraw as soon as the image changes and not wait for the paint event to be handled. This can be done with:

    cardsArray(index).Refresh

    There is also an .Invalidate, which allows the control to redraw when the UI gets around to it, but that's what's already happening when you change the image. The .Refresh method forces the control to redraw right then, before the next line of code is executred. I seem to remember cases where I had to call it twice, but once should work just fine.
    My usual boring signature: Nothing

  18. #18

    Thread Starter
    Hyperactive Member
    Join Date
    Aug 2014
    Posts
    313

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    I will try out dday's method as well,
    but Shaggy's quick hack works, and that is good enough for now !


    Code:
            
    ' Step 1:  Change pic
    index = cardsArray.ToList().IndexOf(sender)
    cardsArray(index).Image = pics(index)
    cardsArray(index).Refresh()
    
    ' Step 2: NEED A DELAY
    Threading.Thread.Sleep(1000)
    
    'Step 3: Hide card.
    cardsArray(index).Visible = False

  19. #19
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    Not exactly a hack. That point is often essential. If you have a long running process and you want to inform the user about where they are at by updating a label, it doesn't help when the changes to the label don't happen until after the long running process has ended. The difference between .Refresh and .Invalidate is this:

    .Refresh: DO IT NOW!!!!
    .Invalidate: Do it when you get a chance.

    Sometimes, you just have to be pushy.
    My usual boring signature: Nothing

  20. #20

    Thread Starter
    Hyperactive Member
    Join Date
    Aug 2014
    Posts
    313

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    I thought V is single threaded.
    When do you have a long running process where you invoke a 2nd thread to give an update?
    Is this stuff with the timer?

  21. #21
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: [RESOLVED] I used to use Huge Loop & DoEvents() to create an artificial delay. A

    Quote Originally Posted by Shaggy Hiker View Post
    The difference between .Refresh and .Invalidate is this:

    .Refresh: DO IT NOW!!!!
    .Invalidate: Do it when you get a chance.

    Sometimes, you just have to be pushy.
    Not exactly. Refresh actually calls Invalidate internally. Specifically, the Invalidate method says "next time you paint, repaint this area". If you don't specify an area then the whole control bounds is assumed. It's possible to call Invalidate multiple times before a single paint to repaint composite areas. The Update method says "repaint all invalidated areas now". The Refresh method simply calls Invalidate with no arguments and then calls Update. If you want to force an immediate repaint of just a particular area then you don;t call Refresh but rather call Invalidate and pass an appropriate argument and then call Update.

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