易失性用户定义函数未按预期重新计算(VBA/Excel)
Posted
技术标签:
【中文标题】易失性用户定义函数未按预期重新计算(VBA/Excel)【英文标题】:Volatile User-Defined Function not recalculating as expected (VBA/Excel) 【发布时间】:2016-09-23 20:06:45 【问题描述】:似乎 Application.Volatile 和 ActiveSheet.Calculate 不是我想象的那样,我需要帮助创建一个在相关数据更改时正确重新计算的函数。这是我现在要做的:
Public Function Availability(EmpName As Range)
Application.Volatile (True)
ColLetter = Cletter(EmpName.Column)
Set NameRange1 = Range("$" & ColLetter & "$1:$" & ColLetter & "$" & (Application.ActiveCell.Row - 1))
Set SumRange1 = Range(Cletter(Application.ActiveCell.Column) & "1:" & Cletter(Application.ActiveCell.Column) & (Application.ActiveCell.Row - 1))
Sum1 = Application.SumIfs(SumRange1, NameRange1, EmpName)
Availability = Sum1
End Function
如您所见,它需要一个单元格并使用该单元格执行 sumifs 以提供标准和标准列,使用放置函数的列对实际数字求和(我知道这似乎是重新发明***,但我只是提出问题的最简单形式)。本质上,它对每一行都执行这些计算。
CLetter 只是另一个将 Column# 转换为字母的小函数,因为我懒得调用 R1C1 并发现它更具可读性。
但是,每当我更改标准或总和范围时,而不是重新计算以考虑标识符或数字的变化,似乎忘记了受影响的行存在(即使我只是将其更改为 MATCH the criteria)或者只是抛出一个#VALUE 错误。通过单击“=Availability(A#)”函数并按 Enter 键可以轻松解决此问题,但我不想编写一个额外的宏来刷新所有这些单元格,因为它们将遍布电子表格。切换波动率有所帮助,但没有解决问题,也没有在以下工作表更改事件上添加一些变体:
Private Sub Worksheet_Change(ByVal Target As Range)
ActiveSheet.Calculate
End Sub
当然有一些方法可以让函数在任何单元格被更改时刷新自身,而不仅仅是它自己的。
【问题讨论】:
部分问题是您正在引用活动单元格。如果您更改数据,您的活动单元格就是刚刚更改的单元格,而不是公式所在的单元格。 “只要在工作表上的任何单元格中发生计算,就必须重新计算 volatile 函数。” - 如果您要更改的单元格未被 any 公式引用,则编辑不会触发任何计算。您可以尝试添加一个“虚拟”公式来引用您正在更改的单元格 - 这应该足以触发重新计算。 Dangit 你是对的,Scott,我曾考虑过这可能是一个问题,但无法想出一种无参数的方法来查找函数调用所在的行和列,它需要(不是手动输入,我的意思是)。知道如何始终从函数内部引用“此单元格”吗?也许是间接的? 试试Worksheet_SelectionChange
check this answer out怎么样
Application.ThisCell
会做你想做的事,或者 Application.Caller
就像 Scott 使用的那样
【参考方案1】:
首先确保您的计算在选项中设置为自动。如果您的计算设置为自动,则每次更改它所引用的数据时都会更改。
问题的第二部分是您正在引用活动单元格。如果您更改数据,则您的活动单元格是刚刚更改的单元格,而不是公式所在的单元格。使用Application.Caller
,它指的是调用函数的单元格。
第三次使用Cells
而不是调用另一个函数来查找列字母。单元格允许引用数字。
Public Function Availability(EmpName As Range)
Application.Volatile
Set NameRange1 = Range(Cells(1, EmpName.Column), Cells(Application.Caller.Row - 1, EmpName.Column))
Set SumRange1 = Range(Cells(1, Application.Caller.Column), Cells(Application.Caller.Row - 1, Application.Caller.Column))
Sum1 = Application.SumIfs(SumRange1, NameRange1, EmpName)
Availability = Sum1
End Function
【讨论】:
【参考方案2】:恕我直言,使用 Application.Volatile 作为忽略 Excel 重新计算规则的创可贴是一个错误 - 为什么不正确执行此操作并将 UDF 需要的所有范围作为参数传递给 UDF? 那么您将不需要应用程序 volatile 等,您的工作簿和 UDF 将计算得更干净。
【讨论】:
因为我是为不太擅长 excel 的人设计的,所以他们必须通过的参数越少越好。他们需要一个函数来计算一个人在给定列中的总工作时间,而不知道该列的长度(并且会经常重新排列),所以我希望他们必须选择的唯一参数是这个人的名字。列长应该是自动的。 所以将该列作为一整列传递并与使用的范围相交。 这不会导致自引用错误吗?我的完整版本使用与 Application.ThisCell.Row - 1 和 Application.ThisCell.Row + 1 结合的列 ID 来计算范围,不能让它尝试添加或减去自身。 只有当包含函数的公式与数据在同一列时才会出现问题,但无论如何您都不想这样做。 但我愿意,你在做一个很大的假设。标准范围是独立的,但总和范围应该是它所在的列(这是用于逐月计算的,必须跨列拖动才能有用)。不过感谢您的帮助。【参考方案3】:由于计算已经是自动的,并且重点不是要添加更多要定义的参数(即除了 sumif 的标准外,所有参数都是自动的),唯一必要的更改是将 Application.ActiveCell 变为 Appplication.ThisCell。 Application.Caller 也可能有效,但这种方式似乎更确定。感谢蒂姆和斯科特的帮助!
【讨论】:
Application.ThisCell 的文档包含以下注释:“用户不应在用户定义的函数内访问 Range 对象的属性或方法。”这是什么意思,这是否意味着只应使用 Application.Caller?以上是关于易失性用户定义函数未按预期重新计算(VBA/Excel)的主要内容,如果未能解决你的问题,请参考以下文章