Replacing Indirect with non-volatile VBA method, but cannot nest the returned value with Excel functions like OFFSET? - vba

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

Related

VBA String Function does not update when I change the text

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

troubles passing a discontinuous named range into a custom function

I've been looking around stack overflow for an answer to this for longer than I care to admit now.
Here's what I have: In a worksheet I have a bunch of discontinuous cells which I need to check for the existence of specific text. I've created a simple function to do this and can do this easily when I define that range manually (in code).
However, when I procedurally create a named range (while doing other stuff) and then try passing in the named range, the function never executes.
I know that the named range is being properly created because I have auto-formatting on it and also I can reference the range with excel formula which accept discontinuous ranges (SUM and whatnot).
Here's the pertinent portions of my code:
Function customProcess1(NamedRange As Range) As Long
For Each c in NamedRange.Cells
...
Next c
End Function
In Excel when I type the formula as "=customProcess1(A1:A2)" I get my number back after the function runs. When I type in "=customProcess1(NamedRange)" my function never even executes.
Again, I'm using the named range as defined already in the document. I can observe the name in the name manager, it references the appropriate cells, i can use the range in formula which accept non-continuous ranges, etc. I can't figure out how to get my working named range into my function.
When I put the formula as "=customProcess1("NamedRange")" the function executes, but since the named range is not ""NamedRange"" but is "NamedRange" it fails to set the object as Range (the object is not found). I've tried taking the named range as a string, but again, if I don't put the quotes around the name, it won't even run the function. So then I've tried passing in a string with the quotes and taking the quotes off inside the function, but this isn't exactly working well either.
In short, I just want to get my non-continuous named range in my custom function. Once I do that, everything is golden.
Anyone have any ideas? I'm not sure why this has been such a chore.
I'm not sure why what you're trying doesn't work and don't really have time to research that part of it, but you could do the following:
Function customProcess1(NamedRange As String) As Long
Dim TheRange As Range
Set TheRange = Range(NamedRange)
For Each c in TheRange.Cells
...
Next c
End Function
Hope this helps.
Adapting your UDF(), I coded:
Function customProcess1(NamedRange As Range) As Long
For Each c In NamedRange.Cells
customProcess1 = customProcess1 + c.Value
Next c
End Function
I then assigned the name Mike to the cells B6,C8,D10 and placed values in these cells. I then placed the formula:
=customProcess1(Mike)
in a cell and got the following:
NOTE:
I did not use =customProcess("Mike")

Excel VBA function works in Visual Basic, but fails in Worksheet

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.

VBA: How to get the last used cell by VBA code when the last error occured in a Workbook/Worksheet?

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

Create a custom worksheet function in Excel VBA

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