应用程序事件,确定Excel何时真正可以读取

应用程序事件,确定Excel何时真正可以读取,excel,vba,com-interop,excel-dna,Excel,Vba,Com Interop,Excel Dna,我一直在考虑允许应用程序使用COM互操作或Excel dna与Excel链接。理想情况下,用户可以链接任何可能包含或不包含VBA代码的Excel工作簿。通常情况下,操作是 i) C#写入Excel工作表 ii)Excel重新计算 iii)如果用户包含任何工作表更改或工作表计算事件,Excel也可以运行VBA iv)C#然后从Excel表格中读取,该表格将包含来自calcs/VBA的更新数据 是否有最后一个应用程序事件在所有操作完成时发出通知 例如,应用程序级事件包括AfterCalculate(

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

i) C#写入Excel工作表

ii)Excel重新计算

iii)如果用户包含任何工作表更改或工作表计算事件,Excel也可以运行VBA

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

是否有最后一个应用程序事件在所有操作完成时发出通知

例如,应用程序级事件包括
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
Private mAppEvents As clsAppEvents

Public Sub Setup()
    If mAppEvents Is Nothing Then
        Debug.Print ("Setup initialized")
        Set mAppEvents = New clsAppEvents
    End If
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 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
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的计算图,并且它们将始终同步执行。。。这两种情况你都无法100%可靠地预测

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

  • 写入:在写入电子表格的C#代码上,只需这样做。写作,不要等待任何东西(也不要阅读任何东西)

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

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

伪代码您的
应用程序.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.
}
现在,事件的顺序也不再重要了,因为您的事件处理程序将在每次发生更改时不断执行,因此您以前可能读取的任何陈旧值最终将在事件上次运行时刷新


当然,阅读代码不应直接对工作表进行任何更改。。。它应该是这样写的,否则你会陷入一个无限循环中:从我对你的问题的理解来看,根本问题是,你试图通过假设事件将按一定顺序执行来预测Excel的计算图,并且它们总是同步执行。。。这两种情况你都无法100%可靠地预测

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

  • 写入:在写入电子表格的C#代码上,只需这样做。写作,不要等待任何东西(也不要阅读任何东西)

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

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

伪代码您的
应用程序.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