I have a button on my spreadsheet that when clicked, triggers a series of nested subs. Example:
Sub Button1_Click()
Macro1
Macro2
Macro3
Macro4
Macro5
Macro6
End Sub
Because the time to run all these subroutines can be rather lengthy, and the user cannot do anything else in excel while it is running, I was wondering if there was a way to create a button that, when clicked, can halt the running of all these subs? I've seen recommendations of adding DoEvent to the code, however, I'm not sure where in my code to implement it.
Any insight would be much appreciated.
The DoEvents statement will allow any lagging user input and handling of events.
The Solution
What you probably want to do is overload the OnKey event handler and use it to set the value of a global variable. Next in your code above you need to verify if that variable is set. If it is abort execution. Be sure to add DoEvents statements in any moments in your code where you want this to run.
Optionally, w/o the OnKey handler, DoEvents will allow the user to hit ESC to stop the execution of your macro. But it will revert to debug mode and highlight the current executed row in your procedure. Not sure this is what you want however.
Tip - what to do first
Having said that... what I personally think you should do FIRST is optimize your VBA code. I would assume there is probably a lot of room for improving the execution time of your procedure (my tutorial).
You can create Macro to perform below steps/operation and call when you want to abort the other running macros:
Step 1-- Menu bar -> Run -> Reset
Step 2-- Menu bar -> Run -> Break
Related
There are a half-dozen answers to this. "Open a second instance" "Have a pause" Etc. I'm not looking for that.
I'm looking for the user of the workbook to be able to manipulate the workbook while the macro is running. I've seen this working before, where the user could scroll around, change tabs, even add and remove data, all while the macro was running. Unfortunately, I couldn't get permission to look at the code (And committing CFAA violations ins't my cup of tea), so I have no idea how they did it.
How can you enable a user to edit the workbook as macros are running? For a specific example, I have Conway's Game of Life running. Users select cells to flip live/dead, then can run a macro to run the entire thing. I think it'd be nice for users to be able to change cells as the macro is running. (which is a second on select macro)
Thank you
Sorry just reread the question. I wouldn't expect the permutation to run for very long - not long enough to interrupt really.
But if it does, then the advice about using lots of DoEvents stands.
The other option is that you can use the OnTime event to have a "heartbeat"
VBA Macro On Timer style to run code every set number of seconds, i.e. 120 seconds
You can set the timer to say 3 seconds. Every time the OnTime event occurs you do one step of your permutation. In the three seconds in between they can edit.
Refactor your macro to use Events. In which case, you would have a series of event handlers (instead of one monolithic macro) to respond to various triggers. This is assuming that the macro is influenced by what the user is doing in the worksheet.
One way of (sort of) doing this is to use a Modeless Userform (UserForm.Show vbModeless)
The user form stays visible but the VBA stops running when the form is shown and the user can then interact with Excel. Then when the user clicks a button on the form the code behind the button starts running again.
So in reality the user is either interacting with Excel or interacting with the form ...
I have code stuck.
It might be in an infinite loop.
Not sure.
Is there a way to break the program to stop at the current line of code that it is running on?
I'd like to avoid shutting down excel because I want to be able to catch where in the loop it is, and by going into the code, I will be able to tell how much processing was done. I would like to break into the code, if possible.
It is stuck on hour glass.
Ctrl+Break doesn't seem to work
Seems like the running code has hijacked all the quota that cpu is giving to excel.
If there is nothing I can do now, is there something in the future I can do to where I can more easily break into the code?
I'm thinking that an intermittent wait within a loop might be a feasible solution.
In the future, include a DoEvents inside the loop. It will run a little slower, but you will be able to use Ctrl+Break to stop it from running.
Create a progress dialog when entering the loop and include a Cancel button. Within your loop check for the Cancel signal/event. This also gives you some flexibility on how you react to the Cancel - you could stop the loop and display key information in a new dialog box (for example).
Basic steps to achieve what I have described (not necessarily the most elegant or the most re-useable, but simple enough for those of lesser experience):
create a modeless (not modal) Form with either suitable labels or a progressbar item (for
visual effect). Include a public property (Boolean) for Cancel (e.g.
boolCancel)
Place a button on form and onClick set boolCancel = True
In your main code, show the form just before your problem loop.
while in your loop you can update some label or progress bar on the
form so that you have a visual indication of whether the loop is
doing something of value or if it is now simply spinning its wheels.
How you do this depends on what your loop is doing.
Also while in your loop check your boolCancel value. If true then
display any state information you want and break from the loop.
If your loop ends normally, hide/unload the progress dialog.
Is it possible to use the call feature to call a macro at a specific line? I ask as the macro i am coding does this - First it runs half of the code, then based upon user choices it opens up a custom UserForm, and runs the UserForm command buttons. After this I need a command button to go back to the code the line after the UserForm was used. The UserForm works as intended and many different stats calculations can be run. It has a button that returns the user back to the rest of the code, but I can't get that to work.
I have tried using both GoTo statements and the Call feature (The most promising solution) but have had no success so far.
There are several ways to achieve it, I will show one of them.
You can modify your procedure (macro) asking for a value as optional.
Sub MyMacro(Optional Answer As Boolean)
If Answer = True then
'Do some stuff
Else
'Do some stuff
End If
End Sub
As Answer is optional you can call your Macro with or without arguments.
Suppose you call your Macro from your user form:
Call MyMacro(True)
Hope this give you some hints.
You can use VBA for this, but I would recommend using the Windows Task Scheduler. I think that will be a lot better, all things considered.
https://www.digitalcitizen.life/how-create-task-basic-task-wizard
As I bet I can tell the following is happening in my Excel 2013 macro:
Sub WorkSheet_BeforeDoubleClick is calling a form
Some logic populates and shows the form
The double click event is captured by the form as an undesired selection in a listbox
Is there a correct way to suppress the double click in the middle of the subroutine? I have ideas for getting around it (sleeping the thread works, re-positioning the form works, locking and unlocking the listbox based on other events works) Those are all poor workarounds and I am hoping for a built-in solution that I am just not aware of.
(And setting Cancel = True does not work because I believe setting only kicks in once the code is finished running. The double click becomes a selection in the middle of the subroutines)
If the issue is the form you are launching from the macro is catching the same double click event try the following:
1) Have your macro call a new sub routine via 'Application.OnTime()' schedule it to run just long enough in the future to pass the 'physical click', maybe 1s. Instead of directly calling Form.Show(), as you are currently doing.
2) Your new subroutine just has to do: Form.Show(), and whatever else you deem necessary.
I've looked for this everywhere in vain.
I have a macro in one Excel file (SplitTickets.xlsm) that loops through 3 separate Excel files.
When SplitTickets is opened a dialog appears with a button to launch the macro.
Since I don't want users to mess with the excel with accidental keys or clicks while the macro is running, I would like to keep the dialog on top of all the files being opened, modified and closed, and update the label displayed when the process is over.
I tried setting it up as modal but since the macro needs to select sheets, cells and ranges, the dialog being modal blocks the rest of the macro being executed.
And turning modal off makes it disappear beneath the flurry of the macro's manipulations.
How can I keep this dialog on top without turning it modal?
You can use the below code instead. I use it on regular basis when I don't want the user to interfere with the code run.
Sub Testing()
Application.Screenupdating = False
Your Code
'This is very important, or it will remain disabled.
Application.Screenupdating = true
End sub
This way the user is not aware of what is happening in the background.
Sandesh :)
Add the following line to the start of your macro:
Application.ScreenUpdating = False
This will turn off the "flurry of macro" activity you can see, so the users aren't tempted to interfere. It will also make your macro run faster, because it no longer has to redraw the screen between each step. Be sure to set it back to True at the end of the macro.
You may also want to use this option as well:
Application.Cursor = xlWait
This will turn the mouse cursor to an hourglass (or whatever "wait" cursor is set in their system) so the user knows the macro is running. To disable the hourglass, use this:
Application.Cursor = xlDefault
Beyond this, the macro should automatically prevent the user from interfering with the sheets while it is running. However, it will keep a queue of the keystrokes made while the macro is running and enter them after the macro is done.
Could you add a Formname.Show at the end of you macro to make it reappear?