VBA UDF 在每次更改后进行评估

Posted

技术标签:

【中文标题】VBA UDF 在每次更改后进行评估【英文标题】:VBA UDF evaluates after every change 【发布时间】:2021-10-26 14:04:57 【问题描述】:

我有一个问题,我认为这是一个非常简单的问题,但现在无法处理,所以我猜是错的。 我有一个计算两个日期之间平均汇率的 UDF

Option Explicit
Public Function averageFromRange() As Double

Dim sh As Worksheet
Set sh = ThisWorkbook.Worksheets("Exchange Rates")

Dim dateStart As Date: dateStart = sh.range("G1").Value
Dim dateEnd As Date: dateEnd = sh.range("G2").Value

Dim myRange As String
Dim rangeStart As range
Dim rangeEnd As range

Set rangeStart = sh.range("A:A").Find(What:=CStr(dateStart), LookAt:=xlWhole, LookIn:=xlValues).Offset(0, 1)
Set rangeEnd = sh.range("A:A").Find(What:=CStr(dateEnd), LookAt:=xlWhole, LookIn:=xlValues).Offset(0, 1)

If rangeStart Is Nothing Then
    MsgBox ("Date " & dateStart & " out of range")
End If

If rangeEnd Is Nothing Then
    MsgBox ("Date " & dateEnd & " out of range")
End If

If Not (rangeStart Is Nothing Or rangeEnd Is Nothing) Then
    myRange = rangeStart.Address & ":" & rangeEnd.Address
    averageFromRange = Application.WorksheetFunction.Average(range(myRange))
End If
End Function

整个工作簿(调用函数的工作表除外)中的任何更改都会将该函数重新评估为 #VALUE!。我尝试将 UDF 参数化以将这些日期作为输入参数,并激活工作表。我没有其他想法如何处理这个问题。你能帮帮我吗?

【问题讨论】:

range(myRange)) - 这隐含地对ActiveSheet 起作用。试试sh.Range(myRange) 【参考方案1】:

由于这些行,当在 [A:A] 列中找不到任何 dateStartdateEnd 时,该函数将返回 #VALUE!

Set rangeStart = sh.range("A:A").Find(What:=CStr(dateStart), LookAt:=xlWhole, LookIn:=xlValues).Offset(0, 1)
Set rangeEnd = sh.range("A:A").Find(What:=CStr(dateEnd), LookAt:=xlWhole, LookIn:=xlValues).Offset(0, 1)

那些行正在尝试设置NothingOffset(0, 1) (即Find 返回Nothing 并且这些行仍在尝试返回Offset

解决方案:首先找到包含DatesCell,如果找到日期,设置Offset范围。

如果列[A:A]Dates(开始和结束)由公式更新,您可能希望UDF 为Volatile。

试试这个代码:

Public Function averageFromRange() As Double

Dim dDateIni As Date, dDateEnd As Date
Dim rINI As Range, rEND As Range

    Application.Volatile    'Comment this line is VOLATILE is not required
    With ThisWorkbook.Worksheets("Exchange Rates")
    
        dDateIni = .Range("G1").Value
        dDateEnd = .Range("G2").Value
    
        With .Columns(1)
            Set rINI = .Find(What:=CStr(dDateIni), LookAt:=xlWhole, LookIn:=xlValues)
            Set rEND = .Find(What:=CStr(dDateEnd), LookAt:=xlWhole, LookIn:=xlValues)
        End With
    
    End With
            
    If rINI Is Nothing Then MsgBox ("Date " & dDateIni & " out of range")
    If rEND Is Nothing Then MsgBox ("Date " & dDateEnd & " out of range")
    If Not (rINI Is Nothing And rEND Is Nothing) Then
        averageFromRange = Application.Average(Range(rINI.Offset(0, 1), rEND.Offset(0, 1)))
    End If
        
    End Function

使用的资源: Worksheet.Range, With statement

【讨论】:

嗯,我通过了那个代码,在我运行任何其他子之前工作正常。当我这样做时,我得到了 2 个 MsgBox,所以日期超出了范围,这很奇怪,因为它们在范围内,为了再次修复它,我需要重新调用该函数。我删除了 volatile 指令,但没有太大变化。 你原来的问题解决了,UDF没有返回#VALUE!不再,这是这个问题的范围。您的新问题似乎与UDF 无关,而是与日期的更新方式、调试函数并找出生成消息时的实际值有关。建议在其他潜艇的开头添加Application.Calculation=xlCalculationManual,在末尾添加Application.Calculation=xlCalculationAutomatic,这样UDF在其他潜艇结束之前不会触发。

以上是关于VBA UDF 在每次更改后进行评估的主要内容,如果未能解决你的问题,请参考以下文章

自动重新评估非易失性 UDF

雪花不支持的子查询类型无法在 UDF 标量中评估

如何在 hive udf 中使用 collect_set 的结果 - 评估方法?

自定义配置单元 UDF 中的覆盖评估方法

Excel UDF 将评估的 SUB 的值加倍

雪花标量 UDF 返回 无法评估不支持的子查询类型