通过用户定义函数更改另一个单元格的值

Posted

技术标签:

【中文标题】通过用户定义函数更改另一个单元格的值【英文标题】:Change value of another cell via a User Defined Function 【发布时间】:2021-12-30 14:13:40 【问题描述】:

Function TotalHours() 旨在接受两个范围。第一个范围通常应该是一行乘七列(一周的 7 天),第二个范围是一个接受值的单元格。

我在设置一个单元格的值时遇到问题。

我遇到问题的线路:

rCell.Value = sngOT

我明白了

“应用程序定义或对象定义的错误”。

我尝试了rCell.Cells(1,1).Value = sngOT 并得到了同样的错误。

Function TotalHours(myRange As Range, rOT As Range) As Single
Dim sngHours As Single, sngNormal As Single, sngOT As Single
Dim rCell As Range

    sngHours = 0
    sngNormal = 0
    sngOT = 0
    For Each rCell In myRange
        If rCell.Value > 8 Then
            sngOT = sngOT + rCell.Value - 8
            sngNormal = sngNormal + 8
        Else
            sngNormal = sngNormal + rCell.Value
        End If
    Next rCell
    If sngNormal > 40 Then
        sngOT = sngOT + (sngNormal - 40)
        sngNormal = 40
    End If
    sngHours = sngNormal + sngOT
    Set rCell = rOT
    rCell.Value = sngOT
    Set rCell = Nothing
    TotalHours = sngHours

End Function

【问题讨论】:

从工作表调用的函数不能更改另一个单元格的值,只能更改放置公式的单元格。 顺便说一句:=MIN(SUM(IF(A1:G1>8,8,A1:G1),40) 将得到正常小时数的总和然后您需要做的就是从总和中减去该结果:=SUM(A1:G1)-H1 其中 H1 具有第一个公式。 嗨,斯科特,谢谢。我没有意识到函数有这个限制。 @Scott Craner:关于“从工作表调用的函数无法更改另一个单元格的值”:如上所述,这本身不是有效的“规则”。尽管我假设您已经知道,但请参阅下面的操作方法。 @Spinner 我知道解决方法。我认为它是一个黑客。我很少推荐它。相反,我建议正确地做。这样做你不知道是什么改变了该单元格中的值,很难跟踪。这就是为什么你会看到很少有长期在这里推荐它的人。它不被认为是正确的编码技术。 【参考方案1】:

像 Scott Craner 一样,我假设当您的函数被用作 UDF 时会引发错误(否则您不会收到错误)。假设这是真的,有一种方法可以从 UDF 中更改其他单元格。

以下内容可以满足您的需求。 注意:我进行了其他更改并添加了代码注释说明原因。

Function TotalHours(rgHours As Range, rgWriteOT As Range) As Single
    
''' Declare hour values as doubles
''' Note: doubles declares as zero
    Dim dbTotalHrs#, dbNormalHrs#, dbOTHrs#, rgCell As Range

''' Get normal hours (max 8 per day) and bandwidth OT (i.e. any hours > 8 on any given day)
    For Each rgCell In rgHours
        dbNormalHrs = dbNormalHrs + WorksheetFunction.Min(rgCell, 8)
        dbOTHrs = dbOTHrs + WorksheetFunction.Max(rgCell - 8, 0)
    Next rgCell
    
''' For any given week worked more than 40 hours: All hours after 40 are OT
    If dbNormalHrs > 40 Then
        dbOTHrs = dbOTHrs + (dbNormalHrs - 40)
        dbNormalHrs = 40
    End If
    
''' Round results to 4 places (to avoid floating point rounding issues)
    dbOTHrs = Round(dbOTHrs, 4)
    dbTotalHrs = Round(dbNormalHrs + dbOTHrs, 4)
    
''' Return Total Hours
    TotalHours = dbTotalHrs

''' Write OT Hours to rgWriteOT (via SetOTValue)
    With rgWriteOT
        .Parent.Evaluate "SetOTValue(" & .Address(False, False) & "," & dbOTHrs & ")"
    End With

End Function

''' Sub to set a value in another cell
Sub SetOTValue(Target As Range, Value#)
    Target = Value
End Sub

所有内容都涵盖了,需要指出的是,您可以使用公式轻松完成所有这些操作: 假设您的每日工作时间在单元格 A2:G2 中,总小时数在单元格 H2 中,OT 小时数在 I2 中,以下是您想要的: 总小时数 (H2) 的公式:=SUM(A2:G2) 加班时数 (I2) 公式:=IF(H2>40,H2-40,SUM(IF(A2:G2>8,A2:G2-8)))

注意在 I2 处开始 和结束 : o 这是一个数组公式。您没有输入 o 相反,您使用 Ctrl、Shift 和 Enter 来“提交”公式

【讨论】:

嗨 Spinner,我更像是一个 Access 专家而不是 Excel 专家,所以这些问题只是为了帮助我澄清一些事情。 1)为什么使用双而不是单? (回顾你的 cmets,我想我明白了原因。我很老派,所以我总是初始化我的变量,而不是相信程序会为我这样做。) 2)为什么使用 WorksheetFunctions 而不是简单的 if-那么我用的语句呢?我从来没有听说过数组公式,所以谢谢你。最初的电子表格使用了一个从未完全正确的公式。谢谢! 很高兴为您提供帮助 :)。 Re 1)有很多关于在使用(本质上是)十进制值时使用双打(与单打)的文章。主要问题是四舍五入的一致性(双打做得更好)。 Re 2)对于很多事情(比如这个例子)。工作表函数比其他方法更容易完成,而且速度一样快(如果不是更快的话)。

以上是关于通过用户定义函数更改另一个单元格的值的主要内容,如果未能解决你的问题,请参考以下文章

当另一个单元格的值发生变化时,一个单元格中的自动日期更新(通过公式计算)

当我在另一个单元格中选择一个值时如何更改单元格的值

尝试使用 VBA 更改工作表中特定单元格的值失败

VBA用户定义的函数来复制单元格的值,文本形成和超链接?

基于将另一个单元格与另一个数据帧的单元格-熊猫进行比较,更改一个数据帧中单元格的值

如何从 UDF 中读取另一个单元格?