I'm starting with VBA and I'm very upset to discover I have fallen at the first hurdle in this book I am following.
I should type the following
Function Hello() As String
Hello = "Greetings"
End Function
This all works fine but next I am supposed to change the text and see the function change on the Excel spreadsheet. Unfortunately I can't get this to work. Does anyone know why?
I've saved the document as an Excel Macro-enabled workbook and tried opening and closing.
You seem to have this function in the code-file for thisWorkbook, but it should be in a module. Add a module to your project, place your code there and make the function Public. See also: How to Call VBA Function from Excel Cells?
Normally a Function should be in a Module, not in ThisWorkbook. You store Event handlers in the ThisWorkbook or a Sheet module.
It doesn't recalculate because it doesn't have a Range input, since the function doesn’t have any arguments, it is treated constant output and hence updating the function doesn’t update the cell value.
But if you modify it to accept a Range input, and if there are any changes to the input range, it will recalculate.
declare your function right
Function Hello(str As String) As String
then use something like
cells(1,1).value=str
function does the job for you but first you must call the function by parameter like this
cells(1,1).value=Hello("How are you")
the result will be that in cell 1,1 will "How are you" be written.
but from this on I am not sure what are you trying to accomplish. If you need funct. to write in specific cell all the time you should use something like
Funtion Hello(row as integer, column as integer) as string
cells(row,column).value=inputbox("give me the input")
end function
then to use this you write into code
result=Hello(1,2)
this example works
Function Area(row As Integer, column As Integer) As String
Cells(row, column).Value = InputBox("give something here")
End Function
Sub my()
result = Area(2, 2)
End Sub
run my()
Ok I worked it out, I had written the function into the file "This Workbook" instead of into the Module I created...
I knew it would be something simple! All working now.
Function Hello() As String
Application.Volatile
Hello = "Greetings"
End Function
Related
I have a follow-up question to this question.
So I want to replace INDIRECT function calls with a VBA method because INDIRECT is volatile and my excel doc is taking several seconds to load and sometimes is not responding.
But when I use the INDIRECTVBA method and nest it with an OFFSET function I get an error and it shows "#VALUE!"
(yes I know OFFSET is another volatile function, I will replace with INDEX..)
Specifically:
Cell BJ10 contains the text "$R$71" which is a reference to my cell holding the data.
=INDIRECT($BJ$10) works but is volatile.
=INDIRECTVBA($BJ$10) works.
=(OFFSET(INDIRECT($BJ$10),0,0)) works but is doubly volatile.
=(OFFSET(INDIRECTVBA($BJ$10),0,0)) does not calculate, it shows "#VALUE!"
Any thoughts?
Here is the INDIRECTVBA method:
Public Function INDIRECTVBA(ref_text As String)
INDIRECTVBA = Range(ref_text)
End Function
Public Sub FullCalc()
Application.CalculateFull
End Sub
Your function doesn't return a range, so it fails as the first argument to OFFSET() (which requires a range in that position).
Also, your function will fail when any other sheet is active (assuming it's in a standard module), because the scope of Range() defaults to the ActiveSheet.
Try something like:
Public Function INDIRECTVBA(ref_text As String)
Set INDIRECTVBA = Application.ThisCell.Parent.Range(ref_text)
End Function
If everything's not on the same sheet then you will need some way to specify which sheet should be used in your UDF
I want to retrieve the value of a worksheet-code-level private constant via VBA. I'm trying to do something similar to having "tags" on my worksheets that are accessible via VBA. I'd like to use the CodeModule in the VBAProject so that I can have a number of "tags", not just one. I just can't figure out how to grab the constant, even after reviewing much code and forums on-line. Does anyone have any insight on this? I am running Excel 2013 on a Windows 8.1 machine.
Maybe the easiest way to do this is to have a function that can the value of a private constant with a public accessor method. For instance, if you have the following code in your worksheet Sheet1:
Private Const myValue = "the answer is 42"
Public Function getSecret()
getSecret = myValue
End Function
Then you can access it from another module with
Sub test()
Dim sheetName = "Sheet1"
MsgBox "The sheet says that " & Sheets(sheetName).getSecret()
End Sub
You can make this fancier - you could create a collection of tags and index them… but I think that goes beyond the question you had. Note that you have to "fully qualify" the name of the accessor macro when it is in a worksheet; the advantage is that you can use the same function to examine the tag of different worksheets just by changing the sheetName.
I would export the code to a .BAS file. You can then read the material back in as a text file and retrieve the constant
I'm trying to build a 2D array of data using "CurrentRegion".
Function ProcessData()
Dim dataList()
dataList = Range("A1").CurrentRegion
' TODO Process the dataList
End Function
When I test this within Visual Basic (Run/F5), it works great; my dataList is built with no problems. However, if I set a cell in my worksheet to:
= ProcessData()
the function silently fails at the "CurrentRegion" step. Why does this happen? How do I remedy it?
If you call a Function from an Excel cell (i.e. as an User-Defined-Function/UDF), you can only access the ranges that are handed to the function via parameters. Any access to other ranges (and .CurrentRegion is a range) will result in a "Circular Reference" potential cancellation of the execution.
Also, in a UDF you cannot modify anything on the worksheet - but only return the result of function!
For further details, check out this link.
I've just encountered this Q&A having the same problem. I think there is a kind of bug for using CurrentRegion within UDF and the reason is not as Peter suggest in his answer.
Compare these two function:
Function GetAddressOfColumn(TopCell As Range)
GetAddressOfColumn = TopCell.CurrentRegion.Address
End Function
Function GetAddressOfColumnOK(TopCell As Range)
GetAddressOfColumnOK = Range(TopCell, TopCell.End(xlDown)).Address
End Function
Both function use different kind of range references. If Peter were right both should return false result. So, look at the data range below and the result of both function. As you can see second function result is as expected and correct.
Eventually, I want to move the cell to the location where the last error occured. Edit: Forgot to say that I'm using Excel 2003.
As requested in comments...
Look up the 'Caller' property of the 'Application' object in the Excel VBA help. When you use it from a VBA routine, it will tell you where the call to the routine came from - what Range, Chart, etc.
An important thing to be aware of when using 'Application.Caller' is that it isn't always a Range object. Look at the help, but the property returns a Variant value that can be a Range, String, or Error. (It is a Range object in the case you're interested in, but you'll need to be aware of this.)
Because of the above, and the vagaries of VBA syntax when it comes to objects vs. values, it can be tricky to use 'Application.Caller'. Putting a line like:
Debug.Print Application.Caller.Address
in your code will fail when the caller isn't a Range. Doing something like:
Dim v
v = Application.Caller
will "compile", but will create circular references when the caller is a Range because you're trying to access the value of the calling Range.
This all means that it's probably best to write a little utility function for yourself:
Public Function currentCaller() As String
If TypeOf Application.Caller Is Range Then
Dim rng As Range
Set rng = Application.Caller
currentCaller = rng.Address(External:=True)
Else
currentCaller = CStr(Application.Caller)
End If
End Function
and then call it from your error handlers where you want to know where the call came from.
One more thing - obviously this can only tell you the caller once a VBA routine has actually been called. If you have errors in your calling formulas, Excel will return error values to your cells without ever calling your VBA routines.
Wrap your VBA function in another function that stores the cell location and value as variants. Keep this 'wrapper' function as basic as possible so it won't cause any additional errors.
If you're trying to debug app-crashing errors, the wrapper function could even store those values in a comma-delimited text file. Once stored, Excel can crash all it wants and you'll still know what the cell location and value were since you stored them outside of Excel beforehand.
Could this be done with an error handler?
An example of what I mean below:
sub code1()
on error goto cell A1
end sub
I have a faint memory of being able to use VBA functions to calculate values in Excel, like this (as the cell formula):
=MyCustomFunction(A3)
Can this be done?
EDIT:
This is my VBA function signature:
Public Function MyCustomFunction(str As String) As String
The function sits in the ThisWorkbook module. If I try to use it in the worksheet as shown above, I get the #NAME? error.
Solution (Thanks, codeape): The function is not accessible when it is defined ThisWorkbook module. It must be in a "proper" module, one that has been added manually to the workbook.
Yes it can. You simply define a VBA function in a module. See http://www.vertex42.com/ExcelArticles/user-defined-functions.html for a nice introduction with examples.
Here's a simple example:
Create a new workbook
Switch to VBA view (Alt-F11)
Insert a module: Insert | Module
Module contents:
Option Explicit
Function MyCustomFunction(input)
MyCustomFunction = 42 + input
End Function
Switch back to worksheet (Alt-F11), and enter some values:
A1: 2
A2: =MyCustomFunction(A1)
The word input needs to be replaced as it is a basic keyword. Try num instead. You can also go further by specifying a type, eg variant.
Function MyCustomFunction(num As Variant)
MyCustomFunction = 42 + num
End Function