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
完成后,用其他代码检查状态。
-
在你的
CQtEvents class module
添加下面的公共variables:Public Refreshed As Boolean
-
在你的
BeforeRefresh event
添加这个:Refreshed = False
-
在你的
AfterRefresh event
添加这行代码:Refreshed = True
-
公开您的
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 variable
和classQtEvents variable
混在一起。 我没有尝试过使用你的variablestesting任何解决scheme,但写了所有上面引用我使用的代码。
演示获得这个工作所需的最低代码的github回购可以在这里find。
如上所述,如果您的事件处理程序不在范围内,或者您的QueryTable引用丢失,则不会捕获该事件。 确保您赶上事件的关键因素是:
-
在文件顶部(我select
ThisWorkbook
文件)的任何子程序/方法之外声明事件处理类模块types的全局variables。 -
添加一个
Workbook_Open
事件处理程序并在那里实例化该variables,以便它立即可用并将保持在作用域中 (因为它是全局的)。 -
在那个时候,或者在你有兴趣的QueryTable的任何下游点,将QueryTable传递给全局实例来连接它的事件。
(当时有人指着我这个方向回答了这个问题 ,我花了几个小时试图弄明白这一点 。)