I wrote a UDF that takes as optional parameter a range. It then calculates the last row of the range being used. If the range is not passed as an argument, the UDF should default to the full worksheet of the calling cell. Somehow, the straigthforward approach didn't work - the UDF Returns 0.
Public Function hrLastRow(Optional r As Range = Nothing) As Long
If r Is Nothing Then
hrLastRow = hrLastRow(ThisWorkbook.Worksheets(Application.Caller.Parent.Name).Cells())
Else
If Application.WorksheetFunction.CountA(r) <> 0 Then
hrLastRow = 1 - r.Rows(1).row + _
r.Find(What:="*", _
After:=r.Cells(1, 1), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).row
hrLastRow = Application.WorksheetFunction.Max(hrLastRow, 0)
Else
hrLastRow = 0
End If
End If
End Function
As I said, if I call this in a cell "=hrLastRow()" the result is zero, despite there being values all over the place :) - so the result should definitely be positive.
Maybe I don't properly use Application.Caller...? Any help would be much appreciated. Or does VBA not allow for the recursive call somehow? Then, why zero?
PS: Works fine on ranges.
PPS: I just noticed, that Excel warns about a circular reference in the calling cell. Maybe this is the underlying issue - but then, how to get around it?
Update: So the target is, to get this working without side effects, like iterative calculation. Someone suggested to just search below the UDF caller to avoidthe circular reference, which sounds like a clever idea to me, simple and on target. Somehow, my code for this seems Buggy though, the results are just off... Here's the current state of the UDF. Just look at the If r is nothing part:
Public Function hrLastRow(Optional r As Range = Nothing) As Variant
If r Is Nothing Then
Dim callerRow As Long
Dim callerWS As Worksheet
Dim searchRange As Range
Set callerWS = Application.Caller.Parent
callerRow = Range(Application.Caller.Address).row
With callerWS
Set searchRange = .Range(.Cells(callerRow + 1, 1), .Cells(.UsedRange.row, .UsedRange.column))
hrLastRow = searchRange.Find(What:="*", _
After:=searchRange.Cells(1, 1), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).row
End With
Else
If Application.WorksheetFunction.CountA(r) <> 0 Then
hrLastRow = 1 - r.Rows(1).row + _
r.Find(What:="*", _
After:=r.Cells(1, 1), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).row
hrLastRow = Application.WorksheetFunction.Max(hrLastRow, 0)
Else
hrLastRow = 0
End If
End If
End Function
The else part of the outer if Statement works fine.