User Sub with Optional parameters - not visible in Macro window - vba

I have a macro that goes through column(s) and removed numbers from all cells in the range. I would like to add an optional parameter, so I can call the sub while telling it which columns to run on. Here's what I have:
Sub GEN_USE_Remove_Numbers_from_Columns(Optional myColumns as String)
The idea being I can call it from another sub, like this GEN_USE_...Columns("A B C")
But, I can't run that from the VB Editor, nor can I see that macro in the Macro Window (when clicking View --> Macros). Why not? Why do I have to call it with a parameter (even GEN_USE_...Columns("")) I can't just call GEN_USE_...Columns() anymore.
I've seen that you can add = Nothing to the end, to set a default value if none is given. I've tried that () but it didn't do anything.
I guess my question is A) How come I can't see my macros that have Optional parameters, in the macro window? and B) Why can't I call the macro with parameters directly from the VB Editor? I have to actually create a sub, then I can call the macro within that sub. No more just highlighting some text and hitting "Play".
I know the two issues are probably related, so any insight would be appreciated!
(PS: I know we're supposed to post code, but I don't think that's very relevant. Of course, if you'd like to see it, let me know and I'll update).

Use Optional myColumns as Variant to show it in the Run Macro ([alt]+[F8]) dialog. Alternately, leave it hidden; you can type the name and click Run. The variant type is also the only one that responds properly to the IsMissing function.
Sub GEN_USE_Remove_Numbers_from_Columns(Optional myColumns As Variant)
If IsMissing(myColumns) Then
myColumns = Intersect(Selection.Parent.UsedRange, Selection).Address '.address 'cause you were using a string
End If
Debug.Print Range(myColumns).Address(external:=True)
End Sub
        
You can call the sub with parameters from the VBE's Immediate window ([ctrl]+G).

A Sub with ANY parameters, optional or not, cannot be run directly and can only be called from another Sub or Function
Best option is to write a wrapper Sub that will appear in the Macros window
Sub USER_Remove_Numbers_from_Columns()
GEN_USE_Remove_Numbers_from_Columns
End Sub

Related

Is there a way to hide macros in Excel?

I just finished some VBA and I was wondering if there is a way to hide certain macros on Excel.
I need the user to run a certain macro and only that one, but it shows all the sub macros in Excel. I want to hide the unnecessary macros from the user so that way the user doesn't accidentally click on the wrong one.
You can also do this by placing the macros you want to hide in a separate module and using Option Private Module at the top of the module before the code. The macros will still be available to your project but will not appear in the Macros seen by the user when he clicks the Macros button.
You can either create a button in the ribbon to run the macro, or you can add "Private" before each "Sub" in the VBA editor that you don't want the user to easily access.
To subjectively 'hide' certain sub procedures (i.e. 'macros') from the (Alt+F8) Developer, Macros dialog use an optional non-variant parameter that means nothing.
Sub meh(Optional w As Worksheet)
Debug.Print "hello world"
End Sub
The meh macro will not show up in the list of macros to run. If you dim the parameter as variant it will show in the list. This is likely due to a optional variant parameter being able to use the IsMissing function. It will also not be able to be run from the VBE with F5 or stepped through with F8.
The test sub procedure will run the code correctly.
Sub test()
meh
End Sub
Sub meh(Optional w As Worksheet)
Debug.Print "hello world"
End Sub

Macros not showing up in the run macro menu

I started learning VBA and I don't understand why some macros copy-pasted from the internet do not show up in the run macro menu (Alt-F8).
Below there are 2 macros, but only the second one is showing. Why? And how do I fix it?
Sub Test1(ByVal Target As Hyperlink)
'...
End Sub
Sub Test2()
'...
End Sub
Macros with arguments are not available in Macros list because they cannot be run alone instead they are called by another macro by passing the required arguments.
If a Sub declaration contains parameters it will not show there.
You cannot call macros that have parameters as you are describing. If you need to have a parameter, you can take it out and then have the user input the value.
Sub Test1()
Dim hyperLink As String
hyperLink = InputBox("Please input hyperlink", "My Parameter")
'...
End Sub
Alternatively, if the hyperlink is in your document, grab the value from your document instead.
Here are my 5 cents - if you give an optional parameter, you will be able to call the sub routine, even if it will not be shown among the ones which you can chose from.
Write aaaaTestMe and press Run.
Public Sub aaaaTestMe(Optional lngA As Long = 8)
Debug.Print lngA
End Sub
You can call an even private macro from any excel object you can assign a macro, calling it this way:
'MyWorkbook'!'MyModule.MyProcedure "MyParameter1"'
(be careful with single quotes: ' around procedure name with parameter)

Can you a call a Sub that has an argument with a button?

I want to call the following sub with a button from my excel sheet but it doesn't appear in the list when you try to assign a macro. I also call it from a bigger macro, in which case I don't want to show the "Finished" message box. Hence I have the DontShowMsgBox boolean argument.
Sub InteriorDumbCopyExport(DontShowMsgBox as Boolean)
'do stuff...
If DontShowMsgBox Then
MsgBox "Finished."
End If
End Sub
It's when I add the argument in that I can no longer see it in the list. Is there an easy way around this?
I want to call the sub directly as I have to call it from a shape so can't use an ActiveX button. I haven't found anything in my searching so far but feel there must be a solution, otherwise what is the difference between a function with an argument and a sub with an argument?
You can indeed do that. In the Assign Macros dialog, simply type the name of the macro and the argument, all enclosed in single quotes. For example:
'InteriorDumbCopyExport True'
Note however that this can cause problems if your workbook is saved as an xlsb file (I don't know why).
In this particular case though, I'd just make the argument optional and default to whichever value you want the shape to use. Then you just use the macro name in the Assign Macros dialog without adding a parameter.
Simply call your button sub with another sub:
Sub test() 'call with your button
Call InteriorDumbCopyExport(DontShowMsgBox as Boolean)
End Sub
Another way would be to know the source of your DontShowMsgBox. As a public variable, you could set it in front of executing the macro and then directly use it within having no arguments for the call itself.
In any module:
Dim DontShowMsgBox As Boolean
And if you only need it for debugging then make it a Const which will only be true if you want it to.
For a FORM control that's normal behaviour as you cannot assign a Sub depending on parameters as the button wouldn't know which parameter to hand over (for a similar reason you can't link functions to buttons), whereas an ActiveX button would create its own Sub (e.g. Private Sub CommandButton1_Click()) within the code space of the worksheet it appears.
So use a Sub without parameters for the button to call, within the sub determine the actual condition of DontShowMsgBox and call another Sub doing the job into which youdeliver this parameter.

VBA cannot find my macro when it has parameters

I am trying to write a macro that will be attached to a series of buttons in an Office 2010 backstage tab. Depending on the button clicked the Macro should be called with different parameters.
The issue I am having is that if the Macro is defined as having parameters then VBA will display the "Macros" dialog box, with no Macros listed. Removing the parameters from the declaration will allow the macro to run, but it needs the Macros to make sense.
The VBA being used is below:
Sub NewDocs(docType As String, docTemplate As String)
Dim sMyShellCommand As String
sMyShellCommand = "C:\NewDocs.exe " & docType & docTemplate
WordBasic.Shell (sMyShellCommand)
End Sub
Any ideas
If I've got your question right....
Because there is no place in the Macros dialog box for entering parameters, the macros with parameters are simply not shown.
If you want to make them visible in the dialog box, you can enumerate those functions you need (and I hope it is not a big number).
For example,
Sub NewDocs1
Dim docType As String
Dim docTemplate As String
docType = "the type you want"
docTemplate = "the template you want"
NewDocs docType, docTemplate
End Sub
In addition, as you said in the question, you wanted the macro to run when buttons were pressed. Then there is no need to make the macro visible in the dialog box (which saves your labor). Simply associate it with the button with correct parameters.
You can pass arguments from the Macro dialog. For example, if you have this macro
Sub myMacro(n As Long)
MsgBox n
End Sub
To run it, enter
mymacro 1000
... and press the Run button.
You can't call functions or subs with parameters from UI components, just subs without parameters. The best solution is to create a parameter free sub for each button you need to associate with, and call the parameterized sub or function from inside each sub

Easy way to find cell reference in excel vba

I am working on a spreadsheet with lots of VBA code in it. Most cell references use numbers, e.g.
Worksheets("Sheet1").Cells(someRow, someColumn)
I am wondering if there is any easy way to find out exactly which cell is being referenced - is it possible to put a breakpoint on this line of code, and then see which cell it will refer to (using the traditional "BA10" style) or will I have to calculate it each and every time using divs and mods?
Not all the references are hard coded, and I would really like to be able to work out where data is being pulled from, as the cell is accessed (not necessarily changed).
(edit) Is it possible to do this without changing the original source line, e.g. in an event module or something?
Debug.Print Worksheets(1).Cells(10, 53).Address(False, False)
returns BA10
There is another option. If you are making changes to a sheet, you can catch the Change event on the Worksheet, and pump out the changed range like so:
Private Sub Worksheet_Change(ByVal Target As Range)
Debug.Print "CHANGED -> " & Target.Address(False, False)
End Sub
Each change to the sheet will be output to your Immediate window like thus:
CHANGED -> G10
CHANGED -> G11:G28
There is also the SelectionChange event as well, but that's unlikely to be too useful. There is no event for just "reading" cells.
You can use Address.
Debug.print Worksheets("Sheet1").Cells(10, 53).Address
Will print the ranges address for you in the Immediate Window.
Is that what you are looking for?
You can also change your reference style to R1C1 in:
tools\options\general\R1C1 reference style
this way you will be able to know what the code is refereeing to.
Another way to go would be to add a watch in the VBE (Visual Basic Editor). You can do this by going to View>Watch Window to make sure the watch window is visible. Then from Debug menu click Add Watch. If you do this in break mode the context will already be set for you so all you have to do is paste in the expression you want watched and it will be visible. This of course works best on range objects (ex: rngFoo.Address as the expression) but you can paste in things like Worksheets(1).Cells(10, 53).Address.
Use the following to know which cell is calling the function:
Application.Caller.Address
Example:
Function Addition(va As Double, vb As Double) As Double
Addition = va + vb
MsgBox "Cell calling function Addition is " & Application.Caller.Address
End Function
Whenever this function is calculated a message box indicates the cell address calling it.
Be aware that calling this function from another VBA function will send an error. Check http://www.cpearson.com/Excel/WritingFunctionsInVBA.aspx