When Creating Macro, What Setting Do I Need? - vba

I have a bunch of VBA code that I'd like to throw in a macro.
Basically I call a bunch of subs that update the page.
It looks something like this:
Call Update1("Work", strConn)
Call Update2("Work", strConn)
Call Update3("Work", strConn)
where Update1, Update2, and update3 are all in the same sheet with the form
Public Sub Update1(strPlace, strConn)
SQL code..
End Sub
How would I throw it all in a macro? Could I copy and paste all the code (including the calls?) That hasn't work. Neither has taking only the Subs. Not sure what's next.

All you should need is to insert a module as a container for all this code. Unless each of the
UPDATE subs are specific to a single sheet, then they should probably be in the module as well.
All of your "call" statements will need to be in their own SUB myMain () ... END SUB
Once you've done this, you can either run it from the Tools, Macro list, or hook it into other events (a custom toolbar button or something else)
Edit: to be more precise, this is what your module should look like:
Sub myMainMacro()
'... initialize stuff
Call Update1("Work", strConn)
Call Update2("Work", strConn)
Call Update3("Work", strConn)
'... do more stuff
End Sub
Sub Update1(strPlace, strConn)
SQL code..
End Sub
Sub Update2(strPlace, strConn)
SQL code..
End Sub
'... other UPDATE SUBs here
Function CalculateValue(myInput as integer) as integer
'... do calculations here
CalculateValue = answer
End Function
All code (except for global variable declarations, which we haven't talked about) needs to be inside a SUB or a FUNCTION (a function returns a value, a SUB does not). Since they are all in the same module, the keyword PUBLIC isn't necessary.

Related

Calling a Private Sub from another module

I have two modules, Module1 and Module2.
In Module1:
Private Function myCheck() As Boolean
if [Operation] = [Something] then
myCheck = True
Else
myCheck = False
End if
End Sub
In Module2, I would like to run myCheck sub in Module 1 then do another operation:
Private Sub Execute()
[Operation 1]
If myCheck = True Then
[Operation 2]
Else
[Operation 3]
End If
End Sub
It does not work. If I place Private Function myCheck within the same module then it works. Is there a special method to call a sub or function from another module?
Use Option Private Module For Module1, then in Module 2:
for a Sub; qualify what you're calling with the Module it's in, like so:
Module1.myCheck()
for a Private Sub; use Application.Run and qualify what you're calling with the Module it's in, like so:
Application.Run ("Module1.myCheck")
Using Private Module hides it's contained sub/s in the Developer > Macros list.
Further Reading :-)
Read through the comments in the code below to see what does and doesn't work.
To confirm the behaviours yourself:
Create a new Excel, open Developer > Visual Basic, insert 3 Modules.
Copy the below code blocks into the relevant modules.
'In Module1
Option Explicit
Sub ScopeTrials()
'NOTES:
' Only NormalSubIn_NormalModule shows in Developer > Macros.
' As the default without a keyword is Public I have called
' these "Normal". I.e. you can use Public or Nothing wherever
' Normal is.
' A line commented out shows what doesn't work.
NormalSubIn_NormalModule
Call NormalSubIn_NormalModule
Application.Run ("NormalSubIn_NormalModule") 'Not recommended!
NormalSubIn_PrivateModule
Call NormalSubIn_PrivateModule
Application.Run ("NormalSubIn_PrivateModule") 'Not recommended!
'PrivateSubIn_NormalModule
'Call PrivateSubIn_NormalModule
'Module2.PrivateSubIn_NormalModule
'Call Module2.PrivateSubIn_NormalModule
Application.Run ("PrivateSubIn_NormalModule") 'Fails with duplicates! See Explanation
Application.Run ("Module2.PrivateSubIn_NormalModule")
'PrivateSubIn_PrivateModule
'Call PrivateSubIn_PrivateModule
'Module3.PrivateSubIn_PrivateModule
'Call Module3.PrivateSubIn_PrivateModule
Application.Run ("PrivateSubIn_PrivateModule") 'Fails with duplicates! See Explanation
Application.Run ("Module3.PrivateSubIn_PrivateModule")
'Explanation: if there is an identical sub in another Private Module, then this fails
'with Runtime Error 1004 (Macro not available or Macros Disabled), which is Misleading
'as the duplication and/or nonspecified module is the problem.
'I.e. always specify module!
'Also, this only fails when the line is actually run. I.e. Compile check doesn't find this
'when starting to Run the code.
End Sub
'In Module2
Option Explicit
Sub NormalSubIn_NormalModule() 'only THIS sub shows in Developer > Macros
MsgBox "NormalSubIn_NormalModule"
End Sub
Private Sub PrivateSubIn_NormalModule()
MsgBox "PrivateSubIn_NormalModule"
End Sub
'In Module3
Option Explicit
Option Private Module
Sub NormalSubIn_PrivateModule()
MsgBox "NormalSubIn_PrivateModule"
End Sub
Private Sub PrivateSubIn_PrivateModule()
MsgBox "PrivateSubIn_PrivateModule"
End Sub
You can use Application.Run to do this:
Application.Run "Module1.myCheck"
The macro will stay "invisible" for the users if they display the Macros Dialog Box (Alt+F8), since it's still private.
Edit #1
A second option is to introduce a dummy variable as an optional parameter in the Sub, like this:
Public Sub notVisible(Optional dummyVal As Byte)
MsgBox "Im not visible because I take a parameter, but I can be called normally."
End Sub
This too, will hide the macro in the Macros Dialog Box (Alt+F8), but it can now be invoked the usual way.

EXCEL VBA procedure ambigous name

My VBA code is too large and I'm trying to make smaller SUBS so the error won't come up, but then the error "Ambigoius name" pops up. I've tried to rename my subs...
Ex.
Private Sub worksheet_calculate()
Range("I9").Interior.Color=Range("AK9").Display.Format.Interior.Color
end sub
Private Sub worksheet_calculate2()
Range("J9").Interior.Color=Range("AQ9").Display.Format.Interior.Color
end sub
...when I rename the other subs as shown in the example it doesn't do anything, only the original work properly. How do I rename them so they can work properly?
In my understanding, worksheet_calculate is the predefined name of the subroutine, triggered by the event when the worksheet is recalculated.
You can define and call other private subs from it.
like
Private Sub worksheet_calculate()
rem sub body
CalculateSub1 pars 'variant one
Call CalculateSub1(pars) 'variant two
rem sub body
End Sub
Sub CalculateSub1(pars)
Rem Sub body
End Sub
Just insert the below line of codes at the end of your main sub which is "worksheet_calculate"
Call worksheet_calculate2
Call worksheet_calculate3
Call worksheet_calculate4
Call worksheet_calculate4
.
.
.
Call worksheet_calculaten

Global reference to object on Click() sub (VBA)

I was wondering if there's a way to refer to the object of the "Click()" sub.
To make it clearer, let's say we have a button named foo1 and this button has a click sub "foo1_Click()". Does vba has a keyword to get the reference to foo1 that is global?
Something like:
Public Sub foo1_Click()
GlobalKeyword.Property
End Sub
P.s.: something like the word "this" from java refering to its own instance of class
Edit: In the example, the "GlobalKeyword" would refer to "foo1"
I think you're looking for the Application.Caller property found here.
In your case you would want to do something like....
Public Sub foo1_Click()
Dim button As Shape
Set button = ThisWorkbook.Sheets("sheetname").Shapes(Application.Caller)
End Sub
Of course after that you would want to do some error checking to make sure button is not nothing.
If you want to use the same code for a lot of buttons, then you may be better of using a separate subroutine.
Private Sub foo1_Click()
Call do_something
End Sub
Private Sub foo2_Click()
Call do_something
End Sub
Sub do_something() 'called by the foo _Click event
MsgBox Application.Caller
End Sub
This way, you it is easy to maintain the core functionality for all buttons simply by updating the do_something procedure.

Having an MS Office UserForm detect which subroutine called it

In a VBA project of mine I am/will be using a series of reasonably complex userforms, many of which are visually identical but have different subroutines attached to the buttons. As a result I'm not overly keen on the idea of duplicating them multiple times in order to get different functionality out of the same layout. Is it possible to have a userform detect which subroutine called it and use this in flow control? I would like to be able to do something like this:
Private Sub UserForm_Initialize()
If [the sub that called the userform is called "foo"] then
Call fooSub
else
Call barSub
End If
End Sub
My backup plan is to have the calling subroutine set a global variable flag and have the userform check that, but that seems like a rather crude and clumsy solution.
Thanks everyone,
Louis
You can use the tag property of the form. Load the form, set the property, then show the form:
Sub PassCallerToForm()
Load UserForm1
UserForm1.Tag = "foo"
UserForm1.Show
End Sub
Now that the property is set, you can determine what to do in the form:
Private Sub UserForm_Activate()
If Me.Tag = "foo" Then
Call fooSub
Else
Call barSub
End If
End Sub
You can also use public variables:
' in userform
Public Caller As String
Private Sub UserForm_Click()
MsgBox Caller
Caller = Now()
Me.Hide
End Sub
' in caller
Sub callUF()
Dim frm As New UserForm1
frm.Caller = "Test Caller"
frm.Show
MsgBox frm.Caller ' valid after Me.Hide
Set frm = Nothing
End Sub
Personally, I would not have one userform doing two disparate activities. The code would get hard to read pretty quickly, I think. Copying the layout of a userform is pretty trivial.
To copy a userform: Open a blank workbook. In the Project Explorer, drag the userform to the new workbook. Rename the userform in the new workbook. Now drag it back to the original workbook. Change the code in the userform copy.
If you absolutely don't want separate userforms, I recommend setting up a property of the userform. Userforms are just classes except they have a user interface component. In the userform module
Private mbIsFoo As Boolean
Public Property Let IsFoo(ByVal bIsFoo As Boolean): mbIsFoo = bIsFoo: End Property
Public Property Get IsFoo() As Boolean: IsFoo = mbIsFoo: End Property
Public Sub Initialize()
If Me.IsFoo Then
FooSub
Else
BarSub
End If
End Sub
I always write my own Initialize procedure. In a standard module:
Sub OpenForm()
Dim ufFooBar As UFooBar
Set ufFooBar = New UFooBar
ufFooBar.IsFoo = True
ufFooBar.Initialize
ufFooBar.Show
End Sub

Call a VBA Function into a Sub Procedure

I know this is a simple question for someone out there, but I have never really used function module at all because I did not understand what they were.
So I have a whole bunch of things I can use this for (cut down on redundancy), but I want to know how I call into a sub (like a button click) procedure from a form.
I tried this...
Sub Command_Click()
Call "pptCreator"
End Sub
I know that is pretty bad, but I have no idea how to bring this into a procedure.
Here are some of the different ways you can call things in Microsoft Access:
To call a form sub or function from a module
The sub in the form you are calling MUST be public, as in:
Public Sub DoSomething()
MsgBox "Foo"
End Sub
Call the sub like this:
Call Forms("form1").DoSomething
The form must be open before you make the call.
To call an event procedure, you should call a public procedure within the form, and call the event procedure within this public procedure.
To call a subroutine in a module from a form
Public Sub DoSomethingElse()
MsgBox "Bar"
End Sub
...just call it directly from your event procedure:
Call DoSomethingElse
To call a subroutine from a form without using an event procedure
If you want, you can actually bind the function to the form control's event without having to create an event procedure under the control. To do this, you first need a public function in the module instead of a sub, like this:
Public Function DoSomethingElse()
MsgBox "Bar"
End Function
Then, if you have a button on the form, instead of putting [Event Procedure] in the OnClick event of the property window, put this:
=DoSomethingElse()
When you click the button, it will call the public function in the module.
To call a function instead of a procedure
If calling a sub looks like this:
Call MySub(MyParameter)
Then calling a function looks like this:
Result=MyFunction(MyFarameter)
where Result is a variable of type returned by the function.
NOTE: You don't always need the Call keyword. Most of the time, you can just call the sub like this:
MySub(MyParameter)
if pptCreator is a function/procedure in the same file, you could call it as below
call pptCreator()
Calling a Sub Procedure – 3 Way technique
Once you have a procedure, whether you created it or it is part of the Visual Basic language, you can use it. Using a procedure is also referred to as calling it.
Before calling a procedure, you should first locate the section of code in which you want to use it. To call a simple procedure, type its name. Here is an example:
Sub CreateCustomer()
Dim strFullName As String
strFullName = "Paul Bertrand Yamaguchi"
msgbox strFullName
End Sub
Sub Exercise()
CreateCustomer
End Sub
Besides using the name of a procedure to call it, you can also precede it with the Call keyword. Here is an example:
Sub CreateCustomer()
Dim strFullName As String
strFullName = "Paul Bertrand Yamaguchi"
End Sub
Sub Exercise()
Call CreateCustomer
End Sub
When calling a procedure, without or without the Call keyword, you can optionally type an opening and a closing parentheses on the right side of its name. Here is an example:
Sub CreateCustomer()
Dim strFullName As String
strFullName = "Paul Bertrand Yamaguchi"
End Sub
Sub Exercise()
CreateCustomer()
End Sub
Procedures and Access Levels
Like a variable access, the access to a procedure can be controlled by an access level. A procedure can be made private or public. To specify the access level of a procedure, precede it with the Private or the Public keyword. Here is an example:
Private Sub CreateCustomer()
Dim strFullName As String
strFullName = "Paul Bertrand Yamaguchi"
End Sub
The rules that were applied to global variables are the same:
Private: If a procedure is made private, it can be called by other procedures of the same module. Procedures of outside modules cannot access such a procedure.
Also, when a procedure is private, its name does not appear in the Macros dialog box
Public: A procedure created as public can be called by procedures of the same module and by procedures of other modules.
Also, if a procedure was created as public, when you access the Macros dialog box, its name appears and you can run it from there
Procedures in a Module start being useful and generic when you pass in arguments.
For example:
Public Function DoSomethingElse(strMessage As String)
MsgBox strMessage
End Function
Can now display any message that is passed in with the string variable called strMessage.
To Add a Function To a new Button on your Form: (and avoid using macro to call function)
After you created your Function (Function MyFunctionName()) and you are in form design view:
Add a new button (I don't think you can reassign an old button - not sure though).
When the button Wizard window opens up click Cancel.
Go to the Button properties Event Tab - On Click - field.
At that fields drop down menu select: Event Procedure.
Now click on button beside drop down menu that has ... in it and you will be taken to a new Private Sub in the forms Visual Basic window.
In that Private Sub type: Call MyFunctionName
It should look something like this:
Private Sub Command23_Click()
Call MyFunctionName
End Sub
Then just save it.