When using Sub's, if I'm not returning a value, is it necessary to use:
Public Sub whatever()
...
Exit Sub
End Sub
instead of just
Public Sub whatever()
...
End Sub
Do I gain anything (memory, speed, etc.) by using "Exit"?
Does the Sub exit anyway when it is done even if I don't use the "Exit" statement?
Thanks.
In this particular case Exit Sub is completely unnecessary. It can be used there but is generally considered bad style. The statement is necessary when you want to prematurely leave the method. For example if you detect a specific condition is met and you don't want to execute the rest of the method
Public Sub Example()
If SomeCondition Then
Exit Sub
End If
' Do other work
End Sub
Related
I have a rather large VBA project and now I need to know what sub is calling the function.
example:
sub first()
RunSomeCode
end sub
sub second()
RunSomeCode
end sub
Function RunSomeCode()
' here I need to know if the calling sub is first() or second()
end function
The only way I know of is to pass a string with the functioncall in first and second sub with the calling sub name.
But this function is called from everywhere and I wanted to know if there is a simpler way to do it.
I need to get a string of what sub called the RunSomeCode, to just get it in debugging is not enough.
Ctrl+L is the shortcut for the Call Stack in VBA. You may see the stack. As you see in the screenshot below, the function was called from the Sub Second:
Assuming you only need it in First and Second then probably this could help
Option Explicit
Sub first()
RunSomeCode "First"
End Sub
Sub second()
RunSomeCode "Second"
End Sub
Sub third()
RunSomeCode
End Sub
Function RunSomeCode(Optional Caller As String)
' here I need to know if the calling sub is first() or second()
If Len(Caller) > 0 Then
Debug.Print Caller
End If
End Function
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 have an UserForm in Excel VBA with different Objects like CheckBox, RadioButtons, TextEdit Box etc. Each Object has different event functions, but i want to call the some event functions in user defined sub function like UserForm_Init() function in order to re-utilize the same code written in them.
Example:
Private Sub CheckBox1_Click() [Later changed to public also]
If CheckBox1.Value Then
--- Code
End If
End Sub
Private Sub CheckBox2_Click()
If CheckBox2.Value Then
--- Code
End If
End Sub
Private Sub RadioButton1_Click()
If RadioButton1.Value Then
--- Code
End If
End Sub
' User Defined Function
Sub UserForm_Init()
CheckBox1_Click()
CheckBox2_Click()
RadioButton1_Click()
End Sub
But when i am trying to compile it is showing compile error, how can i execute these object event functions within user-defined sub function.
I know I can write individual functions within Event functions and call these individual functions for user-defined sub function. But in this case, it will be too-difficult to re-change entire code. I have only option of calling these event-functions in an sub-function for execution.
How can i perform these actions, call event functions with in the user defined sub function [UserForm_Init()]?
VBA doesn't use curly braces. This code won't compile with those. VBA requires Sub and End Sub to define the procedures.
Sub CheckBox1_Click()
if CheckBox1.Value Then
--- Code
end if
End Sub
Sub CheckBox2_Click()
if CheckBox2.Value Then
--- Code
end if
End Sub
Sub RadioButton1_Click()
if RadioButton1.Value Then
--- Code
End If
End Sub
Sub UserForm_Init()
CheckBox1_Click()
CheckBox2_Click()
RadioButton1_Click()
End Sub
NOTE: If your custom function UserForm_Init is not in the Form's code module, you'll need to qualify the procedure names, e.g.:
Sub UserForm_Init()
UserForm1.CheckBox1_Click()
UserForm1.CheckBox2_Click()
UserForm1.RadioButton1_Click()
End Sub
Additional complexity may arise based on your implementation, but I hope this will get you started.
You're missing parts of code in your example; it's hard to determine what you're trying to do without a clear picture. It is possible to call an event procedure from another, it's a bad idea for a number of reasons.
Instead of calling a private procedure like you're trying to, sort of like this:
Private Sub myEvent()
'event code here
End Sub
Sub mySub()
myEvent()
End Sub
...you're better off to move the code that you need to be shared to a shared module, kind of like this:
Public Sub mySubInSharedModule()
'event code here
End Sub
Private Sub myEvent()
Call mySubInSharedModule()
End Sub
Public Sub mySub()
Call mySubInSharedModule()
End Sub
There are numerous articles explaining this further, on this site and others. To get you started, see this search as well as this Stack Overflow question.
My question may be very foolish. but i would like to know is it possible and how?
from one of the function\sub can i create another sub\function.
Example:
Sub first()
x = 10
if x = 10 Then
'Add Another Sub based on the Condition in another module.
Sub Second()
End Sub
End If
End Sub
My real problem is i am trying add new sheet in a loop based on some condition. if i am able to add sheet, then i need to create some buttons in another sheet, as well as function for those buttons.
Please help on this.
By default, my approach would be:
Sub first()
x = 10
if x = 10 Then
'Call Another Sub based on the Condition.
Call Second()
End If
End Sub
Sub Second()
' do stuff
End Sub
If you are creating new sheets and adding buttons with code behind them, all of the code, button design, etc. should be done. Any customization would be through parameters.
This is more about the design of your code. Firstly, you can't put a sub inside another sub, that's just not how it works unfortunately.
What you can do is use scope and conditions to control the flow of your code and make sure that certain parts are only executed when you want them to be.
The easiest, and recommended, method is to just write the other sub anyway but only call it if a certain condition is met.
Public Sub Main_Sub()
Dim x As Integer
x = 11 '// Change x to 5 and the other sub won't run
If x > 10 Then
Other_Sub
'// Code execution will resume here once 'Other_Sub' has finished
MsgBox "Other Sub complete!"
End If
End Sub
Private Sub Other_Sub()
MsgBox "You're in the other sub now!"
End Sub
Another way is to simply exit the sub if your condition isn't met, although this is used more for error handling than conditional execution:
Public Sub Main_Sub()
Dim x As Integer
x = 11 '// Change x to 5 and the other sub won't run
If x > 10 Then
Other_Sub
'// Code execution will resume here once 'Other_Sub' has finished
MsgBox "Other Sub complete!"
Else
Exit Sub
End If
MsgBox "You will only see this message if x was greater than 10!"
End Sub
Private Sub Other_Sub()
MsgBox "You're in the other sub now!"
End Sub
One final method could be using labels, and although it's a viable solution I do advise against it because it severely affects the readability and logic of your code:
Public Sub Main_Sub()
Dim x As Integer
x = 11 '// Change x to 5 and the other sub won't run
If x < 11 Then GoTo Skip_Other_Sub:
Other_Sub
Skip_Other_Sub:
MsgBox "You will see this regardless of the other sub being run!"
End Sub
Private Sub Other_Sub()
MsgBox "You're in the other sub now!"
End Sub
The main point here is that it's better to write all of the code that may or may not be needed and call it conditionally rather than trying to create code "on the fly" which is messy at best and requires programmatic access to the VBE which can leave your machine vulnerable if you aren't 100% confident with what you're doing.
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