I hope I'm asking this in the correct forum:
I'm writing a UDF in VBA for MS-Excel; it basically builds a status message for the transaction on that row. It steps through a series of IF statements, evaluating cell values in different columns FOR THAT ROW.
However, this UDF will reside in multiple rows. So it might be in C12, C13, C14, etc. How would the UDF know which row to use? I'm trying something like this, to no effect
Tmp_Row = Application.Evaluate("Row()")
which appears to return a null
What am I missing here ?
Thanking everyone in advance
Application.Caller is seldom used, but when a UDF needs to know who called it, it needs to know about Application.Caller.
Except, you cannot just assume that a function was invoked from a Range. So you should validate its type using the TypeOf...Is operator:
Dim CallingCell As Excel.Range
If TypeOf Application.Caller Is Excel.Range Then
'Caller is a range, so this assignment is safe:
Set CallingCell = Application.Caller
End If
If CallingCell Is Nothing Then
'function wasn't called from a cell, now what?
Else
'working row is CallingCell.Row
End If
Suggestion: make the function take its dependent cells as Range parameters (if you need the Range metadata; if you only need the values then take in Double, Date, String parameters instead) instead of making it fetch values from the sheet. This decouples the worksheet layout from the function's logic, which in turn makes it much more flexible and easier to work with - and won't need any tweaks if/when the worksheet layout changes.
Application.ThisCell
MS Docs:
Returns the cell in which the user-defined function is being called from as a Range object.
You can put it to the test using the following code:
Function testTC()
testTC = Application.ThisCell.Row
End Function
In Excel use the formula
=testTC()
and (Cut)Copy/Paste to various cells.
Related
I'm not sure if I need a sub or a function here, but I need something that can:
Be called from a cell in Excel
Can print values into other cells in the same worksheet
For eg.
Function PrintDemo(CellRef As Range)
Set CellRef = ActiveCell.CurrentRegion
Dim i as Integer
CellRef.Offset( , 5).Value = i
PrintDemo = 3
End Function
If I try to call this using =PrintDemo(D1) in an excel cell, hoping to print '123' 5 cells to the right, it just shows VALUE error. The function doesn't actually need to return anything, I made it hoping it would solve the error (it didn't). Nothing gets printed.
I haven't used VBA much (definitely not for printing) and I'm sure I'm missing something very fundamental in terms of how things are called. Please help me out.
EDIT: Makes sense that I can't change other cells, can I store my result as an array somewhere and have a range of cells access its values one at a time?
eg. outputArr=[1,2,3,4,5]
A2 -> =outputArr(0)
B2 -> =outputArr(1) and so on
It'll actually be 2D array but I don't think that should make much of a difference. The reason I need formulae everywhere is because this has to run in many places in the sheet, hence can't have any manual steps.
As the subject says, I need a function to return the cell background color. My vba function is
Public Function ReturnedBackGroundColor(rnge As Range) As Integer
ReturnedBackGroundColor = rnge.Offset(0, 0).Interior.Color
End Function
In the cell, I have
=IF(ReturnedBackGroundColor("G9") =3,1,2)
and I get a #VALUE! error. I have searched for a couple of hours off and on this morning and get no joy.
I have tried putting the function in both the sheet code and a module. I have tried Color and ColorIndex. I have tried 3 and vbRed (which BTW isn't recognized by VBA for reasons that I have forgotten. I have tried making the function public in both the sheet code and a module. I have tried recalculating the sheet in both cases.
What I suspect is happening is that I have to set something up in order to make the connection between the two for both the vbred and the function to the sheet.
This is the main issue with these forums. People put up working code but they don't tell you the shtuff that has to go around the code in order for it to work. And that is most likely what I am missing here.
Remove .Offset(0, 0), return a Long (your code is fine otherwise), and give it a Range instead of a String:
=IF(ReturnedBackGroundColor(G9)=3,1,2)
G9 being the actual cell reference, rather than a string literal containing a cell address.
That will be more reliable than building the Range from the string inside the function, since when you're invoking it from VBA code you will not want to assume what sheet is currently active.
Minor changes:
Public Function ReturnedBackGroundColor(rngeADDY As String) As Long
ReturnedBackGroundColor = Range(rngeADDY).Interior.Color
End Function
we:
pass a string
return a long
You can choose to pass either a String or a Range. But there should be consistency between the usage in the worksheet cell and the VBA coding.
Note:
The usual choice is to pass a Range as this helps to establish the correct level of volatility.
I have written a simple VBA function to look up a value by key in a long table of key/value pairs. It then returns that value as a Double:
Public Function GetValue(sheet As String, key As String) As Double
Dim WS As Worksheet
Dim K As Range
GetValue = 0
Set WS = ThisWorkbook.Worksheets(sheet)
If WS Is Nothing Then Exit Function
Set K = FindKeyAnywhere(WS, key)
If K Is Nothing Then Exit Function
GetValue = WS.Cells(K.Row, 5).Value
End Function
I have about 60 of these formulas in a summary sheet:
=GetValue("Data", B$41)
Where "Data" is the name of the page with key/values, and B$41 is a key. Everything was working perfectly, then...
I fat-fingered the VBA, changing the = 0 to = o and calced. Now every cell on the summary still said #VALUE. When I realized what happened, I fixed the error and recalced. However, every cell on the summary still said #VALUE.
If I click in the cell and hit return, it updated fine.
I checked, autocalculate is turned on. I also clicked calc sheet several times. Nothing.
So I went to every single cell and hit return, so they were all updated. Now they don't say #VALUE, but they still don't update when I change data on the data page.
Is there anything special I'm missing? It seems like Excel is "stuck" thinking the calculation isn't valid.
UPDATE:
Using a named range does not work well, because it has to be typed into every formula. Consider...
KEY1 KEY2 ANOTHERKEY
Data1 =GetValue(A$1,B$1)
Data2
When the user CnPs or drag-fills that formula, it will get the key and sheet automatically. If we use a range name instead, they would have to type in the name in every single cell, and there's hundreds.
Is there a way to take a string and return the named range? Like =Range(A1)
As John Coleman says: Excel does not know it needs to recalculate when something on the Data sheet changes because the precedents of your UDF do not include the range od information on the Data sheet.
You need to:
Either Make the function Volatile - but this has bad recalculation
consequences.
Or pass the range on the Data sheet that contains the information
instead of passing a string. This is the best solution.
Based on your update I think you are looking for INDIRECT, which can convert a string to a range.
But there are major downsides to INDIRECT: its volatile, single-threaded and behaves badly when the string the user gives it does not exist.
IMHO this is not a good direction to go: I would recommend you consider a redesign of your data/algorithms.
I have an Excel cell with a complicated formula relating to many other cells.
Does a function exist that would allow me to look at what the value of that cell would be if I changed the value of another cell to a certain value?
Basically, I'm looking for the functionality of a simple data table in a function I can put directly in a cell. I can do this in VBA or by creating a data table, but I'm looking for a built-in method if one exists.
No.
As you have mentioned, you would be best served using a data table - there isn't an equivalent inbuilt function. The plus with data tables is that you can explicitly control their calculation
#brettdj's answer is correct. Unfortunately, I was wrong that I could do what I want even in VBA; the code below was what I was planning to write, but it does not work because functions called from a worksheet cannot change the worksheet:
Public Function Hypothetical(setthiscell As Range, tothisvalue As Variant, andobservethiscell As Range) As Variant
Dim oldvalue As Variant
oldvalue = setthiscell.Value
setthiscell.Value = tothisvalue
Dim result As Variant
result = andobservethiscell.Value
setthiscell.Value = oldvalue
Hypothetical = result
End Function
Neither Scenarios or the Solver address my problem because I want to the solution to be automated. Apparently the only way to accomplish this task is to use data tables.
I'm trying to create some conditional formatting at runtime (huzzah) for an add-in (double huzzah), and have found that, apparently, some functions cannot be used as they would in a normal worksheet. (I just get an invalid procedure call error 5 when trying to create the CF referencing a VBA function I could call in a cell, even though it's in the add-in and not the workbook; I can create the CF fine with a built-in function.) The clearest confirmation I've found for this is here, but it doesn't really explain what the problem is; that's the esoteric part, would love to hear more about what I can expect with this.
The rubber-meets-road part is: can I avoid VBA altogether, and use a series of Excel-only, built-in functions to verify whether a given cell contains a constant (i.e. a value entered by a user), a formula (i.e. some kind of calculation, logical operation, etc.--pretty much starts with an =), or a link (i.e. a reference to a cell in another worksheet or another workbook)? I know Excel has this determination at its fingertips; witness the uses and speed of GoTo/Special. How can I get at it though?
Thanks in advance for your help.
Updated for Excel 2013:
For Office versions 2013 and higher, the ISFORMULA¹ function is available. Combining this with the NOT function, AND function and either the COUNTBLANK, ISBLANK or LEN function can produce a formula to determine whether a cell contains a constant.
The standard formulas in E2:F2 are,
=ISFORMULA(D2)
=AND(NOT(ISFORMULA(D2)), LEN(D2))
If further information on the nature of the cell value is required the TYPE function can be used to determine if the cell contents are a number, text, boolean, error or array.
When used in concert the native worksheet functions discussed here can reproduce the results available from VBA's Range.SpecialCells method and its xlCellTypeConstants or xlCellTypeFormulas xlCellType enumeration.
¹ The ISFORMULA function was introduced with Excel 2013. It is not available in earlier versions.
excel-2013
I don't think you can avoid VBA altogether but you can create a simple UDF and use it within Excel
Eg
Function IsFormula(Check_Cell As Range)
IsFormula = Check_Cell.HasFormula
End Function
and
Function IsLink(Check_Cell As Range)
If InStr(1, Check_Cell.Formula, "!", vbTextCompare) Then
IsLink = Check_Cell.HasFormula
End If
End Function
=IsFormula(A1) will return TRUE if there is a formula in A1 and FALSE otherwise
=IsLink(A1) will return TRUE if there is a formula in A1 containing '!' otherwise FALSE
You could combine these and create a string output "Formula","Link","Value"
Not sure if this is what you want, but it seems to do what you are asking, at least some of it.
http://www.ozgrid.com/VBA/special-cells.htm
It's the range.specialcells method.
It returns a range that contains only constants, or only formulas, etc.
An example of how this code would be used is shown below:
Sub CheckForConstants()
Dim x As Range
Set x = Selection.SpecialCells(xlCellTypeConstants, xlNumbers)
MsgBox "address of cells that contain numbers only is " & x.Address
Set x = Selection.SpecialCells(xlCellTypeConstants)
MsgBox "address of cells that contain constant of any type is " & x.Address
End Sub
You select a range and then execute this macro and it will return the address of those cells that meet the requirements.
The first x looks for cells that contains numbers only.
The second x looks for cells that contains any constants
The range in this case was selection, but you can set to what you want, i.e. range("a1:b5"), etc.
I went back to the worksheet and used the goto special method.
Apparently it also uses the range.special method.
I used the record macro option and this is what I got.
Selection.SpecialCells(xlCellTypeConstants, 23).Select
Range("M7").Select
Selection.SpecialCells(xlCellTypeFormulas, 23).Select
Range("I6:J16").Select
Selection.SpecialCells(xlCellTypeConstants, 1).Select
Range("L9").Select
ActiveWindow.ScrollWorkbookTabs Position:=xlFirst
Sheets("CP").Select
Application.CutCopyMode = False
Range("I21").Select
ActiveSheet.DrawingObjects.Select
Application.Goto Reference:="GoToSpecialRoutine"
The goto special feature on the worksheet uses the special cells method for some of what it does.
It also uses others as well. In the last 5 lines of codes I changed worksheet and asked it to go to objects.
It doesn't really go to them. It just selects them.
worksheet CP contained objects and it used the code in the last 3 lines to select all the objects on the worksheet.
Best bet to see the code behind goto special is to record a macro and then use the goto / special feature in the worksheet.
When finished, Stop recording and view the macro you recorded.
I don't know of any other features to select by type of cell, but I'm just a newby so it could be there very easily and not be known by me.