我正在使用VBA开发Excel(2010+)应用程序,并遇到一个问题,一旦查询完成执行,就不会调用AfterRefresh事件函数。
我无法找到许多有关如何在类模块中触发此事件功能的合适资源或文档。我决定使用类模块设计路线,而不是在收到对先前有关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
以下是创建此类实例的代码的快照,查找相关的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开发人员,但我推断这会创建一个副本并在不相关的表上调用该事件。但是,情况并非如此,并且通过Reference传递也没有解决问题。
此外,当数据正确显示并刷新时,确认查询成功运行。
修改 我将BeforeRefresh事件函数添加到CQtEvents类Module并确认一旦调用Refresh就调用此函数
Private Sub mQryTble_BeforeRefresh(Cancel As Boolean)
MsgBox "Start of BeforeRefresh"
End Sub
如何更改此代码从QTableModule的RefreshDataQuery()子例程获取我的QueryTable,以便在成功运行查询时调用AfterRefresh函数?
答案 0 :(得分:4)
如何捕获QueryTable的AfterRefresh event
?
说明:在您的情况下,在事件被解雇之前,您在清理或程序结束时将其设置为空,从而失去了对QueryTable
的引用。
一般解决方案:您必须确保代码仍在运行和/或您需要保留对QueryTable
的任何引用。
第一个解决方案。调用QT.Refresh method
时,请以这种方式将参数设置为false
:
qt.Refresh false
将停止进一步执行代码,直到刷新qt
为止。但我不认为这个解决方案是最好的解决方案。
第二个解决方案。让您的classQtEvents variable
公开,并在RefreshDataQuery sub
完成后使用其他代码检查状态。
CQtEvents class module
中的添加以下公共变量:
Public Refreshed As Boolean
在BeforeRefresh event
添加此内容:
Refreshed = False
在AfterRefresh event
添加以下代码行:
Refreshed = True
公开您的classQtEvents variable
声明。把它放在Sub RefreshDataQuery()
Public classQtEvents as CQtEvents
但从您的子目录中删除适当的声明。
现在,即使您的子资料已经完成,您也可以通过查看.Refreshed property
来查看更新状态。您可以在Immediate或其他Sub中执行此操作。这适用于立即:
Debug.Print classQtEvents.Refreshed
第三个解决方案。 (有点类似于第一个)按照第二个解决方案中的步骤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
混为一谈。我没有使用你的变量尝试和测试任何解决方案,但是上面写了所有参考我使用的代码。
答案 1 :(得分:1)
可以找到一个github仓库,它可以找到实现此工作所需的最少代码here。
如上所述,如果您的事件处理程序不在范围内,或者您的QueryTable参考丢失,则您无法捕获该事件。确保您抓住事件的关键因素是:
在文件顶部声明事件处理类模块的任何子例程/方法的外的全局变量(我选择了{{1} }文件)。
添加ThisWorkbook
事件处理程序并在那里实例化该变量,以便它立即可用,将保留在范围(因为它是全局的)。< / p>
此时,或者在您有一个您感兴趣的QueryTable的任何下游点,将该QueryTable传递给全局实例以连接其事件。
(当有人指出我这个方向作为this question的答案时,我花了几次尝试自己解决这个问题。)