(I’m using VB6 but I imagine this comes up in most other languages.)
I’ve got a GUI button that calls a routine that takes a minute or two to complete. I want impatient users to be able to click on the button a second time to have it gracefully exit out of the routine at any point.
I used a static variable to make this work pretty well (see code below), but I’m cleaning up the project and I want to put the For/Next loop into its own function, since it’s required in several different places in the project.
But doing that would break my static flag embedded in the for/next, so I need to make some changes. Before I do something hare-brained with public (global) variables, I thought I’d ask what other (smarter, perhaps actually CS educated) people have done when faced with this problem.
So basically my question is how do I replicate this:
Private Sub DoSomething_Click() Static ExitThisSub As Boolean ' Needed for graceful exit If DoSomething.Caption = 'Click To Stop Doing Something' Then ExitThisSub = False ' this is the first time we've entered this sub Else ' We've re-entered this routine (user clicked on button to stop it) ExitThisSub = True ' Set this so we'll see it when we exit this re-entry Exit Sub ' End If DoSomething.Caption = 'Click To Stop Doing Something' For i = 0 To ReallyBigNumber Call DoingSomethingSomewhatTimeConsuming If ExitThisSub = True Then GoTo ExitThisSubNow DoEvents Next ' The next line was missing from my original example, ' prompting appropriate comments DoSomething.Caption = 'Click To Do Something' Exit Sub ExitThisSubNow: ExitThisSub = False ' clear this so we can reenter later DoSomething.Caption = 'Click To Do Something' End Sub
When I move the for/next loop to its own function?
I’m thinking I’ll change ExitThisSub to a public variable QuitDoingSoManyLongCalculations that will exit the new for/next sub and then the DoSomething_Click in the same way.
But I always feel like an amateur (which I am) when I use global variables – is there a more elegant solution?
Well you could declare the variable at module level in the forms as private. That is not a global but a module level variable. Then you could pass it to the function you create and check it in the function.
But be careful with the DoEvents. It basically means allow the windows message loop to process messages. This means that not only can the user click your button again, they can close the form and do other things. So when you are in this loop you’ll need to set a module level variable anyway as you’ll need to check for it in a QueryUnload of the form and in any event handlers.
You can also use the Tag property of the control itself to store a flag of sorts. But I don’t consider that more elegant.
I also prefer to use two different buttons. Just hide one and show the other. That way your cancel code and run code are separated in different event handlers.
To expand on my answer here is some sample code that handles the unloading aspect. Here if you stop via the x it prompts you. If you kill via task manager, it dies gracefully.