【问题标题】:Application events, determine when Excel is really ready to read from应用程序事件,确定 Excel 何时真正准备好从中读取
【发布时间】:2020-03-20 13:29:18
【问题描述】:

我一直在考虑允许应用程序使用 COM 互操作或 Excel-dna 与 Excel 链接。理想情况下,用户可以链接任何可能包含或不包含 VBA 代码的 Excel 工作簿。通常操作是

i) C# 写入 Excel 工作表

ii) Excel 重新计算

iii) 可选地,如果用户包含任何 Worksheet_Change 或 Worksheet_Calculate 事件,Excel 可以运行 VBA

iv) C# 然后从 Excel 表中读回,其中将包含来自 calcs/VBA 的更新数据

是否有一个 FINAL Application 事件来通知一切何时完成?

例如,应用程序级别的事件包括 AfterCalculate()SheetChange,但其中一个或两个都可能触发,这使得很难知道对上述 iv) 中的哪一个做出反应。

我在 VBA 中设置了一个小型模拟项目,当更改工作表时,应用程序级事件按以下顺序触发

Worksheet_Calculate
mApp_AfterCalculate Calc ended at: 20/03/2020 13:23:29
Worksheet_Change Started
Worksheet_Change Ended
mApp_SheetChange Calc ended at: 20/03/2020 13:23:29 --> New Value: 20/03/2020 13:23:29

是否有一种简单的方法可以知道 mApp_AfterCalculate 是最终事件还是 mApp_SheetChange?

模拟项目代码:

表 1:

Private Sub Worksheet_Calculate()
    Debug.Print ("Worksheet_Calculate")
End Sub

Private Sub Worksheet_Change(ByVal Target As Range)

    Debug.Print ("Worksheet_Change Started")
    Call Setup

    Application.EnableEvents = False
    Sheet1.Range("F1").Value = Now
    Application.EnableEvents = True

    Debug.Print ("Worksheet_Change Ended")
End Sub

类模块:clsAppEvents

Option Explicit

Private WithEvents mApp As Application

Private Sub Class_Initialize()
    Set mApp = Application
End Sub

Private Sub mApp_AfterCalculate()
    Debug.Print "mApp_AfterCalculate Calc ended at: " & Now
End Sub

Private Sub mApp_SheetChange(ByVal Sh As Object, ByVal Target As Range)
    Debug.Print "mApp_SheetChange Calc ended at: " & Now & " --> New Value: " & Sh.Range("F1").Value

End Sub

模块 1:

Private mAppEvents As clsAppEvents

Public Sub Setup()
    If mAppEvents Is Nothing Then
        Debug.Print ("Setup initialized")
        Set mAppEvents = New clsAppEvents
    End If
End Sub

【问题讨论】:

    标签: excel vba com-interop excel-dna


    【解决方案1】:

    根据我对您的问题的理解,根本问题是您试图通过假设事件将按特定顺序执行来预测 Excel 的计算图,并且它们将始终同步执行......两者您无法 100% 可靠地预测其中的情况。

    我的建议是使用一种不同的、更可靠的方法,将写作与阅读分离:

    • 写入:在写入电子表格的 C# 代码上,执行此操作。写,不要等待任何东西(也不要读任何东西)

    • 读取:同样在 C# 端,订阅 Application.SheetChange 并在那里做出反应 - 即每次在 C# 端运行此事件时,从电子表格中读取您需要的值。

    这样,谁更改了电子表格并不重要...如果是您的 C# 代码、VBA 代码或用户手动更改它。您知道,每次在电子表格中发生更改时,都会导致您的 C# 代码运行并让您能够做出反应(或不做出反应),具体取决于更改是否发生在您关心的工作表中以及更改发生在以下范围内你在乎。

    伪编码您的Application.SheetChange 的样子:

    public void Application_SheetChange(object sheet, Range range) 
    { 
        if (range.Worksheet.Name != "SheetYouAreInterested")
        {
            return; // nothing to do
        }
    
        if (!range.IntersectsWith(rangeYouAreInterested))
        {
            return; // nothing to do
        }
    
        // Read the values that changed, etc.
    }
    

    现在,事件的顺序也不再重要了,因为您的事件处理程序将在每次发生更改时继续执行,因此您之前可能读过的任何陈旧值最终都会在最后一次事件发生时被刷新运行。

    当然,阅读代码不应该直接对工作表进行任何更改......它应该只阅读,否则你会陷入无限循环:D

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-17
      • 2015-07-04
      • 1970-01-01
      相关资源
      最近更新 更多