5

我正在使用 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 开发人员,但我认为这会创建一个副本并在不相关的表上调用事件。但是,情况并非如此,通过引用传递也不能解决问题。

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

编辑 我将 BeforeRefresh 事件函数添加到 CQtEvents 类模块并确认调用 Refresh 后调用此函数

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

如何更改此代码从 QTableModule 的 RefreshDataQuery() 子例程获取我的 QueryTable,以便在成功运行查询时调用 AfterRefresh 函数?

4

2 回答 2

4

如何捕捉AfterRefresh eventQueryTable?

说明:在您的情况下,在触发事件之前,您QueryTable在进行清洁或程序结束时将其设置为空,从而丢失了对您的引用。

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

第一种解决方案。调用时以这种方式QT.Refresh method设置参数:false

qt.Refresh false 

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

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

  1. 在您CQtEvents class module添加以下公共变量:

    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

第三种解决方案。(有点类似于第一个)按照第二个解决方案的步骤 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。我没有使用您的变量尝试和测试任何解决方案,但上面所有内容都参考了我使用的代码。

于 2013-08-08T22:09:17.647 回答
1

可以在此处找到一个 github 存储库,该存储库演示了实现此工作所需的最少代码。

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

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

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

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

(当有人向我指出这个方向作为这个问题的答案时,我自己花了几次尝试才弄清楚这一点。)

于 2015-10-07T14:39:02.650 回答