excel - 是否可以针对特定公式启用Excel手动计算?

标签 excel vba

我有一个公式,每次执行时都会发出 API 请求,这使得速度变慢。我想阻止 Excel 自动重新计算包含此公式的单元格,但仍自动重新计算其他单元格。

我尝试将计算模式设置为手动:

Application.Calculation = xlCalculationManual

但是,这会阻止没有我的公式的其他单元格自动计算。

我的另一个想法是检查单元格是否已被“卡住”,然后返回其当前值,而不是调用 API 来获取新值。问题是 Excel 不提供在不更改单元格值的情况下退出该函数的方法。

Function MyFormula() As Variant

   If CellIsFrozen() Then
       MyFormula = Application.Caller.Value  'return current value
   Else
       MyFormula = GetNewValueFromAPI()  'expensive call to server
   End If

End Function

我对上述内容的问题是 Application.Caller.Value 通过执行重新计算返回单元格值并导致无限递归。

仅供引用 - CellIsFrozen 方法只是一个示例子函数,它会以某种方式检查单元格是自动调用还是手动调用。

我还知道 Application.Caller.Value2.text,不幸的是这些对我没有帮助。 Value2 也会导致重新计算,并且 text 仅返回字符串表示形式(这没有用,因为如果值是日期,它可能是“######”并且该列太窄)。

有没有办法中断 Excel 对特定公式的重新计算过程?

否则,是否可以在不执行重新计算的情况下提取单元格的值 - 我猜测 Excel 将该值存储在某处,因为它在工作表上可见,每次都坚持重新计算是没有意义的。

最佳答案

在我之前对涉及单细胞的帖子的回答中,我也想分享我们涉及多个细胞的旧经验。那时我们以索引方式使用公式,例如 =myformula(1)... 等,并将其存储在全局数组中。今天感谢您对 Caller 函数的好主意。我重新创建了另一个涉及多个单元的临时解决方案。

enter image description here

再次在模块1中

Global Flag As Boolean, LastValArr(1 To 10, 1 To 2) As Variant, Ws As Worksheet, Rng As Range

Public Function MyFormula() As Variant
Dim Adr As String, X As Integer
   If Flag Then
   MyFormula = GetNewValueFromAPI()  'expensive call to server
   Else
   Adr = Application.Caller.Address
    For X = 1 To 10
        If InStr(1, LastValArr(X, 2), Adr) > 0 Then
        MyFormula = LastValArr(X, 1)
        Exit For
        End If
    Next
   End If
End Function

Function GetNewValueFromAPI() As Variant
GetNewValueFromAPI = Application.WorksheetFunction.RandBetween(1, 1000)
End Function

Sub CalcA1()
Flag = True
Rng.Dirty
Flag = False
Ws.Range("F1").Value = IIf(Flag, "On", "Off")
End Sub

Sub ToggleFlag()
Flag = Not Flag
Ws.Range("F1").Value = IIf(Flag, "On", "Off")
If Flag Then Rng.Dirty
End Sub

在 Workbook_Open 事件中

Private Sub Workbook_Open()
Dim X As Integer
Dim Cell As Range
Set Ws = ThisWorkbook.Sheets("Sheet1")
Set Rng = Ws.Range("A1:A5")
Set Rng = Union(Rng, Ws.Range("C1:C5"))
Flag = True
Rng.Dirty
Flag = False
Ws.Range("F1").Value = IIf(Flag, "On", "Off")
End Sub

在 Sheet1 Worksheet_Calculate 事件中

Private Sub Worksheet_Calculate()
Dim X As Integer
Dim Cell As Range
X = 1
    For Each Cell In Rng.Cells
    LastValArr(X, 1) = Cell.Value
    LastValArr(X, 2) = Cell.Address
    X = X + 1
    Next
End Sub

编辑:在最初发布演示答案后,我发现它缺乏用户友好性和在 Excel 中工作时复制粘贴 UDF 公式的便利性,因此我尝试进一步即兴发挥,以便它可供无法访问 VBA 代码的用户使用,并且可以使用 UDF 的复制粘贴。

所以,我首先遇到了一个将最后值存储在临时表(可能是非常隐藏的表)中的解决方案。由于担心使用单元格访问可能会降低代码的性能,我没有发布它,最终恢复到字典对象。

此解决方案添加了自动映射公式单元格的基本优势(通过在工作表的使用范围中搜索“=myformula”)来启用/禁用计算。这将使无法访问代码模块的用户能够自由地使用 UDF。

此处添加了对 Microsoft 脚本运行时的引用。

模块中的代码:

Global Flag As Boolean, Ws As Worksheet, Rng As Range, Dict As Dictionary
Public Function MyFormula() As Variant
Dim Adr As String
   If Flag Then
   MyFormula = GetNewValueFromAPI() 'expensive call to server
   Else
   Adr = Application.Caller.Address
   'Debug.Print Adr
   MyFormula = IIf(Dict.Exists(Adr), Dict(Adr), 0)
   End If
End Function
Function GetNewValueFromAPI() As Variant
'Delay (2)
GetNewValueFromAPI = Application.WorksheetFunction.RandBetween(1, 1000)
End Function
Sub CalcA1()
Flag = True
If Not Rng Is Nothing Then Rng.Dirty
'Debug.Print "in calA1"
Flag = False
Ws.Range("F1").Value = IIf(Flag, "On", "Off")
End Sub
Sub ToggleFlag()
Flag = Not Flag
Ws.Range("F1").Value = IIf(Flag, "On", "Off")
If Flag And Not Rng Is Nothing Then Rng.Dirty
End Sub
Sub BuildRange()
Application.EnableEvents = False
Dim Cell As Range
CalcCnt = CalcCnt + 1
    Set Rng = Nothing
    Dict.RemoveAll
    For Each Cell In Ws.UsedRange.Cells
        If Left(Cell.Formula, 10) = "=myformula" Then
        'Debug.Print "From Sht Calc -" & Cell.Address
            If Dict.Exists(Cell.Address) = False Then
            Dict.Add Cell.Address, Cell.Value
            Else
            Dict(Cell.Address) = Cell.Value
            End If

            If Rng Is Nothing Then
            Set Rng = Cell
            Else
            Set Rng = Union(Rng, Cell)
            End If
        End If
    Next

Application.EnableEvents = True
End Sub

在工作簿_打开中

Private Sub Workbook_Open()
'Dim X As Integer
Dim Cell As Range
Set Ws = ThisWorkbook.Sheets("Sheet1")
Set Dict = New Dictionary
Flag = True
BuildRange
If Not Rng Is Nothing Then Rng.Dirty
Flag = False
Ws.Range("F1").Value = IIf(Flag, "On", "Off")
End Sub

在工作表中计算事件

私有(private)子工作表_Calculate() build 范围 结束子

关于excel - 是否可以针对特定公式启用Excel手动计算?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56683889/

相关文章:

vba - 尝试将 Excel 图表复制到 Power Point 演示文稿时出现下标超出范围错误

c# - 将数据从(大)文件 Excel 导入到 datagridview,然后导入数据库 - 为什么插入到数据库需要这么长时间并且不保存所有数据?

vba - 从宏中排除第一行

Excel 查询表刷新仅有效一次

Excel Debug模式失败

excel - 如何选择一定数量的带有文本的随机excel单元格并将它们组合成一个字符串

excel - 如何将范围存储到变体而不丢失格式

vba - 根据 2 个不同列中的颜色和文本删除行

excel - 在 Excel 中使用 VBA 将分隔字符串拆分并替换为新行

string - Excel VBA - 从单元格中删除单个字符而不丢失剩余单元格内容的格式