Application.Calculation取决于工作簿

dnL*_*nLL 2 excel vba excel-2010

我正在管理一个包含超过 200 000 个公式(一些非常复杂的数组公式)的工作簿,这意味着我不能让 Excel 每次单击某个地方时自动计算所有单元格(计算所有内容大约需要 8 小时)。

取而代之的是,计算设置为手动,并且在打开Calculation.xlsm时执行了以下 VBA 代码:

With Application
    .CalculateBeforeSave = False
    .Calculation = xlCalculationManual
End With
Run Code Online (Sandbox Code Playgroud)

我使用自定义按钮在需要时仅计算 200k 单元格的某些部分。

我注意到 Excel 会跟踪每个工作簿中的设置,这意味着如果我打开我的 Calculation.xlsm,Excel 会记住计算设置为手动。如果我打开 Values.xlsx,Excel 会记住计算设置为自动。这是在我尝试将值从Calculation.xlsm复制到Values.xlsx 之前

现在,因为我在 Calculation.xlsm 中使用 VBA 将值复制到 Values.xlsx,ExcelApplication.Calculation也会将该设置应用于该工作簿,这意味着如果我使用新的 Excel 实例打开它,计算仍将被设置手动。

如果我在我的Calculation.xlsm工作簿使用 VBAApplication.Calculation = xlCalculationAutomatic关闭Values.xlsx之前添加一个,它将起作用,但 Excel 也将开始计算我的Calculation.xlsm工作簿中的 200k 单元格,这显然是我不想要的。

所以我的问题是关于如何根据特定的工作簿而不是Application对象来实际设置 Excel 的计算。这是基于这样一个事实,即 Excel 会根据打开的工作簿跟踪该设置(您只需进行测试并创建 2 个不同的.xlsx文件,一个启用计算,另一个禁用计算,Excel 将记住这些设置)。

我知道我可以Worksheets.Range.Calculate在关闭它之前使用该方法计算我的 Values.xlsx 工作簿,但如果我之后在 Excel 的新实例中打开它,计算仍将设置为手动。


编辑下午 3:20:不确定我是否足够清楚,英语不是我的母语。简而言之,我有带有 VBA 的Calculation.xlsmCalculation设置为手动。我有Values.xlsx没有 VBA 和计算设置为自动。如果我在Calculation.xlsm使用以下 VBA 代码打开Values.xlsx,Excel 将自动将我的Values.xlsx工作簿转换为手动计算。

计算.xlsm代码:

Private Sub Workbook_Open()
    With Application
        .CalculateBeforeSave = False
        .Calculation = xlCalculationManual
    End With
End Sub

Sub someFunction()
    Set WB = Application.Workbooks.Open("Values.xlsx")
    Set WBws = WB.Sheets("mySheet")
    DoEvents
    wb.Save
    WB.Close
End Sub
Run Code Online (Sandbox Code Playgroud)

someFunction()的执行之后,Values.xlsx计算被设置为手动。那就是问题所在。我希望它保持自动状态(并且我无法将 VBA 添加到该文件中,必须像上面一样从Calculation.xlsm 中完成)。


编辑下午 3 点 40 分:我是否可以将 Application.Calculation 的大工作簿设置为手动,将我需要的所有数据放在剪贴板中(我只需要值,而不是公式),关闭它(VBA 是否仍会继续即使我关闭执行它的工作簿也执行?),将 Application.Calculation 设置为 Auto(因为没有打开的工作簿),然后打开目标工作簿以粘贴值(Excel 仍将数据保留在剪贴板中,因为另一个工作簿已关闭?),然后保存并关闭该工作簿,将计算设置回手动(未打开工作簿)并重新打开执行代码的原始工作簿?

Dav*_*ens 5

一种方法是创建一个新的 Excel 实例。虽然这可能更慢,并且在您不关闭函数内的书籍/应用程序的情况下可能更难以使用,但对于像您的示例这样的简单情况,它可能最容易实现:

Sub someFunction()
Dim newExcel as Excel.Application
Set newExcel = CreateObject("Excel.Application")
    
    Set WB = newExcel.Workbooks.Open("Values.xlsx")
    Set WBws = WB.Sheets("mySheet")
    DoEvents
    wb.Save
    WB.Close
    newExcel.Quit
    Set newExcel = Nothing
End Sub
Run Code Online (Sandbox Code Playgroud)

Application.Calculation属性与应用程序的该实例相关,而不是其他实例。

或者,您可以使用应用程序级事件处理程序。我怀疑这可能会更快,但我还没有测试它的速度。

这个非常相似的问题(它还询问有条件地禁用应用程序级属性)稍微修改了一下。

如果:

我只是担心如果我关闭启动它的工作簿,代码是否仍然会被执行

然后只需使用普通Workbook_BeforeClose事件处理程序来恢复所需的Application.Calculation属性(对于整个应用程序/所有其他打开的工作簿)。

其余答案:

创建一个应用程序级事件处理程序,创建一个名为的类模块cEventClass并将以下代码放入其中:

Public WithEvents appevent As Application
Dim ret
Private Sub appevent_WorkbookActivate(ByVal wb As Workbook)

    Call ToggleCalculation(wb, ret)
    
End Sub
Run Code Online (Sandbox Code Playgroud)

在名为 的标准模块中使用以下内容mod_Caclulate

Option Explicit
Public XLEvents As New cEventClass
Sub SetEventHandler()

If XLEvents.appevent Is Nothing Then
    Set XLEvents.appevent = Application
End If

End Sub

Sub ToggleCalculation(wb As Workbook, Optional ret)
    If wb.Name = ThisWorkbook.Name Then
        ret = xlCalculationManual
    Else
        ret = xlCalculationAutomatic
    End If
    Application.Calculation = ret
End Sub
Run Code Online (Sandbox Code Playgroud)

将其放在Workbook_Open您始终希望手动计算的工作簿的事件处理程序中:

Option Explicit
Private Sub Workbook_Open()
    'Create the event handler when the workbook opens
    Call mod_Caclulate.SetEventHandler
    Call mod_Caclulate.ToggleCalculation(Me)

End Sub
Run Code Online (Sandbox Code Playgroud)

这将仅在打开特定工作簿时创建事件处理程序,并且Calculation每当您将视图切换到不同的工作簿时,处理程序将切换属性。

注意:如果在调试时“结束”运行时或执行任何会导致状态丢失的操作,您将丢失事件处理程序。这总是可以通过调用 Workbook_Open 过程来恢复,因此额外的保护措施可能是在ThisWorkbook代码模块中添加它:

Private Sub Workbook_SheetActivate(ByVal Sh As Object)
' Additional safeguard in case state loss has killed the event handler:
' use some workbook-level events to re-instantiate the event handler

    Call Workbook_Open
End Sub
Run Code Online (Sandbox Code Playgroud)