我最近遇到了同样的问题,很难找到一个好的答案。我意识到这个线程很旧,但是对于发布的其他解决方案有一个不错的替代方案。
您可以利用的一种模式是将 QueryTable 回调事件保存在单独的类模块中,而不是嵌入工作表中。这允许更多模块化、可重用的代码。当您的 Excel 工作簿有多个 QueryTables 时,它变得特别有用。
这是类模块在名为CQtEvents的类模块中的样子
Option Explicit
Private WithEvents mQryTble As Excel.QueryTable
' Add variables you may want to cache here such at the query or connection settings
' 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
' Add other potential properties here
Private Sub Class_Initialize()
' Constructor
MsgBox "CQtEvents init"
End Sub
Private Sub mQryTble_BeforeRefresh(ByVal Cancel as Boolean)
'Insert logic you want to run before a refresh
End Sub
Private Sub mQryTble_AfterRefresh(ByVal Success As Boolean)
'Insert logic you want to run after a refresh
End Sub
上面要注意的关键是WithEvents关键字以及 BeforeRefresh 和 AfterRefresh 的声明/定义。
下面是利用上面定义的类模块的代码的样子
Option Explicit
Sub RefreshDataQuery()
'Dependencies: Microsoft Scripting Runtime (Tools->References) for Dictionary (HashTable) object
Dim querySheet As Worksheet
Dim classQtEvents As CQtEvents
Set querySheet = Worksheets("QTable")
Set interface = Worksheets("Interface")
Set classQtEvents = New CQtEvents ' Instantiate the Class
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 '''
qt.CommandText = "Select * From someTable"
If Not qt Is Nothing Then
qt.Refresh False ' See link at bottom of post for alternatives to this
Else
' ... Error handling code here...
End If
''' CLEAN UP '''
' Free the dictionary
Set qtDict = Nothing
End Sub
这种方法的一个警告是,如果异步运行并保持原样,则不会调用 AfterRefresh。这样做的原因是当模块完成执行时对查询表的引用将消失,这很可能在查询完成执行之前完成。为了解决这个问题,您可以通过设置同步运行它
qt.Refresh False
但是,这不是最好的方法,但如果您不介意在子模块中的任何其他代码运行之前等待查询,这将起作用。有关此Excel VBA 的替代方案的非常好的答案,请参阅此帖子 - KazJaw 完成刷新后未调用 QueryTable AfterRefresh 函数。
希望这会有所帮助,因为这是编写嵌入工作表中的这些事件处理程序的一个很好的替代方案