UDF gives #Value Error in Spreadsheet

ebraatz

Board Regular
Joined
Feb 25, 2017
Messages
76
I'm making a workbook to track student attendance for the year. There is an identical tab for each month of the year which has a linear calendar that will change depending on the year chosen. At the end of the month I tally the number of Absences, etc for a given term. The terms begin and end in the middle of a month, so i wrote a UDF, Function CountPeriod1, to determine the range to use for CountIf based on the given date range and the start and end dates of the period. The function works perfectly when run with a test sub, but when I use it in the worksheet I get a #Value error. I've read many forums about this problem but none of those situations help me. The Function code is:

Code:
Function CountPeriod1(StartDate As Range, EndDate As Range, DateRange As Range, _
                        CountRange As Range, CountWhat1 As Variant, Optional CountWhat2 As Variant, _
                        Optional CountWhat3 As Variant) As Long
                        
    Dim FirstDate As Range
    Dim LastDate As Range
    Dim MatchED As Range
    Dim MatchSD As Range
    Dim PeriodRange As Range
    
    Dim m As Long
    Dim r As Long
           
    
    If DateRange.Rows.Count > 1 Then
        MsgBox "DateRange must be a singular row"
    End If
    
    Set FirstDate = Cells(DateRange.Row, DateRange.Columns(1).Column)                               'First day of selected DateRange
    Set LastDate = DateRange.SpecialCells(xlCellTypeLastCell)           'Last day of selected DateRange
    
    If IsEmpty(LastDate) = True Then
        m = DateRange.Find(What:="*", LookIn:=xlValues, searchorder:=xlByColumns, searchdirection:=xlByColumns).Column
                
        Set LastDate = Cells(DateRange.Row, m)
    End If
    
    'Find where the Period starts and ends in relation to the selected DateRange
    Select Case StartDate.Value
        
        Case Is < FirstDate.Value               'Period begins before the selected DateRange begins
            
            Select Case EndDate.Value
                    
                Case Is > FirstDate.Value       'Period Ends after selected DateRange begins
                    
                    Select Case EndDate.Value
                            
                        Case Is > LastDate.Value    'Period starts before and ends after selected DateRange
                                
                            Set PeriodRange = CountRange
                                
                        Case Is < LastDate.Value    'Period begins before daterange starts, but ends before DateRange ends
                                
                            m = Application.Match(CLng(CDate(EndDate.Value)), DateRange, 0)
                            Set MatchED = Cells(DateRange.Row, DateRange.Columns(m).Column)
                                
                            Set PeriodRange = Range(Cells(CountRange.Row, FirstDate.Column), Cells(CountRange.Row, MatchED.Column))
                             
                        Case Is = LastDate.Value    'Period begins before daterange starts and ends on last day of DateRange
                                
                            Set PeriodRange = CountRange
                                
                    End Select
                
                Case Is < FirstDate.Value   'Period Ends before selected daterange begins
                    
                    CountPeriod1 = 0
                    
                Case Is = FirstDate.Value   'Period ends on first day of Daterange
            
                    Set PeriodRange = Cells(CountRange.Row, FirstDate.Column)
                        
            End Select
            
        Case Is > FirstDate.Value               'Period begins after the selected DateRange begins
        
            Select Case StartDate.Value
            
                Case Is > LastDate.Value        'Period begins after daterange ends
                
                    CountPeriod1 = 0
            
                Case Is < LastDate.Value        'Period begins after start, but before end of DateRange
                
                    m = Application.Match(CLng(CDate(StartDate.Value)), DateRange, 0)
                    Set MatchSD = Cells(DateRange.Row, DateRange.Columns(m).Column)
                    
                    Select Case EndDate.Value
                        
                        Case Is < LastDate.Value    'Period ends before the DateRange Ends - Period is contained entirely within daterange
                                                
                            m = Application.Match(CLng(CDate(LastDate.Value)), DateRange, 0)
                            Set MatchED = Cells(DateRange.Row, DateRange.Columns(m).Column)
                            
                            Set PeriodRange = Range(Cells(CountRange.Row, MatchSD.Column), Cells(CountRange.Row, MatchED.Column))
                        
                        Case Is > LastDate.Value    'period ends after DateRange Ends
                            
                            Set PeriodRange = Range(Cells(CountRange.Row, MatchSD.Column), Cells(CountRange.Row, LastDate.Column))
                                                    
                        Case Is = LastDate.Value    'Period ends on last day of date range
                        
                            Set PeriodRange = Range(Cells(CountRange.Row, MatchSD.Column), Cells(CountRange.Row, LastDate.Column))
                        
                    End Select
                
                Case Is = LastDate.Value        'Period begins on last day of DateRange
                    
                    Set PeriodRange = Cells(CountRange.Row, LastDate.Column)
                
            End Select
            
        Case Is = FirstDate.Value               'Period begins on the first day of selected DateRange
        
            Select Case EndDate.Value
            
                Case Is < LastDate.Value        'Period ends before last day of daterange
                
                    m = Application.Match(CLng(CDate(EndDate.Value)), DateRange, 0)
                    Set MatchED = Cells(DateRange.Row, DateRange.Columns(m).Column)
                    
                    Set PeriodRange = Range(Cells(CountRange.Row, FirstDate.Column), Cells(CountRange.Row, MatchED.Column))
                                    
                Case Is > LastDate.Value        'Period ends after last day of daterange
                
                    Set PeriodRange = CountRange
                
                Case Is = LastDate.Value        'period ends on last day of daterange
                
                    Set PeriodRange = CountRange
                
            End Select
    End Select
    
    If IsMissing(CountWhat3) = True Then
        If IsMissing(CountWhat2) = True Then
            CountPeriod1 = Application.WorksheetFunction.CountIf(PeriodRange, CountWhat1)
            Else:
                CountPeriod1 = Application.WorksheetFunction.CountIf(PeriodRange, CountWhat1) _
                                + Application.WorksheetFunction.CountIf(PeriodRange, CountWhat2)
        End If
        Else:
            CountPeriod1 = Application.WorksheetFunction.CountIf(PeriodRange, CountWhat1) _
                                + Application.WorksheetFunction.CountIf(PeriodRange, CountWhat2) _
                                + Application.WorksheetFunction.CountIf(PeriodRange, CountWhat3)
                                
    End If
    
    
                       
End Function

The function is entered into the cell as:

=CountPeriod1(FirstDayPK, LastDayP1,AugSeptDates,E7:AH7, "A")

TIA!!
 
Last edited:
You would need something like ...

Code:
Set LastDate = DateRange.Worksheet.Cells(DateRange.Row, m)

So I changed all these references. I also added a msgbox here:

Code:
If IsEmpty(LastDate) = True Then
        m = DateRange.Find(What:="*", LookIn:=xlValues, searchorder:=xlByColumns, searchdirection:=xlByColumns).Column
                
        Set LastDate = DateRange.Worksheet.Cells(DateRange.Row, m)
    End If
    
    MsgBox LastDate.Address

When I run the test sub, It returns the desired result, but when I use it in the worksheet, address is the entire Date Range.

@Norie - FirstDayPK is the first day of classes for the Preschool as they start on a different day then the big kids, LastDayP1 is the last day of the first period. AugSeptDates is the range of school dates for August and September. These are determined by a function in the cell, hence the Find method in the code above.
 
Upvote 0

Excel Facts

Do you hate GETPIVOTDATA?
Prevent GETPIVOTDATA. Select inside a PivotTable. In the Analyze tab of the ribbon, open the dropown next to Options and turn it off
There are some limitations when using UDFs in formulas on a worksheet.

Have a look here.
 
Upvote 0
Marcelo

I thought that Find being used to find the last row could be a problem too, is that right?
 
Upvote 0
Norie

According to the link above, you should be right. FIND also does not work in UDFs

M.

oops...FIND does work!


[Table="class: grid"][tr][td="bgcolor: #DCE6F1"][/td][td="bgcolor: #DCE6F1"]
K
[/td][/tr]
[tr][td="bgcolor: #DCE6F1"]
1
[/td][td][/td][/tr]

[tr][td="bgcolor: #DCE6F1"]
2
[/td][td]
a​
[/td][/tr]

[tr][td="bgcolor: #DCE6F1"]
3
[/td][td]
b​
[/td][/tr]

[tr][td="bgcolor: #DCE6F1"]
4
[/td][td]
c​
[/td][/tr]
[/table]


Code:
Function GetRow(s As String, r As Range)
    GetRow = r.Find(s).Row
End Function

=GetRow("b",K2:K4)
returned 3 as expected

M.
 
Upvote 0
Norie

According to the link above, you should be right. FIND also does not work in UDFs

M.

Alrighty then. So any suggestions on how to find the last cell with an actual date in it? Every cell in the Date range contains the following formula:

=IFERROR(IF(WEEKDAY(AF6,2)<5,IF(MONTH(AF6+1)<10,AF6+1,""),IF(MONTH(AF6+3)<10, AF6+3,"")),"")

So I can't look for blanks because the 'blanks' contain and empty string. I was trying to do it without a loop.
 
Upvote 0
Marcelo

What happens when you try Find with some of the usual arguments, eg LookIn etc?
 
Upvote 0
Here's my list of things that don't work in UDFs.

[Table="width:, class:grid"][tr][td="bgcolor:#C0C0C0"][/td][td="bgcolor:#C0C0C0"]
A​
[/td][td="bgcolor:#C0C0C0"]
B​
[/td][td="bgcolor:#C0C0C0"]
C​
[/td][td="bgcolor:#C0C0C0"]
D​
[/td][td="bgcolor:#C0C0C0"]
E​
[/td][td="bgcolor:#C0C0C0"]
F​
[/td][/tr][tr][td="bgcolor:#C0C0C0"]
1​
[/td][td="bgcolor:#F3F3F3"]
Property/Method
[/td][td="bgcolor:#F3F3F3"]
2003
[/td][td="bgcolor:#F3F3F3"]
2007
[/td][td="bgcolor:#F3F3F3"]
2010
[/td][td="bgcolor:#F3F3F3"]
2013
[/td][td="bgcolor:#F3F3F3"]
2016
[/td][/tr]
[tr][td="bgcolor:#C0C0C0"]
2​
[/td][td]Application.OnTime[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][/tr]
[tr][td="bgcolor:#C0C0C0"]
3​
[/td][td]Range.CurrentArray[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][/tr]
[tr][td="bgcolor:#C0C0C0"]
4​
[/td][td]Range.CurrentRegion[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][/tr]
[tr][td="bgcolor:#C0C0C0"]
5​
[/td][td]Range.DisplayFormat[/td][td]
Didn’t exist​
[/td][td]
Didn’t exist​
[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][/tr]
[tr][td="bgcolor:#C0C0C0"]
6​
[/td][td]Range.FindNext[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][/tr]
[tr][td="bgcolor:#C0C0C0"]
7​
[/td][td]Range.SpecialCells[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][td]
X​
[/td][/tr]
[/table]


I think Find does work, but haven't tried it in the last five minutes to verify.
 
Upvote 0
Just an observation. This line:
Rich (BB code):
m = DateRange.Find(What:="*", LookIn:=xlValues, searchorder:=xlByColumns, searchdirection:=xlByColumns).Column
has the wrong syntax for the searchdirection. Should be either xlNext or xlPrevious. Don't know if using the correct syntax will make any difference.
 
Upvote 0

Forum statistics

Threads
1,223,909
Messages
6,175,315
Members
452,634
Latest member
cpostell

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top