3

我正在使用以下 QueryTable 查询。执行后.Refresh,VBA 过程结束。查询有效,但我需要在完成后执行代码。

.AfterRefresh事件似乎是我需要的,但我无法执行。

With ActiveSheet.QueryTables.Add(Connection:="URL;" & sUrl, Destination:=ActiveSheet.Range("a1"))

             .RefreshStyle = xlOverwriteCells
             .SaveData = True
             .Refresh
             .AfterRefresh (Success)
End With

这是不执行的 AfterRefresh 子程序。

Sub QueryTable_AfterRefresh(Success As Boolean)

        If Success Then
                 Debug.Print "Success"
        Else
                 Debug.Print "Failed"
        End If
End Sub

查询完成后触发子程序需要什么?我尝试在 End With 之后和之后调用子例程,.Refresh但都没有成功。

谢谢。

4

3 回答 3

4

请确保您的QueryTable_AfterRefresh子不是放在模块中,而是放在工作表/工作簿下,与此处相同:https ://stackoverflow.com/a/14646261/1953175此外,您不需要调用事件,.AfterRefresh (Success)从您的代码。

于 2013-02-02T23:04:53.393 回答
4

我最近遇到了同样的问题,很难找到一个好的答案。我意识到这个线程很旧,但是对于发布的其他解决方案有一个不错的替代方案。

您可以利用的一种模式是将 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 函数

希望这会有所帮助,因为这是编写嵌入工作表中的这些事件处理程序的一个很好的替代方案

于 2013-08-09T00:26:41.457 回答
0

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

正如其他答案中提到的,确保您赶上事件的关键因素是:

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

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

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

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

于 2015-10-07T14:29:36.210 回答