Say I have a button embedded into my spreadsheet that launches some VBA function.
Private Sub CommandButton1_Click()
SomeVBASub
End Sub
Private Sub SomeVBASub
DoStuff
DoAnotherStuff
AndFinallyDothis
End Sub
I'd like to have an opportunity to have some sort of a "cancel" button that would stop SomeVBASub execution at an arbitrary moment, and I'm not into involving Ctrl+Break here, 'cause I'd like to do it silently.
I guess this should be quite common issue, any ideas?
Thanks.
Add another button called "CancelButton" that sets a flag, and then check for that flag.
If you have long loops in the "stuff" then check for it there too and exit if it's set. Use DoEvents inside long loops to ensure that the UI works.
Bool Cancel
Private Sub CancelButton_OnClick()
Cancel=True
End Sub
...
Private Sub SomeVBASub
Cancel=False
DoStuff
If Cancel Then Exit Sub
DoAnotherStuff
If Cancel Then Exit Sub
AndFinallyDothis
End Sub
How about Application.EnableCancelKey - Use the Esc button
On Error GoTo handleCancel
Application.EnableCancelKey = xlErrorHandler
MsgBox "This may take a long time: press ESC to cancel"
For x = 1 To 1000000 ' Do something 1,000,000 times (long!)
' do something here
Next x
handleCancel:
If Err = 18 Then
MsgBox "You cancelled"
End If
Snippet from http://msdn.microsoft.com/en-us/library/aa214566(office.11).aspx
Or, if you want to avoid the use of a global variable you could use the rarely used .Tag property of the userform:
Private Sub CommandButton1_Click()
Me.CommandButton1.Enabled = False 'Disabling button so user cannot push it
'multiple times
Me.CommandButton1.caption = "Wait..." 'Jamie's suggestion
Me.Tag = "Cancel"
End Sub
Private Sub SomeVBASub
If LCase(UserForm1.Tag) = "cancel" Then
GoTo StopProcess
Else
'DoStuff
End If
Exit Sub
StopProcess:
'Here you can do some steps to be able to cancel process adequately
'i.e. setting collections to "Nothing" deleting some files...
End Sub
what jamietre said, but
Private Sub SomeVBASub
Cancel=False
DoStuff
If not Cancel Then DoAnotherStuff
If not Cancel Then AndFinallyDothis
End Sub
I do this a lot. A lot. :-)
I have got used to using "DoEvents" more often, but still tend to set things running without really double checking a sure stop method.
Then, today, having done it again, I thought, "Well just wait for the end in 3 hours", and started paddling around in the ribbon. Earlier, I had noticed in the "View" section of the Ribbon a "Macros" pull down, and thought I have a look to see if I could see my interminable Macro running....
I now realise you can also get this up using Alt-F8.
Then I thought, well what if I "Step into" a different Macro, would that rescue me? It did :-)
It also works if you step into your running Macro (but you still lose where you're upto), unless you are a very lazy programmer like me and declare lots of "Global" variables, in which case the Global data is retained :-)
K
~ For those using custom input box
Private Sub CommandButton1_Click()
DoCmd.Close acForm, Me.Name
End
End Sub
This is an old post, but given the title of this question, the END option should be described in more detail. This can be used to stop ALL PROCEDURES (not just the subroutine running). It can also be used within a function to stop other Subroutines (which I find useful for some add-ins I work with).
As Microsoft states:
Terminates execution immediately. Never required by itself but may be placed anywhere in a procedure to end code execution, close files opened with the Open statement, and to clear variables*. I noticed that the END method is not described in much detail. This can be used to stop ALL PROCEDURES (not just the subroutine running).
Here is an illustrative example:
Sub RunSomeMacros()
Call FirstPart
Call SecondPart
'the below code will not be executed if user clicks yes during SecondPart.
Call ThirdPart
MsgBox "All of the macros have been run."
End Sub
Private Sub FirstPart()
MsgBox "This is the first macro"
End Sub
Private Sub SecondPart()
Dim answer As Long
answer = MsgBox("Do you want to stop the macros?", vbYesNo)
If answer = vbYes Then
'Stops All macros!
End
End If
MsgBox "You clicked ""NO"" so the macros are still rolling..."
End Sub
Private Sub ThirdPart()
MsgBox "Final Macro was run."
End Sub
Related
I have a Corel Draw issue using Exit Sub to close a Userform after clicking a button.
It will run the script to completion, but won't execute the Exit Sub command.
I tried setting a dummy variable to a true value If X=1 then Exit Sub but that didn't do the trick either.
Private Sub CommandButton1_Click()
MsgBox "Script Ran!"
Exit Sub
End Sub
My hope is to click a button, and have the userform close after it runs. My impression was Exit Sub is the way to do this.
Exit Sub functions as an exit point of the current Sub.
Having it before End Sub does literally nothing except leaving the Sub one line earlier.
What you want is Unload Me.
See the relevant VBA documentation of the Unload Statement.
I have a sub called as sub1() which activates the userform1 through the command userform1.show()
This userform1 has a button called as continue. On clicking that button continue - A Macro called as Private Sub continuebutton() gets activated.
I want to program in such a way that it redirects to the line after userform1.show() in sub1().
Is it something that can be done?
Theoretically, what you want is possible, if you do it like this:
In the UserForm:
Private Sub btnContinue_Click()
flag = True
Debug.Print "I continue ..."
sub1
End Sub
In a module:
Public flag As Boolean
Public Sub sub1()
If flag Then
Debug.Print "sub 1 continues here..."
Else
flag = False
UserForm1.Show
Exit Sub
End If
End Sub
It will work exactly as intended, BUT it is not a good practice to work this way. (Some people may throw stones at you for using public variables at all in VBA.) Here are two articles, that give better ideas:
https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/
Disclaimer - this one is mine:
http://www.vitoshacademy.com/vba-the-perfect-userform-in-vba/
On the form properties for userform1, set its "Modal" property to true.
When the form opens, it will have exclusive focus, and the code in sub1 will not continue running until after it closes. This may be the solution you need.
In the code below, the msgbox will only appear once userform1 closes:
sub sub1()
userform1.show()
msgbox "Now continuing with sub1"
end sub
No way as long as you show the form.
If you show the form modal, the calling routine continues if (and only if) the form is closed.
If you show the form non-modal, the code continues to run directly after the show - so it's already done.
So either you have to close the form when the user clicks the "continue..." button to let the calling macro continue or you have to split your code into two routines and call the second on button-click.
You can change your Sub1 as follows:
Sub sub1(Optional Continue As Boolean)
If Continue = True Then
DoSomeStuff
Exit Sub
End If
userform1.show
End sub
And then, you can call your sub1 using:
Private Sub continuebutton()
Call sub1(True)
End Sub
Hope it helps
If you don't want to go with the 'Modal Form' solution, you could add a subroutine to your main module, and call it when required. So, in userform1, you have:
sub sub1()
userform1.show()
end sub
public sub sub2()
msgbox "Now continuing..."
end sub
And then in userform1, set some code on its onClose event:
Private Sub continuebutton()
Call sub2()
end sub
This is my first time posting on this site, and would appreciate some help. I'm a member of the mrexcel forums, and have received a lot of help from those great people over there, but have not been able to find an answer to my question. At work, I've created a workbook that allows us to enter data into different worksheets via a userform. I'm trying to make this as user-friendly as I possibly can, to attempt to eliminate as many "user-errors" as possible. One issue I'm trying to avoid is a user forgetting to close the sheet when their shift is over, and locking everyone out because the workbook is read-only when another user has the file open. So, I have a code that will save and close the workbook after 40 minutes of inactivity. One issue this is causing (and it is a very minor issue, but one that has been brought up to me by one of my employees), is that every once in a while, coincidentally the user will be entering data into the userform right around that 40 minutes of inactivity, and the workbook will close before they had a chance to submit the data. I was wondering if anyone could help me modify my code so that when it reaches that 40 minutes of inactivity, before it saves and closes, it sees that the userform is open and cancels the action if it is. Here is the code that I've got. Thanks for any help I can get.
In Module 1:
Dim CloseTime As Date
Sub TimeSetting()
CloseTime = Now + TimeValue("00:40:00")
On Error Resume Next
Application.OnTime EarliestTime:=CloseTime, _
Procedure:="SavedAndClose", Schedule:=True
End Sub
Sub TimeStop()
On Error Resume Next
Application.OnTime EarliestTime:=CloseTime, _
Procedure:="SavedAndClose", Schedule:=False
End Sub
Sub SavedAndClose()
ActiveWorkbook.Close Savechanges:=True
End Sub
In ThisWorkbook:
Private Sub Workbook_Open()
Call TimeSetting
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Call TimeStop
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Call TimeStop
Call TimeSetting
End Sub
Just call the TimeStop() sub on the Userform_Initialize() event, and then call the TimeSetting() sub when you close the userform
You can see if a userform is loaded by using this function:
Function IsUFLoaded(ByVal UFName As String) As Boolean
Dim UForm As Object
For Each UForm In VBA.UserForms
If UForm.Name = UFName Then
IsUFLoaded = True
Exit For
End If
Next
End Function
And then in SavedAndClose Sub:
Sub SavedAndClose()
' change userform_name to your userform name
If IsUFLoaded("userform_name") = False Then ActiveWorkbook.Close Savechanges:=True
End Sub
Why not, instead of doing what you're suggesting: allowing a user to accidentally leave themselves logged in to a userform which would cause the issue you're trying to avoid, you instead also reset the timer with Call TimeStop and Call TimeSetting within the change events of fields in the userform?
I researched net, but I haven`t found a solution yet and I am still grappling with the following problem:
In vba UserForm I have two command buttons:
1st one ('Run Operation') runs an operation that could take around 30 minutes.
2nd one ('Cancel') was created to interrupt the operation that was triggered off by 'Run Operation'
When I press 'Run Operation' button I cannot press neither 'Cancel' nor 'x' to stop the running operation and I have to wait until the operation finishes, thus the userform is freezed for around 30 minutes.
Code looks more or less like this:
private Sub Cancel_Click()
Cancel = True
End Sub
private Sub RunOperation_Click()
RunOperation.Enabled = False
Call Macro()
End Sub
private Sub Macro()
For i = 1 to 100
'do stuff here
If Cancel = True Then
RunOperation.Enabled = True
Exit Sub
Exit If
Next i
End Sub
What`s more both buttons have TakeFocusOnClick set to False.
I`d be grateful for any ideas.
Thanks in advance !
The DoEvents method is your friend here.
What's happening is that since VBA is single-threaded (i.e. only one macro can be running at a time) it's not possible for events (in your case Cancel_Click()) to trigger. The DoEvents method essentially pauses the code wherever it appears to see if any other events have been triggered and resolves them before code execution is resumed.
Try this, it should work:
Private Sub Macro()
For i = 1 To 100
'do stuff here
DoEvents '<~~ Insert this line here
If Cancel = True Then
RunOperation.Enabled = True
Exit Sub
End If
Next i
End Sub
I have this Vba code that takes a lot of time to execute and i did this sub to stop the execution, But it didnt work , i used the Sendkeys function.
sub stop ()
SendKeys "{Ctrl,Pause}"
end sub
i want also to resume the execution of my Macro, i tried clicking Ctrl+Break but it didnt resume.
Thank you for helping.
Open VBE and insert a module and copy-paste the below code
Sub Main()
For i = 1 To 100000
DoEvents
Debug.Print i
Next i
End Sub
Sub PauseMacro()
Application.SendKeys "^{BREAK}"
End Sub
Go back to Sheet1 and on the developer tab insert a button and assign the PauseMacro to it.
Now run the Main sub and hit the button to stop the execution of the Main macro
Oh, btw. here's how to use the Application.SendKeys method.
You should avoid using SendKeys.
An approach similar to the one suggested by me how, but without using SendKeys could be this:
Global IsTimeToStop As Boolean
Sub Main()
IsTimeToStop = False
For i = 1 To 100000
DoEvents
Debug.Print i
If IsTimeToStop Then Exit Sub
Next i
End Sub
Sub PauseMacro()
IsTimeToStop = True
End Sub
Replace Global with Dim if this is not a standard module.
What about setting up a watch on a variable that breaks if it changes?
just right click on a variable and add a new watch.