שתף באמצעות


Create a pause in VB, wait for input

Question

Friday, June 19, 2009 3:01 PM

In VB.net, is it possible to create a pause in a while loop, wait for user input (for example, hit the STOP button, if desired), and if no input within say... a second, continue on?

Basically I have a loop that increments and continues to increment but I am unable to stop the program if desired. To stop the program, VB must:
       
Serial.Write("s" & Chr(13))

Where "s" is a string that the controller looks for to stop all current processes. I would like my while loop to have a chance for the user to cancel, if desired.

Thanks in advance

Matthew

All replies (24)

Monday, June 22, 2009 3:27 PM ✅Answered

When the program is running, I want the value of AzString to be converted into int32 (taken care of), incremented, converted back into string (taken care of), write Serial.write("W" & AzString & ElString & chr(13)) to move the antenna 3 degrees, then pause for 2-3 seconds. The only thing wrong is that once the incrementing starts there is no way to interrupt it using a pause button and then continue when the user desires.

If you run the referenced (above) section of code in your app, do it from a start/stop button.  Clicking start would start that section of your code IN A SEPARATE THREAD.  By running all of that code in a separate thread, you'll be able to use the button anytime you need to.  Here is a link, which you may or may not have already seen, that might be of use to you.  Look it over an see if there is any code that you could use.

http://social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/43b1b2ea-329d-46ac-9926-0334e8dcd1d1/

Doug

SEARCH ... then ask


Monday, June 22, 2009 4:13 PM ✅Answered

I thought there might be some more difficulty when the stream part comes in to play.  However, as my code stands it does allow you to have full control of the form in-between ticks -- because it has such a short activity within its method block.  It sounds to me like you need to investigate using a BackgroundWorker object -- perhaps do the reading/writing within the DoWork handler so that the form's resources are not taken up.  That would probably solve all your problems.  If you're not familiar with this class do a search in these forums - there are many examples of its use here.


Wednesday, June 24, 2009 12:51 PM ✅Answered | 1 vote

here is a stub on how to use a backgroundworker to do what you are trying to do, it will need some tweeking for your specific application.

Public Class Form1

    Private WithEvents MyWorker As System.ComponentModel.BackgroundWorker
    Private Pause As Boolean = False

    Private Sub Form3_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        MyWorker.WorkerSupportsCancellation = True

    End Sub

    Private Sub MyWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles MyWorker.DoWork

        Do
            If Not Pause Then
                'do your work
            End If
            System.Threading.Thread.Sleep(3000) ' wait 3 seconds
        Loop Until MyWorker.CancellationPending

    End Sub

    Private Sub MyWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles MyWorker.RunWorkerCompleted
        'clean up what ever you need to.
    End Sub

    Private Sub Start_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Start.Click
        MyWorker = New System.ComponentModel.BackgroundWorker
        MyWorker.RunWorkerAsync()
    End Sub

    Private Sub PauseContinue_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PauseContinue.Click
        Me.Pause = Not Me.Pause
        If Me.Pause Then
            PauseContinue.Text = "Continue"
        Else
            PauseContinue.Text = "Pause"
        End If
    End Sub

    Private Sub Cancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Cancel.Click
        MyWorker.CancelAsync()
    End Sub

End Class

Adam - Please remember to mark the answers.


Friday, June 19, 2009 3:12 PM

Only when that loop is calling a method in the loop.

Then you can handle before the method is called/invoked a switch which you have set by a button.
Normally a method will always processed until the end

Private X as boolean = False
Private sub A
     For x = 0 to whateverloop
      B
     end for
end sub

Private sub B
    if XStop then
       threading.thread.sleep(10000)'10 seconds
   End if
    'Do what you want to do
    XStop = false
end sub

Private Sub Handles_Button (blabla
     XStop = True
end sub

Typed in this messages so watch typos or small mistakes


Success
Cor


Friday, June 19, 2009 3:35 PM

Private XStop as Boolean = False     

  If Serial.IsOpen = True Then
            While Azimuth <= 450
                Azimuth += 3
Do B
            End While
        End If

Private Sub B
If XStop = true then
threading.thread.sleep(4000)
End if

If Xstop = false
                ElString = Elevation.ToString.PadLeft(3, Chr(48))
                AzString = Azimuth.ToString.PadLeft(3, Chr(48))
                Serial.Write("W" & AzString & " " & ElString & Chr(13))
                Label1.Text = AzString
                System.Threading.Thread.Sleep(4000)
End If

Something similar to this? What would make Xstop true, could I assign a button click to that?


Saturday, June 20, 2009 7:57 AM

Something similar to this? What would make Xstop true, could I assign a button click to that?

That is in the code I've showed

In that code, I set also that stop again to "on", it took me more then some minutes first to understand your problem and then to get that part in it.

Success
Cor


Monday, June 22, 2009 1:15 PM

Cor, I appreciate your help but am still a little lost in how to create/invoke a method inside a loop. This is the loop that I currently have:

If Serial.IsOpen = True Then
            While Elevation <= 180
                Elevation += 3

                ElString = Elevation.ToString.PadLeft(3, Chr(48))
                AzString = Azimuth.ToString.PadLeft(3, Chr(48))
                Serial.Write("W" & AzString & " " & ElString & Chr(13))
                System.Threading.Thread.Sleep(2000)
                Label2.Text = ElString
            End While
        End If

I'd like to have a button that is able to be pushed while the loop is running. If the button is pushed in, I'd like the button name to change to "Continue" and have the loop pause until the button is pushed again, which then reverts the button back to "Pause" and the loop carries on.

I am a little confused by your posts. I will continue to play around with it but if you are able to provide some more insight and have time to do so, I'd love to hear it.

Thanks for your help

Matthew


Monday, June 22, 2009 1:35 PM

Currently, its part of a timer called ElTimer:

    '
    'Elevation Increment
    '
    Private Sub ElTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ElTimer.Tick
        If Serial.IsOpen = True Then
            Serial.Write("C")
            Serial.Write(Chr(13))
            AzString = Serial.ReadLine()
        End If

        If Serial.IsOpen = True Then
            Serial.Write("B")
            Serial.Write(Chr(13))
            ElString = Serial.ReadLine()
        End If

        Elevation = Convert.ToInt32(ElString.Substring(ElString.IndexOf("+") + 2))
        Azimuth = Convert.ToInt32(AzString.Substring(AzString.IndexOf("+") + 2))

        If Elevation <> Elevation Then
            Exit Sub
        End If

        If Serial.IsOpen = True Then
            While Elevation <= 180
                Elevation += 3

                ElString = Elevation.ToString.PadLeft(3, Chr(48))
                AzString = Azimuth.ToString.PadLeft(3, Chr(48))
                Serial.Write("W" & AzString & " " & ElString & Chr(13))
                System.Threading.Thread.Sleep(2000)
                Label2.Text = ElString
            End While
        End If

    End Sub

And I just do a ElTimer.Start() to start the incrementing. Only thing is, once I start ElTimer, the loop will run until complete or I physically stop running the program.


Monday, June 22, 2009 2:00 PM | 1 vote

Can you try this sample, it is about your first question not the second, which is a little bit more complicated, but probably you see it when you have tried this one. It needs 2 buttons while button 2 is to stop the program, it shows a running clock and if you click on that it stops 5 minutes showing.

Public Class Form1
    Private swStop As Boolean
    Private Sub DisplayTime()
        Button1.Text = Now.ToLongTimeString
        Application.DoEvents()
        If swStop Then
            Threading.Thread.Sleep(5000)
        End If
        swStop = False

    End Sub
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        swStop = True
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Button2.Visible = False
        Dim Juz As DateTime = Now.AddMinutes(10)
        While Juz >= Now
            DisplayTime()
            Button1.Text = Now.ToShortTimeString
        End While

    End Sub
End Class

Success
Cor


Monday, June 22, 2009 2:23 PM

When button2 was pressed, button 1 became a clock.

I wanted to use something like this to make 1 button a pause/continue button. My problem is that I am unable to push the button once the "start" button is pushed because the loop continuously runs.

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Button2.Text = "Pause"
        Static Downstate As Long
        Downstate = Not Downstate
        If Not Downstate Then
            Button2.Text = "Pause"
        Else
            Button2.Text = "Continue"
        End If
    End Sub
End Class


Monday, June 22, 2009 2:45 PM | 1 vote

Try the following code.  I think this is what you are asking for -- the ability to pause the stream (in this case an incrementing counter)...
 
 

Public Class Form1

    Private Paused As Boolean = False
    Private WithEvents tim As New Timer

    'REQUIRES A BUTTON (Button1) AND A LABEL (Label1) ON THE FORM.

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        tim.Interval = 250
        tim.Enabled = True
        Button1.Text = "Pause"
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Paused = Not Paused
        If Not Paused Then
            Button1.Text = "Pause"
            tim.Enabled = True
        Else
            Button1.Text = "Continue"
            tim.Enabled = False
        End If
    End Sub

    Private Sub tim_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tim.Tick
        Static counter As Int32 = 0

        counter += 1
        Label1.Text = counter.ToString
    End Sub
End Class

 
 


Monday, June 22, 2009 2:52 PM

Dig boy, that is very similar to what I currently have. The only problem is that once the incrementing loop starts, the button to "pause" the program becomes useless because the program will not allow the button to be pressed (and stops all other functions as well) until the loop stops.

To give a broader overview of what I am dealing with here: The program opens a serial connection to an antenna drive controller. The antenna drive controller moves based on a string input of:

Serial.write("W" & AzString & ElString & chr(13))

Where w is just w, AzString is a 3 digit string of "aaa" and ElString is the same with a string of "eee" where aaa is 000 to 450 and eee is 000 to 180

When the program is running, I want the value of AzString to be converted into int32 (taken care of), incremented, converted back into string (taken care of), write Serial.write("W" & AzString & ElString & chr(13)) to move the antenna 3 degrees, then pause for 2-3 seconds. The only thing wrong is that once the incrementing starts there is no way to interrupt it using a pause button and then continue when the user desires.

Thank you both for your continued help.


Monday, June 22, 2009 2:57 PM | 1 vote

When button2 was pressed, button 1 became a clock.

I wanted to use something like this to make 1 button a pause/continue button. My problem is that I am unable to push the button once the "start" button is pushed because the loop continuously runs.

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Button2.Text = "Pause"
        Static Downstate As Long
        Downstate = Not Downstate
        If Not Downstate Then
            Button2.Text = "Pause"
        Else
            Button2.Text = "Continue"
        End If
    End Sub
End Class

Did you click on that clock,  that stops the loop 5 seconds and then the clock goes on?

You see that DoEvents in the I gave you code, the problem is, that it eats completely your processor in a continuous  loop which you want

It is in my idea better that when the stop button is stopped, you stop all movements, keep where it was and with the start button start the proces again. That is the normal approach by the way.

However, that is not what you were asking for, and I simply fullfiled the question, not the problem.


Success
Cor


Monday, June 22, 2009 3:03 PM

I would very much prefer to attack the problem and not the symptom, if possible. Could you explain this DoEvents process to me? Can that be inserted into my loop to help prevent the loop from starting and not wanting to stop?

I could implement a button that would issue a command

Serial.write("s" & chr(13)) this issues a stop command to the device. I believe ElString and AzString should retain their values as long as ElTimer and AzTimers are stopped as well.


Monday, June 22, 2009 3:07 PM | 1 vote

Hi MBorowski,

i would do it in this way:

Public Class Form1
    Private bStop As Boolean
    Private bEnd As Boolean
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        bStop = Not bStop
        If Not bStop Then
            Button1.Text = "Pause"

        Else
            Button1.Text = "Continue"
        End If

    End Sub
    Private Sub CheckPause()
        While bStop And Not bEnd
            Application.DoEvents()
        End While
    End Sub
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        bEnd = True

    End Sub
    Private Function YourMethod() As Boolean ' Returning if End is pressed (Button2)
        ' Callable by timer
        ElTimer.Stop()
        ' Your Code here...


        Application.DoEvents()

        CheckPause()
        If bEnd Then
            ' Release all Objects, Close all stream and end your application
            Return True
        End If

        ElTimer.Start()
        Return False
    End Function
End Class

Mark the thread as answered if the answer helps you. This helps others who have the same problem !


Monday, June 22, 2009 3:21 PM

How do I call the private function YourMethod inside of the timer? I think this is getting close to what I am looking for.


Monday, June 22, 2009 3:46 PM

This is certainly the button activity that I wish to have:

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Button2.Text = "Start"
        Static Downstate As Long
        Downstate = Not Downstate
        If Downstate Then
            Button2.Text = "Pause"
            AzTimer.Start()
        Else
            Button2.Text = "Continue"
            AzTimer.Stop()
        End If
    End Sub

Use 1 button. Button is labeled "Start". After button is pressed, text changes to "Pause". If that button is pressed, loop activity stops and button changes to "continue". This will go on everytime button is pushed.

I think my main stem of confusion (which continues after reading the measuring reaction time post), is how to put this into a seperate thread so that the user still has control over the GUI and can pause/continue whenever he/she wishes.


Monday, June 22, 2009 3:52 PM

Except using bStop and bEnd instead of the downstate long.


Monday, June 22, 2009 3:56 PM

I think my confusion is with this part of the code that you laid out:

Where do I call this boolean? I think this is similar to what Cor was alluding to in his reply, but I am still unsure how to incorporate it.

Private
 Function
 YourMethod() As
 Boolean
 ' Returning if End is pressed (Button2)


        ' Callable by timer


        ElTimer.Stop
()

        ' Your Code here...






        Application.DoEvents()



        CheckPause()

        If
 bEnd Then


            ' Release all Objects, Close all stream and end your application


            Return
 True


        End
 If




        ElTimer.Start()

        Return
 False


    End
 Function

Monday, June 22, 2009 4:05 PM

Hi again,

just inside your timer event. Its returning if it should end the programm. Sorry, for the delay of answering, but i have installed a 3th party system, and it does not work :-(Mark the thread as answered if the answer helps you. This helps others who have the same problem !


Wednesday, June 24, 2009 10:10 AM

Hi MBorowski,

if you type application.DoEvents you will see in the tooltip of the intellisense, something like (i have german version)  "Processing all windows messages, that are ine the message queue". That means for example if you click a button at the time your code is in the loop or in some long time calculations your application would not "react" anymore on this action. So inserting a Application.DoEvents() just prevent the non reacting.Mark the thread as answered if the answer helps you. This helps others who have the same problem !


Wednesday, June 24, 2009 12:29 PM

Just for some context, many people frown upon the use of Application.DoEvents. I'm not sure of the exact reasoning behind this  -- other than effects it could have in a multi-threaded environment -- and I personally am not passionate about the issue.  However, I believe you can accomplish the same task by using the Refresh() method on a given control.  This is essentially targeting a single area of concern versus clearing the entire message queue.


Wednesday, June 24, 2009 3:08 PM

Thank you so much for the help with background workers.

I am going to assume that I want to run my loop inside 'Do your work

and the 'clean up will be whatever needs to be done to return to the main program?

I am going to read some more about it and tinker with the above code.

Thank you very much.

Matthew


Wednesday, December 21, 2011 11:36 AM

Does no one use DoEvents() anymore?

You can just put it inside your loop and if any keyboard/mouse events occur, they will handled by the routine attached to the event.

Maybe that's too old school for VB.net.