I have a method-A() that is called from multiple methods,
On a condition in method-A, i have to terminate the macro.
i saw one option as Exit sub but this will just exit the current sub ie:method-A() and the remaining program continues.
how to handle this.
Sub mainMethod()
method-A()
end Sub
Sub method-A()
if (true) Then
'Terminate the macro. that is exit method-A() and also mainMethod()
end Sub
Edit after comment:
Just use end where you want to terminate ALL code.
Sub mainMethod()
method_A()
end Sub
Sub method-A()
if (true) Then End
'Terminate the macro. that is exit method-A() and also mainMethod()
end Sub
Original Answer: All you need to do is make methodA a function and return this function as FALSE if you want to exit the main method as per the following code:
Sub mainMethod()
'Run the custom function and if it returns false exit the main method
If Not method_A Then Exit Sub
'If method_A returns TRUE then the code keeps going
MsgBox "Method_A was TRUE!"
End Sub
Function method_A() As Boolean
Dim bSomeBool As Boolean
'Code does stuff
bSomeBool = True
'Check your condition
If bSomeBool Then
'Set this function as false and exit
method_A = False
Exit Function
End If
'If bSomeBool = False then keep going
End Function
Related
Inside a sub ("Sensitivities") I am calling another sub ("MVE_NIM_Subs.MVE_Main"), if this sub outputs a MsgBox I want to end the sub and to go to the "Jump" defined, instead of continuing executing the sub. ¿How can I do it?
Thanks
Public Sub Sensitivities()
Application.Run "MVE_NIM_Subs.MVE_Main"
........
Jump:
End Sub
You can trap your Msgbox with a public boolean variable. In the sub MVE_NIM_Subs.MVE_Main modify your code to set a public boolean variable to be true if the msgbox appears.
After this sub ends, execution will go back to executing code inside sub Sensitivities. Then just check the value of the public boolean variable. If it's true, then go to Jump.
Something like this:
Option Explicit
Public DidMsg As Boolean
Sub Sensitivities()
DidMsg = False
Application.Run "MVE_NIM_Subs.MVE_Main"
If DidMsg = True Then GoTo Jump
'rest of your code
'
'
'
'
Jump:
'rest of your code after point Jump
'
'
'
'
End Sub
Sub MVE_Main()
'your code whatever it is
'right after using the msgbox type:
DidMsg = True
End Sub
MsgBox is a modal element, so code execution is paused until the user deals with it. This leaves you with two options:
Don't display a MsgBox (if you only want to stop code when this happens I'm not sure what the point of it is anyway?)
Create a non-modal userform to emulate a MsgBox instead.
You have to options in my opinion:
Make MVE_NIM_Subs.MVE_Main a function that returns Bool. When you display message box in MVE_NIM_Subs.MVE_Main set return value to True. Then in calling sub you can write:
If returnValue Then GoTo Jump
Keep MVE_NIM_Subs.MVE_Main as sub and declare some global Bool variable, that can be used within two subs. In outer sub set it to False before calling MVE_NIM_Subs.MVE_Main and in MVE_NIM_Subs.MVE_Main set it to True whenever you show the message box. Then you can use it in outer sub to decide whether to jump or not, like in first option :)
I don't know what happened but after I implemented a new code the 'Sub' continues running after I press my 'Cancel' button.
My 'Cancel' Button:
Private Sub CancelButton_Click()
If Cancel Then Exit Sub
Cancel = True
'Save Settings
....
Unload Me
End Sub
My Sub and new function:
Sub Example()
Dim myArr() as string
...
some loops....
If Cancel Then
Exit Sub
End If
myArr = NewFunction(a1,a2)
myVar = myArr(1)
...
End Sub
Function NewFunction(a1,a2) As String ()
Dim tmpArr() as string
...
ReDim tmpArr(1)
...
NewFunction = tmpArr()
End Function
Issues:
If I avoid execution of line myVar = myArr(1) the code continues running after canceling, so I need to stop execution manually. Why?
If line myVar = myArr(1) is executed, the form will be unloaded and error appears:
runtime error 9
Subscript out of range
Don't get it what is wrong. The code runs ok if I'm not canceling it. The problem only is with 'Cancel' button which was working perfect before...
Cheers, Andy
Form buttons:
This is correct code which works for me:
Dim Cancel As Boolean
Private Sub UserForm_Initialize()
Cancel = False
End Sub
Private Sub CancelButton_Click()
SetStatus ("Cancelling")
If Cancel = False Then
Cancel = True
Exit Sub
End If
'Save Settings
'... some code
If Cancel Then
Unload Me
End If
End Sub
Sub Example()
'...
'some loops....
If Cancel Then
'close open files...
'... some code
SetStatus ("Cancelled")
Exit Sub
End If
'...
End Sub
When 'Cancel' button is pressed, the code will stop in safe manner. Then the second click on 'Cancel' button the form will be unloaded.
I've got a workbook which runs a macro to show the userform Open1 as it opens, using the (very basic) code:
Private Sub Workbook_Open()
Open1.Show
End Sub
This does its job fine - each time I open the workbook, the userform pops up and runs perfectly.
But, I want the userform to appear the first time the workbook is opened only. Is there a way to allow this to happen?
You could use a dummy module which gets deleted the first time you open the spreadsheet...
Something like:
If ModuleExists("DummyModule") Then
Open1.Show
DoCmd.DeleteObject acModule, "DummyModule"
End If
Function ModuleExists(strModuleName As String) As Boolean
Dim mdl As Object
For Each mdl In CurrentProject.AllModules
If mdl.Name = strModuleName Then
ModuleExists = True
Exit For
End If
Next
End Function
Update: as stated, DoCmd isn't used in excel vba. That will teach me to write code without testing it!
The following updated code will work, but in order to access the VB environment, excel needs to be trusted.
There is a setting in the Trust Center>Macro Settings that you can tick for this code to work under Developer Macro Settings
As such, this may not be the way to go as it opens up the possibility of security issues...
Sub RemoveModule()
If ModuleExists("DummyModule") Then
Open1.Show
Dim vbCom As Object: Set vbCom = Application.VBE.ActiveVBProject.VBComponents
vbCom.Remove VBComponent:=vbCom.Item("DummyModule")
End If
End Sub
Function ModuleExists(strModuleName As String) As Boolean
Dim mdl As Object
For Each mdl In Application.VBE.ActiveVBProject.VBComponents
If mdl.Name = strModuleName Then
ModuleExists = True
Exit For
End If
Next
End Function
Try this:
If Sheets("Hide").Cells(1,1) = "1" Then
Open1.Show
Sheets("Hide").Cells(1,1) = "0"
End if
You must create the sheet Hide, and give the cell A1 the value 1, in that case the form will be shown.
After you create the sheet, hide it with this
Sheets("Hide").Visible = xlVeryHidden
And show it with this
Sheets("Hide").Visible = True
Here's an alternative bit of code that will persist between saves and allow you to reset it. No need to create a hidden sheet.
Put this in a module (invoke the DisplayFormIfFirstTime from your Workbook_Open event handler....)
Option Explicit
Private Const cMoniker As String = "FormHasBeenDisplayed"
Private Sub DisplayFormIfFirstTime()
If HasBeenOpened = False Then DisplayForm
End Sub
Public Sub DisplayForm()
MsgBox "Ok, its not a form but a dialog box...", vbInformation
End Sub
Public Function HasBeenOpened() As Boolean
Dim oName As Name
On Error Resume Next
Set oName = Application.Names(cMoniker)
On Error GoTo 0
If Not oName Is Nothing Then
HasBeenOpened = True
Else
Call Application.Names.Add(cMoniker, True, False)
End If
End Function
'Call this to remove the flag...
Public Sub ResetOpenOnce()
On Error Resume Next
Application.Names(cMoniker).Delete
End Sub
Based on the idea supplied by PaulG, I have coded an upgrade that will check for the name and if not found run a function, add the name and save the workbook for a more seemless approach to this problem...
Placed in ThisWorkbook
Private Sub Workbook_Open()
Run "RunOnce"
End Sub
Placed in a module
Sub RunOnce()
Dim Flag As Boolean: Flag = False
For Each Item In Application.Names
If Item.Name = "FunctionHasRun" Then Flag = True
Next
If Flag = False Then
Call Application.Names.Add("FunctionHasRun", True, False)
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
Call RunOnceFunction
End If
End Sub
Private Function RunOnceFunction()
Open1.Show
End Function
Sub ResetRunOnce()
For Each Item In Application.Names
If Item.Name = "FunctionHasRun" Then
Application.Names.Item("FunctionHasRun").Delete
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
End If
Next
End Sub
I have been creating a tool which extracts data from other Excel files and does various types of analysis. Because of these numerous different analyses, I have had to create multiple subs which are then all called upon in a "master" sub, which is activated when the user presses a button.
The issue I'm having is with error handling to stop the macro when there's an issue or when the user is trying to stop the code themselves - e.g. a pop-up box appears with a "continue or stop" type scenario. However, I can't work out how to make the macros stop! Using Exit Sub doesn't work because the master sub will then just jump to the next sub in the queue.
See example below - the user presses the button to activate master_sub. If within the sub1 stage the user selects "No" or "Cancel" in a MsgBox, sub1 will end (if using Exit Sub) but master_sub will then just go on to sub2 and sub3. How do I make master_sub stop based upon an input/action within sub1?
Sub sub1()
Dim MyMsg as Variant
MyMsg = msgbox("Do you wish to continue?", vbYesNo)
If MyMsg = 7 then 'If user clicks "No"
Exit Sub
Else
'Do stuff
End if
End Sub
Sub sub2()
'More stuff
End Sub
Sub sub3()
'Other stuff
End Sub
master_sub()
call sub1
call sub2
call sub3
End Sub
Instead of using Exit Sub Throw a custom exception and catch it in your master_sub.
Something like
Sub sub1()
Dim MyMsg as Variant
MyMsg = msgbox("Do you wish to continue?", vbYesNo)
If MyMsg = 7 then 'If user clicks "No"
Throw new MyException
Else
'Do stuff
End if
End Sub
master_sub()
Try
call sub1
call sub2
call sub3
Catch ex as MyException
'Do something if you want
End Try
End Sub
The "how does master_sub" detect when one of the child methods has been cancelled can be solved like this:
Sub master_sub()
Dim result As Boolean
result = Sub1()
If (result = False) Then
Exit Sub
End If
result = Sub2()
If (result = False) Then
Exit Sub
End If
result = Sub3()
If (result = False) Then
Exit Sub
End If
End Sub
Function Sub1() As Boolean
Return True
End Function
Function Sub2() As Boolean
Return False
End Function
Function Sub3() As Boolean
Return True
End Function
I've kept it very simple, but basically by changing from a Sub to a Function you can have each method return a value and decide in your main routine (master_sub) what to do depending on the value returned.
SOLVED!
I have to validate that certain cells are not empty, so I want to create a subroutine and pass the variables I need checked.
This is what I came up with:
Sub errorMessage(errMsg As String, errRange As String)
If Range(errRange) = "" Then
MsgBox errMsg, , "Error:"
Range(errRange).Activate
'this is what i was looking for :doh:, the 'end' line terminates everything..
END
End Sub
Now when I call it from my button, will it actuall end the sub of the button?
i.e.
Private Sub CommandButton1_Click()
Call errorMessage("name is missing", "D4")
'this function shouldn't be called if there was a msgbox displayed with the above call
sendEmail
End Sub
How can i make this happen?
EDIT:
OK So this is how i sovled it, the reason i'm trying to do this is to avoid tons of lines of code in the buttonClick sub, what are your thoughts??
keep in mind that this thing has to check about 25 questions for blanks before executing the sendEmail sub....
Private Sub CommandButton1_Click()
Call validateEntry("Client Name is missing.", "D4")
Call validateEntry("# not complete.", "D5")
Call validateEntry("Address same as CV?", "D6")
Call validateEntry("Number missing.", "D8")
Call validateEntry("Type missing.", "D9")
Call validateEntry("Q1 requires a Yes or No.", "E19")
Call validateEntry("Q2 requires a Yes or No.", "E21")
Call validateEntry("Q3 requires a Yes or No.", "E23")
Call validateEntry("Q4 requires a Yes or No.", "E25")
Call validateEntry("Q5 requires a Date.", "D28")
Call validateEntry("Q6 requires a Yes or No.", "E30")
Call validateEntry("Q7 requires a Yes or No.", "E32")
MsgBox "passed"
'sendEmail
End Sub
Sub validateEntry(errMsg As String, errRange As String)
If Range(errRange) = "" Then
MsgBox errMsg, , "Error:"
Range(errRange).Activate
End
End If
End Sub
So, in your example, you're looking for the "passed" notification to only be sent when there is data in cell D4, right?
This should work:
Private Function errorMessage(errMsg As String, errRange As String) As Boolean
errorMessage = False
If Len(Trim(Range(errRange))) = 0 Then
MsgBox errMsg, , "Error:"
Range(errRange).Activate
errorMessage = True
End If
End Function
Public Sub CommandButton1_Click()
If errorMessage("name is missing", "D4") = False Then
MsgBox "passed"
End If
End Sub
Alternatively, you can handle all MsgBox notifications from within the function, to group similar logic together, and keep the Button Click Event Sub clean:
Private Function errorMessage(errMsg As String, errRange As String)
If Len(Trim(Range(errRange))) = 0 Then
MsgBox errMsg, , "Error:"
Range(errRange).Activate
Else
MsgBox "passed"
End If
End Function
Public Sub CommandButton1_Click()
Call errorMessage("name is missing", "D4")
End Sub
There are a number of misconceptions here.
First, no, it will not end the button routine by default. You will need to handle that within your button.
Next, you're missing an End If somewhere in here:
Sub errorMessage(errMsg As String, errRange As String)
If Range(errRange) = "" Then ' This may not be the best way to check for
' an empty range
MsgBox errMsg, , "Error:"
Range(errRange).Activate
Exit Sub
End Sub
You really don't even want a subroutine in the first place, you want a function that returns a boolean, like this:
Function errorMessage(errMsg As String, errRange As String) as Boolean
' Function returns True if an error occured
errorMessage = False
If Range(errRange) = "" Then
MsgBox errMsg, , "Error:"
Range(errRange).Activate
errorMessage = True
End If
End Sub
And then here:
Private Sub CommandButton1_Click()
If errorMessage("name is missing", "D4") Then
Exit Sub
End If
'this msgbox should not display if the above msgbox is displayed
MsgBox "passed"
' continue on with all of your fun processing here
End Sub