How to call a macro from a button and pass arguments - excel

I want to add a button to my excel worksheet which should call a macro that can handle one agument (an integer value). Sadly when creating the button, I cannot link any macro that has arguments. Also just typing the macro and the argument does not work.
Is there any simple solution to pass an argument to a macro when a button is pressed?

Yes, you can assign a macro to a button (or other excel controls/menu actions) and pass constant OR variable arguments to it.
In the 'Assign Macro' window (right-click on object and select 'Assign Macro'):
Enclose the macro name in single quotes
e.g. to pass 2 constants: 'Button1_Click("A string!", 7)'
Select 'This Workbook' for the 'Macros in' field
If you wish to pass a variable (like the value of a cell), enclose the parameter in Evaluate()
For example, to pass the value of Sheet1!$A$1 to a button function, you would have the following text in the 'Macro name:' field:
Button1_Click(Evaluate("Sheet1!$A$1"))
If you don't enclose your variable argument with an 'Evaluate' function, excel returns the error 'Formula is too complex to be assigned to an object.'.
I would have included an image if this were allowed on my first post.

Suppose you have a public sub take 1 argument like below (just for explanation purposes.)
And you insert a button on Worksheet like below, and you can not find the macro name when you want to assign your sub to this button.
Now, you can type in your sub name + space + argument manually in single quotes, like below, click ok.
Then you see, problem solved.

Sub ert()
Call ert2(Cells(1,1).Value)
End Sub

Use an activeX control command button and in the button click method, call the sub and pass the argument:
Private Sub CommandButton_Click()
Dim x as Integer
x = 1
Call SomeSub(x)
End Sub

To call this Sub from a button :
Public Sub TestButton(strMessage As String)
MsgBox strMessage
End Sub
... be aware that the Sub won't be listed in the available macros, because it has a parameter. Just type in the call in single quotes : 'TestButton "Hello"'

Called from a regular "forms" button on a worksheet you can do something like this:
Sub TestMe()
Dim c, arr
c = Application.Caller
arr = Split(c, "_")
If UBound(arr) > 0 Then MsgBox "Got " & arr(1)
End Sub
Where the calling button is named (eg) "Button_3"
Or (simpler) right-click the button and enter 'TestMe2 5' (Including the single-quotes)
Sub TestMe2(i)
MsgBox "Got " & i
End Sub
See also: Excel 2010 - Error: Cannot run the macro SelectCell using .onAction

I had trouble with my version of Personal.xlsb!'testForXXXX("Test Test")'. I got an error when clicking the button containing the Macro.
However, I was able to fix it by removing the "(" and ")". So, Personal.xlsb!'testForXXXX "Test Test"' worked (notice the space between testForXXXX and "Test...").
In fact, I didn't need Personal.xlsb! and was just able to use 'testForXXXX "Test Test"'.

I hit the same issue with the assign button not being very useful until I realised that all the potential macros displayed were only the ones in my Personal.xlsb file that took no arguments. Then I typed Personal.xlsb!'macroNameWhichTakesArguments("arg1", ...)' and the sheet picked it up.
I.E. Personal.xlsb!'testForXXXX("Test Test")'
Where the macro testForXXXX take a string as input, then the sheet worked ok.
When you use the assign macro route for a button in excel 2007 at least testForXXXX will not show up in the list of potential macros if it takes args.
I suspect Aaron C got tripped up by this too. Maybe its best not to use a Personal.xlsb file?

I would have liked to comment the answer by QA Collective but I do not have enough reputation point yet. Previous solutions did not work for me, as my macros are in named modules in my VBA project.
With Excel 2013, the way to pass a parameter to a macro using a button that worked in my context is :
'<module name>.<macro name> <parameter>'
e.g
'DataProcessor.disle "myText"'
or
'DataProcessor.editWSProcess True'
(boolean do not need double quotes when passed as parameters)
Please note that single quotes are around the whole expression.

Related

Is there a why a userform _change procedureal event not be recursive?

I have data in a textbox (call it info) on a userform that I would like the user to be able to change so I set up a info_change() subroutine. Problems are:
(1) as I load initial data into info.text from VBA, the info_change() subroutine is called.
(2) when I go into the info field to change the value INSIDE the info field, it call the info_change() subroutine again and continues to call the routine until the last entry I put in info field = value in info field before changing in (seems recursive)
Any thoughts? Maybe instead of calling it info_change(), call it another procedural event?
Thanks, Marty
Easy example using _AfterUpdate event
Instead of reacting each time a single part of the info textbox got changed by the control's _Change event, it's more efficient to use the _AfterUpdate event pausing till you changed to another control (btw could be easily augmented by a CommandButton procedure).
In order to prevent unnecessary calls a comparison is made between old and current info text using a type definition in the code module's declaration head. Furthermore I added a boolean IsTest variable to this definition to allow a test display of changes made triggering some other stuff in code (see b).
Additional hint: there's no recursion, the Change event or AfterUpdate event are only firing as reaction to each single change or update, i.e. making no difference if triggered via Userform_Initialize settings or an eventual user input, however the condition check prevents the Init from getting active.
Declaration head of Userform code module
Option Explicit ' declaration head of code module
'User defined type
Private Type TThis
cnt As Long
oldTxt As String
IsTest As Boolean
End Type
Dim this As TThis
Private Sub UserForm_Initialize()
'[1]assign text to Info-Control
Me.TextBox1.Text = "bla bla"
'[2]remember text
this.oldTxt = Me.TextBox1.Text
'[3]optional choice of test modus
this.IsTest = True
If this.IsTest Then Debug.Print " #", "Change history", "(new) text" & vbNewLine; 0, Now, this.oldTxt
End Sub
Private Sub TextBox1_AfterUpdate()
'~~~~~~~~~~~~~~~
'~> Example call
'~~~~~~~~~~~~~~~
check Me.TextBox1
End Sub
Sub check(myTextbox As MSForms.TextBox)
' Purp.: compare current text with old text
If this.oldTxt <> myTextbox.Text Then
'a) optional test display
If this.IsTest Then this.cnt = this.cnt + 1: Debug.Print this.cnt, Now, myTextbox.Text
'b) do other stuff
' ...
'c) remember old text
this.oldTxt = myTextbox.Text
End If
End Sub

Executing VBA function from current workbook in cell formula get error #name (#nom for French version)

I meet a problem which is probably very simple when I use a VBA function into a formula of a cell, I get the cell content "#NAME" (not found function ? while a macro using the function (for test) is executed normally (displays the wished content for the cell, the returned value by the function, which extracts the filename from a fullpath).
context :
I had by the past (more than 12 years ago) developed, may be 50,000, VBA instructions, using office2003.
Today I have to develop again some macros with Office365. So I have forgotten a lot since this time and some features can have changed which can become tricking (I need to read again my old soft to recall all my knowledge, but I have no access to for now)
The problem
I get the error "#name" when I use a function created into VBA
associated to the current workbook. No explanation, no help, I tried to
find something during several hours and I found nothing.
I have developed too for testing (see code) a "sub" which calls the function, and his execution is successful, but...
I cannot run the function from the formula of any cell.
I have tested the security parameters of macro and fully unlocked the execution temporarily, and too declare the local directory as confident area.
Note : this code is detailed as an example
The code
Public Function FNameOf(CellPointed As Range)
Dim CurCell As Range
Dim Text1 As String
Dim Text2 As String
Set CurCell = CellPointed
Text1 = CurCell.Value
Text2 = Mid$(Text1, InStrRev(Text1, "\") + 1, Len(Text1))
FNameOf = Text2
End Function
Sub DispFileName2()
Dim style, disp, titre
Dim Cursel As Range
'Cursel = ActiveCell
disp = FNameOf(ActiveCell)
style = vbOKOnly
titre = "Nom du fichier extrait du texte (fullpath) de la cellule courante"
MsgBox disp, style, titre
End Sub
If I submit the macro DispFileName2 if get the message with the file name extracted from the path which is the content of the current cell
If I set the formule of a cell :
=FNameOf(AnotherCell) 'which contains a fullpath to a file
I get always the error "#NOM" (in French version) or, I think so, "#NAME" (in english Version) as if the function name (ref) was unknown from the workbook (the code is not reached, a stop is set on the first instruction)
What can be the reason ?
Please place your user defined function somewhere in a module (neither in "ThisWorkbook" nor in the individual worksheet's code, e. g. "Feuil1").
You should add the result declaration As String also.
I understood the example is for reference only, but you may shorten it:
Public Function FNameOf(CellPointed As Range) As String
FNameOf = Mid(CellPointed.Value, InStrRev(CellPointed.Value, "\") + 1)
End Function
I just got the solution while reading in more details previous edited threads in several tabs.
It is explained into a remark of : thread 12351339
The text is :
Microsoft Excel Objects such as 'Sheet1' or 'ThisWorkbook' are
classes. I don't believe you can access Functions which you put in
these classes through a cell. You could access them in VBA e.g.
ThisWorkbook.Square2() but it's recommended to put all UDF's into as
standard module and not a worksheet module. – Eddie Sep 28 '17 at
13:49
By default the creation panel defines code associated to current worksheet, then the function is not visible for the worksheet while the sub is a macro of the worksheet.
I have created a module and the function has run immediately.
Best regards
Trebly
Note : I never met this problem of visibility before because the developments where since the beginning concerning VBA user classes and modules combining multiple Excel workbooks and Word and a Mail manager activeX and so on...
I keep the subject because of the explanations, code and keywords may be to find more easily the solution for anybody else.

VBA Excel : Populate TextBox with specific string of text when Option Button is selected

I developed a Form in Excel (2016) and I am trying (with VBA) to configure its behavior so that if the user selects a particular option button, two additional things happen:
A checkbox further down on the form is automatically checked.
A text box further down on the form automatically displays a set string of text.
More specifically, if the user selects OptionButtonABC, then ...
CheckBoxNone should become checked
TextBoxCompanyName (which does not display any text by default) should now display the string: 'ABC'.
I initially created a subroutine that just targeted condition 1, and everything worked fine. However, when I try to integrate the code required to handle condition 2, things start to unravel.
Below, you will see the code in its most current state. I have a Private Sub that initiates upon the Click event and then immediately defines a variable as a string. I then set up an if/then statement that specifies what should happen IF OptionButtonABC is true. Namely, CheckBoxNone should be selected (this works fine) AND TextBoxCompanyName should now display the string 'ABC'.
Private Sub OptionButtonABC_Click()
Dim Variable_ABC As String
Variable_ABC = ABC
If Me.OptionButtonABC.Value = True Then
Me.CheckBoxNone = True And Me.TextBoxCompanyName.Text = Variable_ABC
End If
End Sub
The desired behavior should (theoretically) be pretty easy to achieve, but my experience with VBA is still pretty limited, so I am reaching out to the community for a bit of advice. As I mentioned above, the code above works for the first condition. However, I am still unable to get the string of text ('ABC') to show up in the text box.
Thanks in advance for any advice you may offer.
Private Sub OptionButtonABC_Click()
Dim Variable_ABC As String
Variable_ABC = "ABC" 'String Values uses double quotes
If Me.OptionButtonABC.Value = True Then
Me.CheckBoxNone = True
Me.TextBoxCompanyName.Text = Variable_ABC
End If
End Sub
The operator AND must be used only in the IF statement comparison, not in what you want to do.

String declaration error in if else

I have Camp as string. When I write this code, I get an error:
*Me.BoatDesc =< the expression you entered refer to an object that is close*
Here is my code
private Sub Save_Click()
Dim Camp As String
If Me.BoatDesc = "Camp" Then
Me.Amount = Me.Amount * 12
End If
Correct me if I am wrong.
You are using VBA, not VB.Net. Here are some notes
Here is a simple form, it will be open when the code is run. The code will be run by clicking Save. Note that the default for an MS Access bound form is to save, so you might like to use a different name.
This is the form in design view, note that there is a control named BoatDesc and another named Amount, as can only be seen from the property sheet.
The save button have an [Event Procedure], which is the code.
Note that the code belongs to Form2, the form I am working with, and the words Option Explicit appear at the top. This means I cannot have unnamed variables, so it is much harder to get the names wrong.
This is the code to be run by the save button.
Option Compare Database
Option Explicit
Private Sub Save_Click()
''Do not mix up strings, variables, controls and fields
''If you want to know if a control, BoatDesc, equals
''the string "camp", you do not need this
''Dim Camp As String
If Me.BoatDesc = "Camp" Then
Me.Amount = Me.Amount * 12
End If
End Sub

How to put a tooltip on a user-defined function

In Excel 2007, how do I add a description and parameter hints to a user-defined function? When I start typing a function invocation for a built-in function, Excel shows a description and parameter list--a tooltip. I'd like to do the same for the functions I define.
Not just for the formula insert wizard, but in the formula box, so if I key "=myFun(", at the "(" the tooltip pops up just like it does for "=average("
There's no help in VBA Help, none on MSDN and none on any of the Excel and VBA dedicated forums I can find, so this is clearly a long shot.
Not a tooltip solution but an adequate workaround:
Start typing the UDF =MyUDF( then press CTRL + Shift + A and your function parameters will be displayed. So long as those parameters have meaningful names you at-least have a viable prompt
For example, this:
=MyUDF( + CTRL + Shift + A
Turns into this:
=MyUDF(sPath, sFileName)
Professional Excel Development by
Stephen Bullen describes how to
register UDFs, which allows a
description to appear in the Function
Arguments dialog:
Function IFERROR(ByRef ToEvaluate As Variant, ByRef Default As Variant) As Variant
If IsError(ToEvaluate) Then
IFERROR = Default
Else
IFERROR = ToEvaluate
End If
End Function
Sub RegisterUDF()
Dim s As String
s = "Provides a shortcut replacement for the common worksheet construct" & vbLf _
& "IF(ISERROR(<expression>), <default>, <expression>)"
Application.MacroOptions macro:="IFERROR", Description:=s, Category:=9
End Sub
Sub UnregisterUDF()
Application.MacroOptions Macro:="IFERROR", Description:=Empty, Category:=Empty
End Sub
From: http://www.ozgrid.com/forum/showthread.php?t=78123&page=1
To show the Function Arguments dialog, type the function name and press CtrlA. Alternatively, click the "fx" symbol in the formula bar:
I know you've accepted an answer for this, but there's now a solution that lets you get an intellisense style completion box pop up like for the other excel functions, via an Excel-DNA add in, or by registering an intellisense server inside your own add in. See here.
Now, i prefer the C# way of doing it - it's much simpler, as inside Excel-DNA, any class that implements IExcelAddin is picked up by the addin framework and has AutoOpen() and AutoClose() run when you open/close the add in. So you just need this:
namespace MyNameSpace {
public class Intellisense : IExcelAddIn {
public void AutoClose() {
}
public void AutoOpen() {
IntelliSenseServer.Register();
}
}
}
and then (and this is just taken from the github page), you just need to use the ExcelDNA annotations on your functions:
[ExcelFunction(Description = "A useful test function that adds two numbers, and returns the sum.")]
public static double AddThem(
[ExcelArgument(Name = "Augend", Description = "is the first number, to which will be added")]
double v1,
[ExcelArgument(Name = "Addend", Description = "is the second number that will be added")]
double v2)
{
return v1 + v2;
}
which are annotated using the ExcelDNA annotations, the intellisense server will pick up the argument names and descriptions.
There are examples for using it with just VBA too, but i'm not too into my VBA, so i don't use those parts.
Also you can use, this Macro to assign Descriptions to arguments and the UDF:
Private Sub RegisterMyFunction()
Application.MacroOptions _
Macro:="SampleFunction", _ '' Your UDF name
Description:="calculates a result based on provided inputs", _
Category:="My UDF Category", _ '' Or use numbers, a list in the link below
ArgumentDescriptions:=Array( _ '' One by each argument
"is the first argument. tell the user what it does", _
"is the second argument. tell the user what it does")
End Sub
Credits to Kendall and the original post here.
For the UDF Categories
I just create a "help" version of the function. Shows up right below the function in autocomplete - the user can select it instead in an adjacent cell for instructions.
Public Function Foo(param1 as range, param2 as string) As String
Foo = "Hello world"
End Function
Public Function Foo_Help() as String
Foo_Help = "The Foo function was designed to return the Foo value for a specified range a cells given a specified constant." & CHR(10) & "Parameters:" & CHR(10)
& " param1 as Range : Specifies the range of cells the Foo function should operate on." & CHR(10)
&" param2 as String : Specifies the constant the function should use to calculate Foo"
&" contact the Foo master at master#foo.com for more information."
END FUNCTION
The carriage returns improve readability with wordwrap on. 2 birds with one stone, now the function has some documentation.
#will's method is the best. Just add few lines about the details for the people didn't use ExcelDNA before like me.
Download Excel-DNA IntelliSense from https://github.com/Excel-DNA/IntelliSense/releases
There are two version, one is for 64, check your Excel version. For my case, I'm using 64 version.
Open Excel/Developer/Add-Ins/Browse and select ExcelDna.IntelliSense64.xll.
Insert a new sheet, change name to "IntelliSense", add function description, as https://github.com/Excel-DNA/IntelliSense/wiki/Getting-Started
Then enjoy! :)
Unfortunately there is no way to add Tooltips for UDF Arguments.
To extend Remou's reply you can find a fuller but more complex approach to descriptions for the Function Wizard at
http://www.jkp-ads.com/Articles/RegisterUDF00.asp
I tried #ScottK's approach, first as a side feature of my functional UDF, then as a standalone _Help suffix version when I ran into trouble (see below). In hindsight, the latter approach is better anyway--more obvious to a user attentive enough to see a tool tip, and it doesn't clutter up the functional code.
I figured if an inattentive user just typed the function name and closed the parentheses while he thought it over, help would appear and he would be on his way. But dumping a bunch of text into a single cell that I cannot format didn't seem like a good idea. Instead, When the function is entered in a cell with no arguments i.e.
= interpolateLinear()
or
= interpolateLinear_Help()
a msgBox opens with the help text. A msgBox is limited to ~1000 characters, maybe it's 1024. But that's enough (barely 8^/) for my overly tricked out interpolation function. If it's not, you can always open a user form and go to town.
The first time the message box opened, it looked like success. But there are a couple of problems. First of course, the user has to know to enter the function with no arguments (+1 for the _Help suffix UDF).
The big problem is, the msgBox reopens several times in succession, spontaneously while working in unrelated parts of the workbook. Needless to say, it's very annoying. Sometimes it goes on until I get a circular reference warning. Go figure. If a UDF could change the cell formula, I would have done that to shut it up.
I don't know why Excel feels the need recalculate the formula over and over; neither the _Help standalone, nor the full up version (in help mode) has precedents or dependents. There's not an application.volatile statement anywhere. Of course the function returns a value to the calling cell. Maybe that triggers the recalc? But that's what UDFs do. I don't think you can not return a value.
Since you can't modify a worksheet formula from a UDF, I tried to return a specific string --a value --to the calling cell (the only one you can change the value of from a UDF), figuring I would inspect the cell value using application.caller on the next cycle, spot my string, and know not to re-display the help message. Seemed like a good idea at the time--didn't work. Maybe I did something stupid in my sleep-deprived state. I still like the idea. I'll update this when (if) I fix the problem. My quick fix was to add a line on the help box: "Seek help only in an emergency. Delete the offending formula to end the misery.
In the meantime, I tried the Application.MacroOptions approach. Pretty easy, and it looks professional. Just one problem to work out. I'll post a separate answer on that approach later.
A lot of dancing around the answer. You can add the UDF context help, but you have to export the Module and edit the contents in a text editor, then re-import it to VBA. Here's the example from Chip Pearson: Adding Code Attributes

Resources