通过用户定义函数更改另一个单元格的值
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)对于很多事情(比如这个例子)。工作表函数比其他方法更容易完成,而且速度一样快(如果不是更快的话)。以上是关于通过用户定义函数更改另一个单元格的值的主要内容,如果未能解决你的问题,请参考以下文章
当另一个单元格的值发生变化时,一个单元格中的自动日期更新(通过公式计算)