Excel VBA - 在Refresh完成后未调用QueryTable AfterRefresh函数

时间:2013-08-08 21:03:58

标签: excel vba excel-vba

我正在使用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函数?

2 个答案:

答案 0 :(得分:4)

如何捕获QueryTable的AfterRefresh event

说明:在您的情况下,在事件被解雇之前,您在清理或程序结束时将其设置为空,从而失去了对QueryTable的引用。

一般解决方案:您必须确保代码仍在运行和/或您需要保留对QueryTable的任何引用。

第一个解决方案。调用QT.Refresh method时,请以这种方式将参数设置为false

qt.Refresh false 

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

第二个解决方案。让您的classQtEvents variable公开,并在RefreshDataQuery sub完成后使用其他代码检查状态。

    你在CQtEvents class module中的
  1. 添加以下公共变量:

    Public Refreshed As Boolean
    
  2. BeforeRefresh event添加此内容:

    Refreshed  = False
    
  3. AfterRefresh event添加以下代码行:

    Refreshed = True
    
  4. 公开您的classQtEvents variable声明。把它放在Sub RefreshDataQuery()

    之前
    Public classQtEvents as CQtEvents
    
  5. 但从您的子目录中删除适当的声明。

    现在,即使您的子资料已经完成,您也可以通过查看.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 variableclassQtEvents variable混为一谈。我没有使用你的变量尝试和测试任何解决方案,但是上面写了所有参考我使用的代码。

答案 1 :(得分:1)

可以找到一个github仓库,它可以找到实现此工作所需的最少代码here

如上所述,如果您的事件处理程序不在范围内,或者您的QueryTable参考丢失,则您无法捕获该事件。确保您抓住事件的关键因素是:

  1. 在文件顶部声明事件处理类模块的任何子例程/方法的的全局变量(我选择了{{1} }文件)。

  2. 添加ThisWorkbook事件处理程序并在那里实例化该变量,以便它立即可用,将保留在范围(因为它是全局的)。< / p>

  3. 此时,或者在您有一个您感兴趣的QueryTable的任何下游点,将该QueryTable传递给全局实例以连接其事件。

  4. (当有人指出我这个方向作为this question的答案时,我花了几次尝试自己解决这个问题。)