Excel: Passing the current cell as argument when calling vba function from worksheet - vba

I'm trying to make a VBA function that accepts a cell as argument and works from there using a variety of Range.Offset. This function will be called in the worksheet cells. For testing I'm using this simple script:
Public Function testPublic(targetCell As Range) As Boolean
targetCell.Offset(0, 3).Value2 = "Test is successful!"
testPublic = True
End Function
To see if I can get the cell reference to work, I pass simple references such as C5, but I only get #VALUE! error. Not sure what's wrong with this.
Tried to change Range as Variant, still doesn't work

You cannot use a function called as a UDF from a worksheet to update another cell in the worksheet: the function can only return a value to the cell containing the function (an array formula can return multiple values, but again only to the cells where the formula was entered).
For more information see: https://support.microsoft.com/en-us/topic/description-of-limitations-of-custom-functions-in-excel-f2f0ce5d-8ea5-6ce7-fddc-79d36192b7a1

Try below calling code
If testPublic(targetCell:=Range1) Then
MsgBox "success"
End If
The function code is as below. Change for ByRef and ByVal as per your logic.
Public Function testPublic(ByRef targetCell As Range) As Boolean
targetCell.Offset(0, 3).Value2 = "Test is successful!"
testPublic = True
End Function

Related

User Defined function not recognized

I've written a function in VBA that modifies the current VLOOKUP to return nothing instead of #N/A when a value is not found. The function works fine when I have it saved in a module in my active workbook but when I move it to my .xlam project the output is always #NAME?. I should also note that autocomplete does find the function when I begin typing it in a cell.
Here is the code I have so far:
Function NAVLOOKUP(val As Variant, rng As Range, ofst As Integer) As Variant
Dim temp As Variant
On Error Resume Next
temp = Application.WorksheetFunction.VLookup(val, rng, ofst, False)
If IsEmpty(temp) Then
NAVLOOKUP = ""
Else
NAVLOOKUP = temp
End If
End Function
It's pretty straightforward so I'm not sure what the problem is. I also made a dummy function in the same .xlam project that just adds an "S" to any value and it has been working with no problem.
Function ADDS(val As Variant) As String
ADDS = val & "S"
End Function
Since the second function works I am at a loss here. Any help would be appreciated.

Count visible blank cells using VBA?

When I enter the following function as a UDF in a cell:
Function VisibleBlankCells(r As Range) As Long
On Error Resume Next
VisibleBlankCells = Intersect(r.SpecialCells(xlCellTypeVisible), r.SpecialCells(xlCellTypeBlanks)).Count
On Error GoTo 0
End Function
r.SpecialCells(xlCellTypeBlanks) evaluates ALL cells in r as empty regardless of whether they contain text or not. What might be the cause of this and an alternative solution?
Get rid of the On Error Resume Next for a start - you should always assume that your code will fail and account for it accordingly, simply ignoring errors will just complicate matters.
Secondly ,there is no need to use Intersect - just identify the visible cells directly, and then use a further SpecialCells() method to identify the blank child cells.
Function VisibleBlankCells(r As Range) As Long
VisibleBlankCells = r.SpecialCells(xlCellTypeVisible).SpecialCells(xlCellTypeBlanks).Count
End Function
tested with this:
Sub test_code()
Dim r As Range: Set r = Selection
Debug.Print CountBlanks(r)
End Sub
Function CountBlanks(r As Range) As Long
CountBlanks = r.SpecialCells(xlCellTypeVisible).SpecialCells(xlCellTypeBlanks).Count
End Function
This kind of filter mechanism won't work in an UDF (see this for information on that). I suggest a looping inside your UDF:
Public Function VisibleBlankCells(rng As Range) As Long
Dim i As Integer
Dim cell As Range
i = 0
For Each cell In rng
If cell.Rows.Hidden = False And _
cell.Columns.Hidden = False And _
cell.Value = "" Then
i = i + 1
End If
Next
VisibleBlankCells = i
End Function
However, there may be some problems regarding the updating and functionality:
The value of the UDF only updates after editing the referenced range or calling other UDFs. So if you hide a column or row in that range, it won't have an instant effect
In the (working) execution of your code in a Sub, the visible cells (also) refer to yet unused cells in your worksheet to be "not visible". In my solution however, all cells that are not contained in a hidden row/column are considered visible.

How to prevent VBA function from re-executing inside the code

I'm calling VBA function from an Excel worksheet. When I change a cell inside the code of the VBA function, Excel tries to re-execute that function again (and again in the second iteration and ...)
Example: if you have the code:
Function test() As Variant
Range("A1") = 1
test = "test"
End Function
When you use "=test()" anywhere, it will return #VALUE!. A debug will show that when you update A1, it will try to re-execute test().
Can you prevent Excel from doing this? E.g. saying 'don't update any of my numbers until I'm done with this function'? I've tried the Application.Calculation flag, or doing some external concurrency checks, but that doesn't seem to work ...
Try:
Function test() as Variant
test="test"
End Function
As Gary's student says in his comment: UDF's should only change worksheet entries of the cells that call the function, and this value should be returned as a result of the function, not by changing a cell value of an explicit address.
In other words, this does not work:
Function test() As Variant
Range("A1") = 1
test = "test"
End Function
Instead call test() from cell A1 and do this:
Function test() As Variant
test = 1
End Function
If the desired behaviour is to edit multiple cells (the return value of the function AND another cell), this should be implemented through a sub rather than a function.
To answer your specific question of how to stop the Function re-executing when you change A1 or your function, and stop it returning #Value: 1. Use the Excel user interface (Formulas--> calculation options) to change calculation mode to manual 2. add some error trapping to your function like this.
Function test() As Variant
On Error Resume Next
Range("A1") = 1
test = "test"
End Function
as has already been said the function will not change the value of A1

Capture the value of multiple cell range in a custom formulaarray

I've written my own function in VBA to do some things.
Parameters are Variant.
Private Function ProcessedValuesEx(pValues As Variant)
The problem occurs when I select a multiple cell range as my function parameter.
When it executes, the parameter has no value. I'm not able to see any content. Even with the VBA inspector.
When selection only one cell there is no problem. The parameter gets the content of the cell.
Perhaps set the argument as a range, and then loop through each cell in the range, like so:
Private Function ProcessedValuesEx(ByVal rValues As Range)
Dim pValue As Range
For Each pValue In rValues.Cells
'Do something with pValue here
Next pValue
End Function

Method Back Reference to calling Worksheet/Cell

I have a method to execute a Distinct Result from a Table Column on a Worksheet.
The result of the Method will go into a Data-Validation listbox in a cell. I have two needs right now that require me to "dummy mitigate" the method's use, by limiting the number of columns passed to the method by one. This part i can get done, what i would like to do is have it so that if there are multiple columns in the Range, then it "pukes" on the user, stating that an illegal function call was made from "Worksheet"."Cell" and to alert the IT Support to resolve the problem.
Getting the Worksheet is great and the easy part. Its more of getting the reference to the Calling Cell without explicitely forcing the IT Support to pass the Cell as a value to the method.
Any ideas on how to extract the Calling Cell?
Ultimately this method will be used across several worksheets to perform the same logic with different ranges being passed to it.
Edited 2012-09-24 10:30am CST
Here is my implementation so far, havent utilized the Application.Caller method into it.
Function UniqueValues(ws As Worksheet, col As String) As Variant
Dim rng As Range: Set rng = ws.Range(col)
Dim dict As New Scripting.Dictionary
If Not (rng Is Nothing) Then
Dim cell As Range, val As String
For Each cell In rng.Cells
val = CStr(cell.Value)
If Not dict.Exists(val) Then
dict.Add val, val
End If
Next cell
End If
'Return value
UniqueValues = dict.Items
End Function
This method is already being used in code-behind logic. I will be copying this logic and making it apart of the Application.Volatile segment for the Excel Workbook i am working on. Will be similar but not the same.
Here is the current design, not much but what i am workgin with atm.
Function DistinctValues(rng As Range)
Application.Volatile True
If rng.Columns.Count > 1 Then
Err.Raise -1542376, rng.Worksheet.name, "An invalid function call was made!"
End If
End Function
Application.Caller returns the cell that called a function. See this MSDN definition.