【问题标题】:Application.Calculation depending on workbookApplication.Calculation 取决于工作簿
【发布时间】:2014-08-28 19:10:04
【问题描述】:

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

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

With Application
    .CalculateBeforeSave = False
    .Calculation = xlCalculationManual
End With

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

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

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

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

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

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


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

Calculation.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

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


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

【问题讨论】:

  • 打开工作簿活动对您有帮助吗?喜欢 Private Sub Workbook_Open() 并添加您的代码是否要激活或禁用自动计算..
  • @mehow 这意味着你开始这个过程完成你的轮班并在第二天回来:D
  • Ya正是krish KM,这正是它的意思!我编辑了我的 OP 只是为了确保您理解。
  • 是的。计算是Application 级别的属性,因此当您为一个工作簿设置它时,它适用于该应用程序实例中的所有工作簿。您需要的可能是一个应用程序级别的事件处理程序,以便每当您更改 ActiveWorkbook 时,它都会相应地重置该属性。
  • 为此,您可以修改我给here 的答案以切换Application.Calculation 而不是Application.CelldragAndDrop 属性:)

标签: vba excel excel-2010


【解决方案1】:

一种方法是创建一个新的 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

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

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

this very similar question 稍作修改(它还询问有条件地禁用应用程序级属性)。

如果:

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

然后只需使用普通的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

在名为 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

把这个放在你一直想要手动计算的工作簿的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

这将仅在打开特定工作簿时创建事件处理程序,并且每当您将视图切换到不同的工作簿时,处理程序都会切换 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

【讨论】:

  • 是否可以使用 VBA 创建一个新的应用程序对象?那不是更容易吗?如果我可以有 2 个单独的 Application 对象并将数据从第一个 App 对象中的工作簿复制到另一个 App 对象,它们都具有不同的 App.Calculation 值。如果不可能,我将使用事件处理程序,尽管我有点不愿意使用事件(我在 VBA 和 Outlook 中的事件方面遇到了糟糕的性能问题)。
  • 当然可以。让我修改我的答案以包含该选项。
  • 替代解决方案完美无缺,我不知道为什么我以前没有想到它。但是,如果我需要打开 2 个以上的工作簿,您的第一个原始解决方案会更加强大。
  • 如果需要,您可以拥有 2 个以上的 Excel 实例 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-03
  • 2016-05-12
  • 2011-05-17
  • 1970-01-01
相关资源
最近更新 更多