仅在Active Workbook上进行Excel计算,寻找变通方法

Mik*_*ill 5 excel vba vsto excel-vba

当您打开多个Excel文件,并且VBA/VSTO代码调用该Calculate函数或打开自动计算时,Excel将痛苦地重新计算所有打开的工作簿,而不仅仅是活动工作簿.

在此输入图像描述 这是一个众所周知且报道良多的问题,已存在多年,但微软似乎并不想修复它.

保存前仅计算活动工作簿

Microsoft Excel愿望清单:工作簿级别计算

可笑的是,在VBA和VSTO中,微软让我们有能力:

  • 重新计算特定的Worksheeet
  • 重新计算所有打开的工作簿

...但是没有选择只重新计算一个特定的工作簿.

在我工作的金融公司,这是一个巨大的问题.我们的精算师有大,笨重的Excel文件,充分公式,当他们点击计算上一个工作簿或我们保存文件前进行计算,然后他们必须等待几分钟到其他所有打开的Excel文件得到计算.

有两种方法可以解决这个问题.

你可以像这样运行一些VBA:

Application.Calculation = xlManual 
For Each sh In ActiveWorkbook.Worksheets 
    sh.Calculate 
Next sh 
Run Code Online (Sandbox Code Playgroud)

..但这不能保证有效.您的"Sheet1"可能包含指向"Sheet2"中单元格的公式,但"Sheet2"中的其他单元格可能依赖于"Sheet1".因此,计算每个工作表一次可能不足以在工作簿上执行完整计算.

或者,您可以在单独的实例中打开每个Excel文件(通过在打开Excel图标时按住ALT).但是,你失去了完整的Excel cut'n'pasting功能,如下所述:

无法在Excel实例之间完全切割'n'

所以,我的问题是......有没有人找到解决这个问题的方法

只想重新计算Active Excel工作簿中的单元格.

我想知道是否可以添加一些VBA或VSTO,在我启动Active Workbook上的计算之前将所有非活动工作簿设置为"只读",从而防止其他工作簿被重新计算.但这是不可能的.Workbook.ReadOnly只能读取" ",而不能以编程方式设置.

或者可能在Worksheet_Calculate事件中添加一个处理程序,它检查正在运行的VBA代码是否属于Active Workbook,如果没有,它会中止尝试计算......?但这个事件实际上被拉开序幕后,该工作表已被计算,所以一切都太迟了.

我们公司不可能是唯一遭受这个问题的人......

任何建议(除了升级到Lotus 1-2-3)?

Wol*_*fie 1

此方法使用 Excel 的另一个实例来避免多个工作簿计算。使用新实例的几行代码取自此 SO 问题,该问题涉及类似的主题,您可能会感兴趣。

您必须在具体情况下测试其速度,因为关闭/打开时间可能不会超过避免的计算!

宏观步骤

  • 将计算设置为手动
  • 保存并退出所需的工作簿
  • 在新的 Excel 实例中打开它
  • 重新计算
  • 在 Excel 的原始实例中保存、关闭并重新打开。

运行该脚本的要点:

  • 宏不能存在于要重新计算的工作簿中,因为它在此过程中被关闭(两次)。它应该放在其他一些“实用程序”工作簿中。

代码- 详细信息请参阅评论

Sub CalculateWorkbook(WB As Workbook)
    ' Store path of given workbook for opening and closing
    Dim filepath As String
    filepath = WB.FullName
    ' Turn off calculation before saving
    Dim currentCalcBeforeSave As Boolean
    currentCalcBeforeSave = Application.CalculateBeforeSave
    Application.CalculateBeforeSave = False
    ' Store current calculation mode / screen update and then set it to manual
    Dim currentCalcMode As Integer, currentScreenUpdate As Integer
    currentCalcMode = Application.Calculation
    currentScreenUpdate = Application.ScreenUpdating
    Application.Calculation = xlCalculationManual
    Application.ScreenUpdating = False
    ' Close and save the given workbook
    WB.Close savechanges:=True
    ' Open a new INSTANCE of Excel - meaning seperate calculation calls
    Dim newExcel As Excel.Application
    Set newExcel = CreateObject("Excel.Application")
    ' Could make it visible so that any problems don't leave it hidden in the background
    ' newExcel.Visible = False
    ' Set the calculation mode to manual in the new instance sothat the workbook isn't calculated on opening.
    ' This can't be done without an existing workbook object.
    Dim tempWB As Workbook
    Set tempWB = newExcel.Workbooks.Add
    newExcel.Calculation = xlCalculationManual
    newExcel.CalculateBeforeSave = False
    ' Open the workbook in the new instance of Excel
    Dim newWB As Workbook
    Set newWB = newExcel.Workbooks.Open(filepath)
    ' Calculate workbook once
    newExcel.Calculate
    ' Close and save the workbook, tempworkbook and quit new instance
    newWB.Close savechanges:=True
    tempWB.Close savechanges:=False
    newExcel.Quit
    ' Re-open in the active instance of Excel
    Application.Workbooks.Open filepath
    ' Reset the application parameters
    Application.CalculateBeforeSave = currentCalcBeforeSave
    Application.ScreenUpdating = currentScreenUpdate
    Application.Calculation = currentCalcMode
End Sub
Run Code Online (Sandbox Code Playgroud)

通过向其传递您希望重新计算的工作簿对象来调用上面的子程序(这可以通过按钮等完成)。

这已经在一个非常简单的示例工作簿上进行了测试,并且该概念有效。但是,请先在工作簿的副本上进行测试,因为它尚未经过完全的稳健性测试,并且没有错误处理。