VBA For Excel AfterRefresh事件

时间:2013-02-02 22:08:25

标签: excel excel-vba vba

我正在使用以下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

查询完成后触发子程序需要什么?我在.Refresh之后和结束之后尝试了对子程序的调用但是没有工作。

感谢。

3 个答案:

答案 0 :(得分:4)

请确保您的QueryTable_AfterRefresh子不在模块中,但在Sheet / Workbook下,与此处相同:https://stackoverflow.com/a/14646261/1953175此外,您不需要调用事件,删除{{ 1}}来自您的代码。

答案 1 :(得分:4)

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

您可以使用的一种模式是将QueryTable回调事件保存在单独的类模块中,而不是嵌入到工作表中。这允许更模块化,可重用的代码。当您的Excel工作簿具有多个QueryTable时,它变得特别有用。

以下是类模块在名为 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

但是,这不是最好的方法,但是如果您不介意在子模块中的任何其他代码运行之前等待查询,那么它将起作用。请参阅这篇文章,了解KazJaw对此Excel VBA - QueryTable AfterRefresh function not being called after Refresh completes的替代方案的非常好的答案。

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

答案 2 :(得分:0)

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

如其他答案中所述,确保您抓住事件的关键因素是:

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

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

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

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