dcsimg
Results 1 to 7 of 7
  1. #1

    Thread Starter
    Lively Member
    Join Date
    Nov 2013
    Posts
    83

    Resolved [RESOLVED] Tight loop doesn't allow full button control.

    The start loop below exits when the run boolean is false. That works okay, but while that loop is running, the Quit button must be clicked twice to exit. The first click (anywhere on the form) allows it to be highlighted and the second click allows it to respond and exit the app.

    I thought the Application.DoEvents would allow both buttons to respond normally but it doesn't.

    Question: How can I implement a loop that continues to respond properly to all buttons.

    Code:
    Public Class Form1
        Dim run As Boolean = False
        Dim a As Double = 0
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            start()
        End Sub
    
        Sub start()
            While run
                a += 1
                Debug.Print(a)
                Application.DoEvents()
            End While
        End Sub
    
        Private Sub BUTTON1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            run = Not (run)
            ' Debug.Print("Pressed")
            Debug.Print(run)
            start()
        End Sub
    
        Private Sub Quit_Button_Click(sender As Object, e As EventArgs) Handles Quit_Button.Click
            Button1.PerformClick()
            Application.Exit()
        End Sub
    End Class

  2. #2
    .NUT jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    98,672

    Re: Tight loop doesn't allow full button control.

    That's just plain bad code. If you feel like you need to use Application.DoEvents to make your UI respond then what you should actually be doing is using asynchronous code of some sort. In this case, one option would be to use a BackgroundWorker and put your loop in the DoWork event handler. You could then exit the application in the RunWorkerCompleted event handler. Basically, Application.DoEvents is almost always a hack.
    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
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    32,033

    Re: Tight loop doesn't allow full button control.

    While it is a hack, if this is the actual code, I'd use it anyways. DoEvents was the only way to do some things in VB6, and isn't the best way to do the same things in .NET, but it does have the advantage of being quick and simple. If the goal is a simple little program to test something out, DoEvents is easier than any other option. So, as long as you are aware that it has issues when you use it in more complicated programs, and that there are better options in those cases, then go ahead and use it in a simple program like this.

    What I really don't like is the Button1.PerformClick. While that method exists, it's pretty much always a bad idea. I'm not even sure what the actual implementation of the method looks like. I would assume that it posts a click message to the message queue, rather than simply calling the Button1_Click method directly, in which case it is going to have some odd interactions with DoEvents.

    The code in the event handlers is simple. If you want two different event handlers to do the same thing, create a method with the common code, and call that method from each event handler. In this case, I'd just replicate the part I wanted in each method, since it won't be more than a couple lines, tops.

    As to the issue, it sounds like the form doesn't have focus, so the first click gives it focus. That may be due to the call to Start being in Load. I'd have to play around with it to see, but you might try moving items out of Load and into the Shown event handler.
    My usual boring signature: Nothing

  4. #4

    Thread Starter
    Lively Member
    Join Date
    Nov 2013
    Posts
    83

    Re: Tight loop doesn't allow full button control.

    The Button1.PerformClick was put there as I was trying to make the thing work. Without it, I simply could not exit the code if it was in the start routine.

    I will do some research on what a background worker is and how to implement it. I will also do the research to find out what shown is and when / how to use it.
    The last thing I will look for is how to get focus on the form as suggested.

    I have lots of research to do, but thank you both.


    Again. all I am trying to do is get a routine working repeatedly until a button is pressed.

  5. #5

    Thread Starter
    Lively Member
    Join Date
    Nov 2013
    Posts
    83

    Re: Tight loop doesn't allow full button control.

    I looked up Async and Await and this is what I came up with from the example. When the Run Button is clicked the RunAsync function does run, but while it is running neither button responds. Unless I did something wrong, this cannot be halted by pressing the Run button a second time.

    Code:
    Imports System.IO
    
    Public Class Form1
        Dim run As Boolean = True
        Private Sub Run_Button_Click(sender As Object, e As EventArgs) Handles Run_Button.Click
            run = Not (run)
            Main()
        End Sub
    
        Private Sub Quit_Button_Click(sender As Object, e As EventArgs) Handles Quit_Button.Click
            Application.Exit()
        End Sub
    
        Sub Main()
            ' Create a Task with AddressOf.
            Dim task = New Task(AddressOf ProcessDataAsync)
            ' Start and wait for task to end.
            task.Start()
            task.Wait()
            Console.ReadLine()
        End Sub
    
        Async Sub ProcessDataAsync()
            Dim task As Task(Of Integer) = runAsync()
            ' This statement runs while RunAsync executes.
            Debug.Print("Please wait, processing")
            ' Use await to wait for task to complete.
            Dim result As Integer = Await task
            Debug.Print("Count: " + result.ToString())
        End Sub
    
        Async Function runAsync() As Task(Of Integer)
            Debug.Print("RunAsync Started")
            Dim count As Double = 0
            While 1
                count += 1
                If count = 1000000000 Then
                    Exit While
                End If
            End While
            Debug.Print("RunAsync exited")
            Return count
        End Function
    End Class

  6. #6
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    39,812

    Re: Tight loop doesn't allow full button control.

    That would be because of this section of your code:
    Quote Originally Posted by PickyBiker View Post
    Code:
        Sub Main()
            ' Create a Task with AddressOf.
            Dim task = New Task(AddressOf ProcessDataAsync)
            ' Start and wait for task to end.
            task.Start()
            task.Wait()
            Console.ReadLine()
        End Sub
    As you are explicitly waiting for it, no other code can execute.

    Simply removing the line task.Wait() may get things working.

  7. #7

    Thread Starter
    Lively Member
    Join Date
    Nov 2013
    Posts
    83

    Re: Tight loop doesn't allow full button control.

    Than you to all who took the time to help me with this. It is working as expected and I can start or stop the RunAsync function at any time.

    Code:
    Imports System.IO
    
    Public Class Form1
        Dim run As Boolean = False
    
        Private Sub Run_Button_Click(sender As Object, e As EventArgs) Handles Start_Button.Click
            run = Not (run)
            If run Then
                Start_Button.Text = "Stop"
                Main()
            Else
                Start_Button.Text = "Start"
    
            End If
        End Sub
    
        Private Sub Quit_Button_Click(sender As Object, e As EventArgs) Handles Quit_Button.Click
            Application.Exit()
        End Sub
    
        Sub Main()
            ' Create a Task with AddressOf.
            Dim task = New Task(AddressOf ProcessDataAsync)
            ' Start and wait for task to end.
            task.Start()
            'task.Wait()
        End Sub
    
        Async Sub ProcessDataAsync()
            Dim task As Task(Of Integer) = runAsync()
            ' This statement runs while RunAsync executes.
            Debug.Print("Please wait, processing")
            ' Use await to wait for task to complete.
            Dim result As Integer = Await task
            Debug.Print("Count: " + result.ToString())
        End Sub
    
        Async Function runAsync() As Task(Of Integer)
            If run = True Then
                Debug.Print("RunAsync Started")
            Else
                Debug.Print("RunAsync Halted")
            End If
            Dim count As Double = 0
            While run
                count += 1
                If count = 1000000000 Then
                    Exit While
                End If
            End While
            Debug.Print("RunAsync exited")
            Return count
        End Function
    End Class

Posting Permissions

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



Featured


Click Here to Expand Forum to Full Width


×
We have made updates to our Privacy Policy to reflect the implementation of the General Data Protection Regulation.