Excel VBA – QueryTable刷新完成后,不会调用AfterRefresh函数

我正在开发一个使用VBA的Excel(2010+)应用程序,并且遇到AfterRefresh事件函数一旦查询执行完毕就没有被调用的问题。

我一直没能find许多体面的资源或文档来说明如何在类模块中触发这个事件函数。 我决定使用类模块devise路线,而不是在接收到有关QueryTables(此处Excel VBA AfterRefresh )的早期问题的响应之后,将事件处理程序放入工作表中。

这是我的类模块CQtEvents的代码

Option Explicit Private WithEvents mQryTble As Excel.QueryTable Private msOldSql As String ' Properties Public Property Set QryTble(ByVal QryTable As QueryTable): Set mQryTble = QryTable: End Property Public Property Get QryTble() As QueryTable: Set QryTble = mQryTble: End Property Public Property Let OldSql(ByVal sOldSql As String): msOldSql = sOldSql: End Property Public Property Get OldSql() As String: OldSql = msOldSql: End Property Private Sub Class_Initialize() MsgBox "CQtEvents init" End Sub ' Resets the query sql to the original unmodified sql statement ' This method is invoked when the Refresh thread finishes executing Private Sub mQryTble_AfterRefresh(ByVal Success As Boolean) ' Problem is here ' This function is never called :( Even if the query successfully runs Me.QryTble.CommandText = Me.OldSql End Sub 

下面是创build这个类的一个实例的代码的快照,find一个相关的QueryTable,然后调用Refresh

 Option Explicit Sub RefreshDataQuery() 'Dependencies: Microsoft Scripting Runtime (Tools->References) for Dictionary (HashTable) object 'From MGLOBALS cacheSheetName = "Cache" Set cacheSheet = Worksheets(cacheSheetName) Dim querySheet As Worksheet Dim interface As Worksheet Dim classQtEvents As CQtEvents Set querySheet = Worksheets("QTable") Set interface = Worksheets("Interface") Set classQtEvents = New CQtEvents Dim qt As QueryTable Dim qtDict As New Scripting.Dictionary Set qtDict = UtilFunctions.CollectAllQueryTablesToDict Set qt = qtDict.Item("Query from fred2") ''' Building SQL Query String ''' Dim sqlQueryString As String sqlQueryString = qt.CommandText Set classQtEvents.QryTble = qt classQtEvents.OldSql = sqlQueryString ' Cache the original query string QueryBuilder.BuildSQLQueryStringFromInterface interface, sqlQueryString ' Test message MsgBox sqlQueryString qt.CommandText = sqlQueryString If Not qt Is Nothing Then qt.Refresh Else ' ... Error handling code here... End If ''' CLEAN UP ''' ' Free the dictionary Set qtDict = Nothing End Sub 

另外这里是模块结构http://imgur.com/8fUcfLV的屏幕截图

我首先想到什么可能是问题是通过值传递QueryTable。 我不是最有经验的VBA开发人员,但我推断这会创build一个副本,并在无关的表上调用该事件。 然而,情况并非如此,通过引用也没有解决问题。

此外,由于数据正确显示并被刷新,所以查询被确认成功运行。

编辑我添加了BeforeRefresh事件函数CQtEvents类模块,并确认这个函数被调用一次刷新被调用

 Private Sub mQryTble_BeforeRefresh(Cancel As Boolean) MsgBox "Start of BeforeRefresh" End Sub 

我怎样才能改变这个代码从QTableModule的RefreshDataQuery()子例程中获取我的QueryTable,以便查询成功运行时调用AfterRefresh函数?

如何捕获QueryTable的AfterRefresh event

说明:在你的情况下,在事件被解雇之前,当你进行清理或程序结束时,丢失对QueryTable的引用。

一般的解决scheme:您必须确保您的代码仍在运行,并且/或者您需要保留对QueryTable任何引用。

第一个scheme 当调用QT.Refresh method设置参数为false这样的:

 qt.Refresh false 

这将停止进一步的代码执行,直到你的qt被刷新。 但我不认为这个解决scheme是最好的。

第二解决scheme 使您的classQtEvents variable公开, RefreshDataQuery sub完成后,用其他代码检查状态。

  1. 在你的CQtEvents class module添加下面的公共variables:

     Public Refreshed As Boolean 
  2. 在你的BeforeRefresh event添加这个:

     Refreshed = False 
  3. 在你的AfterRefresh event添加这行代码:

     Refreshed = True 
  4. 公开您的classQtEvents variable声明。 把这个Sub RefreshDataQuery()

     Public classQtEvents as CQtEvents 

但从你的子内删除适当的声明。

现在,即使你的子文件已经完成,你可以通过检查.Refreshed property来检查刷新状态。 你可以在立即或其他小组内完成。 这应该立即工作:

 Debug.Print classQtEvents.Refreshed 

第三解决scheme (有点类似于第一个)按照第二个解决scheme的步骤1到3。 在你调用qt.Refresh method你可以添加这个循环,这将停止进一步的代码执行,直到刷新qt

 'your code If Not qt Is Nothing Then qt.Refresh Else ' ... Error handling code here... End If 'checking Do Until classQtEvents.Refreshed DoEvents Loop 

最后的评论 。 我希望我没有把qt variableclassQtEvents variable混在一起。 我没有尝试过使用你的variablestesting任何解决scheme,但写了所有上面引用我使用的代码。

演示获得这个工作所需的最低代码的github回购可以在这里find。

如上所述,如果您的事件处理程序不在范围内,或者您的QueryTable引用丢失,则不会捕获该事件。 确保您赶上事件的关键因素是:

  1. 在文件顶部(我selectThisWorkbook文件)的任何子程序/方法之外声明事件处理类模块types的全局variables。

  2. 添加一个Workbook_Open事件处理程序并在那里实例化该variables,以便它立即可用并将保持在作用域中 (因为它是全局的)。

  3. 在那个时候,或者在你有兴趣的QueryTable的任何下游点,将QueryTable传递给全局实例来连接它的事件。

(当时有人指着我这个方向回答了这个问题 ,我花了几个小时试图弄明白这一点 。)