Excel UDF在从Sub调用时工作,在从sheet调用时不工作

wkyowqbh  于 2023-06-25  发布在  其他
关注(0)|答案(2)|浏览(125)

我有一个UDF函数,可以将输入数组的值转换为日期范围。当我从Sub调用它时,它可以工作,但当我试图从Excel工作表调用它时,它显示#Value。我该如何纠正这种行为?

Function GetWorkingDays(WorkingDaysArr() As Variant, MonthYear As String) As String

    Dim Result As String, WorkingDaysCount, ConsecutiveDays, DayStart As Integer
    Result = ""
    WorkingDaysCount = UBound(WorkingDaysArr) - LBound(WorkingDaysArr)
    For i = 0 To WorkingDaysCount Step 1
        If Not IsEmpty(WorkingDaysArr(i)) Then
            ConsecutiveDays = ConsecutiveDays + 1
            If DayStart = 0 Then
                DayStart = i + 1
            End If
        Else
            If ConsecutiveDays > 0 Then
                Dim DayStartStr, DayEndStr As String
                DayStartStr = ConcatDateString(CStr(DayStart), MonthYear)
                DayEndStr = ConcatDateString(CStr(i), MonthYear)
                If DayStartStr <> DayEndStr Then
                    Result = Result + DayStartStr + "-" + DayEndStr + ", "
                Else
                    Result = Result + DayStartStr + ", "
                End If
            End If
            DayStart = 0
            ConsecutiveDays = 0
        End If
    Next i
    Result = Left(Result, Len(Result) - 2)
    GetWorkingDays = Result
    
End Function

Function ConcatDateString(day As String, MonthYear As String) As String

    ConcatDateString = day + "." + MonthYear
    If Len(day) = 1 Then
        ConcatDateString = "0" + ConcatDateString
    End If
    
End Function

Sub test()
Dim varData(31) As Variant
varData(0) = "val"
varData(1) = "val"
varData(2) = "val"
varData(3) = "val"
varData(4) = "val"
varData(27) = "val"
varData(28) = "val"
varData(30) = "val"
MsgBox GetWorkingDays(varData, "06.23")
End Sub

Expected formatResult

cygmwpex

cygmwpex1#

你的函数需要一个数组(例如{1,2,3}),但是你提供了一个范围。
不使用GetWorkingDays(WorkingDaysArr() As Variant,,而是尝试GetWorkingDays(WD As Range,,然后将WorkingDaysArr = Application.Transpose(WD.Value2)添加到代码中。
如果你想同时使用这两个函数,它会更复杂:GetWorkingDays(WD, MonthYear As String)然后

If TypeName(WD) = "Range" Then
    WorkingDaysArr = Application.Transpose(WD.Value2)
ElseIf TypeName = "Variant()" Then
    WorkingDaysArr = WD
Else
    GetWorkingDays = "Invalid Input"
    Exit Function
End If
nhhxz33t

nhhxz33t2#

为了能够调用具有Range类型或Variant数组类型参数的函数,必须在其定义中将相应的参数声明为Variant类型。也因为在Function中,我们需要将传入的Range转换为一个基数为1的一维数组,而在另一种情况下,它将被一个数组调用,该数组可能有不同的基数。这就是为什么在For-Next中我们必须使用从LBound到UBound而不是从0到UBound - LBound...另一个问题是,如果我们调用Range,它可以是A1:A10或A1:K1。为了使函数具有通用性,它必须在内部解决这个问题,用户可以以任何方式调用它。除了这些观察之外,函数的结构存在问题。例如,如果语句:如果不为空(WorkingDaysArr(i))Then]为假,则执行Else。在那里,语句[If ConsecutiveDays > 0 Then]将始终为false...最终结果将是“”,并且过程[结果=左(结果,Len(结果)-2)]将返回错误。我不会深入讨论更多细节,因为问题是关于以两种方式使用函数:

Option Explicit     'The explicit option solves many errors

Function GetWorkingDays(WorkingDaysArr As Variant, MonthYear As String) As String
   Dim Result As String, ConsecutiveDays As Integer, DayStart As Integer, i As Long
   Dim DayStartStr, DayEndStr As String, trans As Range, lb As Long, ub As Long
   Result = ""
   
   If TypeName(WorkingDaysArr) = "Range" Then
      Set trans = WorkingDaysArr 'use this to pass trans in the next line by reference
      'the definition of the rngTo1Darr is in a group of three functions
      'in an other answer in this site. See the link below
      WorkingDaysArr = rngTo1Darr(trans)
   End If
   'here the WorkingDaysArr have to be of type "Variant()"
   If TypeName(WorkingDaysArr) <> "Variant()" Then
      MsgBox ("GetWorkingDays> WorkingDaysArr parameter accept only 'Variant()' and 'Range' data types")
   End If
   lb = LBound(WorkingDaysArr)
   ub = UBound(WorkingDaysArr)
   
   For i = lb To ub
      If Not IsEmpty(WorkingDaysArr(i)) Then
         ConsecutiveDays = ConsecutiveDays + 1
         If DayStart = 0 Then
            DayStart = i + 1
         End If
      Else
         'if code comes here the next line is always FALSE and Result stays ""
         'i don't think it is what you really need
         If ConsecutiveDays > 0 Then
            DayStartStr = ConcatDateString(CStr(DayStart), MonthYear)
            DayEndStr = ConcatDateString(CStr(i), MonthYear)
            If DayStartStr <> DayEndStr Then
               Result = Result + DayStartStr + "-" + DayEndStr + ", "
            Else
               Result = Result + DayStartStr + ", "
            End If
         End If
         DayStart = 0
         ConsecutiveDays = 0
      End If
   Next i
   'check the lenght else maybe get error...
   If Len(Result) >= 2 Then
      Result = Left(Result, Len(Result) - 2)
   End If
   GetWorkingDays = Result
End Function

Function ConcatDateString(day As String, MonthYear As String) As String
   If Len(day) = 1 Then
      ConcatDateString = "0" + ConcatDateString
   Else
      ConcatDateString = day + "." + MonthYear
   End If
End Function

因此,您可以从VBA调用:MsgBox GetWorkingDays(varData,“06.23”)
从一个细胞:

=GetWorkingDays(A1:A10,“06.23”)
=GetWorkingDays(A1:G1,“06.23”)

下面是代码中使用的一组三个函数的链接:https://stackoverflow.com/a/76365487/15794828

相关问题