使用 Evaluate 写入其他单元格的 UDF 是不可预测的

Posted

技术标签:

【中文标题】使用 Evaluate 写入其他单元格的 UDF 是不可预测的【英文标题】:UDF using Evaluate to write to additional cells is unpredictable 【发布时间】:2017-04-24 23:34:28 【问题描述】:

我不愿意询问有关 UDF 被设计为写入其他单元格的问题,因为按照设计,这种行为应该被禁用。但是...我将克服所有潜在的批评并提出要求。我真的不希望完全回答这个问题,所以我只是想了解一下我遇到的奇怪行为。

我有一个用于计算简单轴承允许值的 UDF。它需要 10 个参数。如果任何参数超出范围,UDF 将在调用单元格中返回“错误”。我想通过列出所有有问题的输入来整合这个相当无用的反馈,这样用户就不必一个接一个地进行单个更正。这样,所有错误的输入都会被列出,用户可以一次更正所有的输入。仅供参考,由于某些输入交互,输入存在 10 多个潜在问题。否则,用户可能会尝试按摩输入数十次而没有成功。这就是我想一次列出所有反馈的原因。

UDF:速记 - 它将数据发送到类模​​块以执行所有检查和计算。

public Function LBA(ByVal layup_string As String, ByVal diaBolt As Double, ByVal boltHead As String, _
ByVal eD As Double, ByVal tMetallicFitting As Double, ByVal tempF As Double, ByVal depth As Double, _
ByVal allowable_type As String, ByVal basis As String, ByVal cond As String) As Variant
    '
    ' declare variables
    Dim s As String
    Dim val As Variant
    Dim clba As New cFunc_LBA
    '
    ' send to class constructor 
    clba.init layup_string, diaBolt, boltHead, eD, tMetallicFitting, tempF, depth, allowable_type, basis, cond
    '
    ' get errors
    If clba.contains_errs Then ' ............................... check for design space violattions: errors
        s = clba.get_errs ' .................................... get concat string of all errors
        Evaluate ("post_error_messages(""" & s & """)") ' ...... run the subroutine to post err msgs
        val = "Error" ' ........................................ return value to calling cell
        '
    Else
        '
        ' return a valid bearing allowable
        val = clba.LBA ' ....................................... expose bearing allowable property
        '
    End If
    '
    LBA = val
End Function

类模块按预期工作。所有计算和错误日志都有效。在 UDF 中,当我检查是否有错误时,它会返回错误。然后我将错误(在一个长连接字符串中)发送到另一个子程序,该子程序应该将错误输出到其他工作表单元格中。

子:

Private Sub post_error_messages(ByVal s As String)
    ' declare variables
    Dim arr As Variant
    '
    ' initialize variables
    arr = Split(s, ",")
    '
    ' post error messages
    For i = 0 To UBound(arr) ' .................. loop thru error messages
        m.Cells(17 + i, 2) = CStr(arr(i)) ' ..... write msg in cell, increment by ROW#
    Next i
End Sub

我在此过程中注意到的一个怪癖......我只能让子例程(由 Evaluate 调用)接受一个参数。我也只能让它接受一个简单的数据类型string。我试过arraysvariantsscripting.dictionary,但都没有奏效。因此,我所有的错误消息都连接成一个长字符串,然后拆分并在 sub 中循环。

我现在的问题是,这种设置只能起到某种作用。

问题 1:无论返回多少错误,Sub 发布错误消息都只会返回三个项目。事实上,它总是返回三个项目,即使只有两个(显示的最后一个被重复)。如果有 10 条错误消息 - 显示 3 条。我将debug.print 语句放在我的错误消息子中,这样我就可以看到发生了什么,它表明当只返回 2 条错误消息时,它应该只打印到 2 个单元格,但无论如何它都会打印到第三个单元格。超过 3 个错误被丢弃。

问题 2:如果我删除工作表中显示错误消息的单元格并再次执行 UDF,则消息将不会返回。只有当我关闭工作簿并再次打开它时,错误消息子例程才会再次打印到单元格(来自 UDF)。

另外,这并不是一个真正的问题,Evaluate 运行了两次。我已经查过了,这似乎是一个已知问题。我只是把它放在那里,但我不确定这会导致任何问题。

再次,由于我在 Excel 的 UDF 的预期功能之外工作,我不希望有解决方案。也就是说,任何人都可以对此提供任何见解吗?

【问题讨论】:

您没有将 post_error_message 功能放入 Worksheet.Calculate 事件中是否存在技术原因?还是使用对话框显示错误列表? m 在哪里/如何声明和填充? m 是 udf 所在的工作表。感谢 Tim,这个 UDF Evaluate 方法直接来自您的一篇文章。 @RichHolton 我正在将我的代码添加到其他人的工作簿中。里面有很多东西,我不想把事件混在一起。此外,还有一个将错误发布到工作表单元格的请求。在阅读了蒂姆关于这种方法的帖子后,我想我会尝试一下。 为什么不创建另一个 UDF,比如 LBA_ERROR(lbaCell as Range),它返回与指定单元格中的 LBA 函数关联的错误列表?它可以返回一个多行字符串,或者您可以为错误行号添加一个参数,并拥有一系列包含对 LBA_ERROR() 的调用的单元格。这为查找错误消息提供了灵活性,并符合 Excel 的限制。 【参考方案1】:

不是您问题的答案,但这个精简版对我来说可以:

Public Function LBA() As Variant
    Dim val, s

    s = "A,B,C,D,E"

    Evaluate "post_error_messages(""" & s & """)"

    val = "Error" '

    LBA = val
End Function

Private Sub post_error_messages(s As String)

    Dim arr As Variant, i

    With Sheet1.Cells(17, 2)
        .Resize(10, 1).Value = "" '<<< clear any previous errors!
        arr = Split(s, ",")
        .Resize(UBound(arr) + 1, 1).Value = Application.Transpose(arr)
    End With

End Sub

【讨论】:

以上是关于使用 Evaluate 写入其他单元格的 UDF 是不可预测的的主要内容,如果未能解决你的问题,请参考以下文章

获取单元格的行和列,xlwings UDF在哪里被调用?

调用存储在 .xlam 文件中的 UDF 的 Evaluate() 函数并使其作用于另一张纸

Excel公式更改另一个单元格的值?

Hive UDF - evaluate() 方法中的错误

hive自定义UDF函数,步骤详解

无法在 UDF 的单元格中写入字符串