【问题标题】:Excel calculation just on Active Workbook, looking for a workaround仅在 Active Workbook 上进行 Excel 计算,寻找解决方法
【发布时间】:2017-04-04 09:13:34
【问题描述】:

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

这是一个众所周知且广为人知的问题,已经存在多年,但微软似乎对修复它并不感兴趣。

Calculate only the active workbook before saving

Microsoft Excel wishlist: Workbook level calculation

可笑的是,在 VBA 和 VSTO 中,Microsoft 让我们能够:

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

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

在我工作的金融公司,这是一个大问题。我们的精算师拥有庞大而庞大的 Excel 文件,其中充满了公式,当他们在一个工作簿上单击“计算”或我们在保存文件之前执行计算时,他们必须等待几分钟才能将所有其他打开的 Excel 文件也 计算出来。

有两种解决方法。

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

Application.Calculation = xlManual 
For Each sh In ActiveWorkbook.Worksheets 
    sh.Calculate 
Next sh 

..但这不能保证有效。您的“Sheet1”可能包含指向“Sheet2”中单元格的公式,但“Sheet2”中的其他单元格可能依赖于“Sheet1”。因此,计算每个 Worksheet 一次 可能不足以对您的 Workbook 执行完整计算。

或者,您可以在单独的实例中打开每个 Excel 文件(在打开 Excel 图标时按住 ALT)。但是,您将失去完整的 Excel 剪切和粘贴功能,如下所述:

Can't fully cut'n'paste between Excel instances

那么,我的问题是……有没有人找到解决方法来解决这个问题?

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

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

或者可能向Worksheet_Calculate 事件添加一个处理程序,该处理程序检查正在运行的VBA 代码是否属于活动工作簿,如果不属于,它会中止尝试计算...?但是这个事件实际上是在计算完 Worksheet 之后开始的,所以为时已晚。

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

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

【问题讨论】:

  • 鉴于您可以在 VBA 中使用trace a cells' dependends,我的第一个想法是您可以对相关单元格进行树搜索,首先计算最后一个分支并向后工作......这将解决问题你提到使用sh.Calculate。您可以将其与rng.Calculate 通话配对。可能需要一些真正的思考来为此编写一个优雅的函数!
  • 谢谢 Wolfie.. 是的,我们确实考虑过这个.. 但它会是一段噩梦般的代码编写,这就是为什么我认为以某种方式“禁用”会更安全计算期间所有其他打开的工作簿。问题是,当你重新启用它们的计算时,无论如何它都会开始计算,所以我们又回到了开始的地方!!
  • 您可以使用 VBA 以只读方式打开工作簿 (msdn.microsoft.com/en-us/library/office/ff194819.aspx)
  • @Luuklag 以只读方式打开它们需要先关闭它们,对于 OP 来说并不是很理想(我认为)!
  • @MikeGledhill 是的,当我在写这个建议时,我意识到让健壮和快速(这是重点!)可能会很棘手,我认为 Excel 的计算功能做了类似但高度优化的事情。我会考虑一下,我也感受到了这种痛苦。

标签: excel vsto vba


【解决方案1】:

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

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

宏步骤

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

运行此脚本的关键点

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

代码 - 详见 cmets

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

通过将您希望重新计算的工作簿对象传递给上面的 sub 来调用它(这可以通过按钮等来完成)。

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

【讨论】:

  • 感谢这个想法。不幸的是,在我们的网络上,保存 Excel 文件,然后重新加载它……这不是一个选项。这将使整体保存/计算时间更长。不过感谢您的反馈,我很感激。
  • @Mike,啊,不用担心,知道它有它的缺点,如果 Excel 能快速做一些可以利用的事情,呵呵!我会把这个答案留在这里,以防它对其他人有用。
【解决方案2】:

我在我的 FastExcel 产品中使用的方法包括将所有非活动工作簿中的所有工作表的 WorkSheet.EnableCalculation 设置为 false,并将活动工作簿中的所有工作表设置为 True。

这可行,但缺点是在您更改活动工作簿时使下一个计算成为对新活动工作簿的完整计算:因此需要权衡。

您可以通过从Download 下载 FastExcel 的试用版来尝试在您的情况下试用 FastExcel V3 版本 231.655.789.380 的 15 天全功能试用版

然后使用 FastExcel Calculation Options 并选中 Current Calculation Mode 设置中的 Active Workbook 复选框;

免责声明:我拥有、开发和销售 FastExcel 产品。包含活动工作簿计算代码的 FastExcel 组件是 FastExcel V3 Calc

【讨论】:

  • 谢谢查尔斯。是的,我和我的团队早些时候讨论过这个“设置 EnableCalculation”标志的想法,但我们决定反对它,因为一旦我们的 ActiveWorkbook 完成计算,我们需要将 EnableCalculation 设置回 true ......所以我们回来了原来的问题:Excel痛苦地重新计算其他工作簿。所以,这实际上并没有帮助。
  • 但您不需要将其他工作簿的 enablecalculation 设置回 true。您只需要在不同的工作簿变为活动状态时使用应用程序事件来更改它(将旧的活动启用计算设置为假,新的活动启用计算设置为真)。试试看它是否适合你。
  • 困难在于@Charles,即“当您将值从 False 更改为 True 时,Excel 会重新计算工作表”(来自 docs),因此如果您在切换工作簿时启用它,即使用户不希望计算也会发生?
  • 像往常一样,这些文档充其量只是误导。实际发生的情况是,从 False 切换到 True 会弄脏该工作表上的所有公式,但不会在手动计算模式下触发计算事件。在自动计算模式下,由于某些公式已被弄脏,会发生计算事件。 (顺便说一句,在 FastExcel 中,您还可以为不同的打开工作簿设置不同的计算模式,因此您可能在 Auto 中有一本快书,而在 Manual 中可能有一本慢书)。 Active Book calc 技术的速度折衷是对刚刚激活的工作簿的第一次重新计算是完整计算
  • @Wolfie - 有关 Worksheet.Enablecalculation 的更详细说明,请参阅我的网站 decisionmodels.com/calcsecretsh.htm
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-17
  • 1970-01-01
  • 2020-08-17
  • 2022-11-10
  • 1970-01-01
  • 2016-06-13
相关资源
最近更新 更多