设置:
- Windows 7(工作中)
- Windows 10(在家)
- Excel 2016(工作中构建 4627)
- Excel 2016(在家构建 8730)
- Power Query 设置为导入、附加和转换 Excel 文件的文件夹。此步骤有效。
在使用任何技术等待 Power Query 完成刷新后,如下所示的“我尝试过的事情:”部分中所述,可以显示一个消息框,并且可以在 Power Query 表完成更新之前执行任何其他代码到查询和连接窗格中显示的刷新指示器(微调器?)图标。
上述语句的例外是OnTime
Application 类的方法,如下面的“代码”部分所示,它似乎不会中断电源查询刷新的轮询。问题在于它使用硬编码的时间量来暂停 VBA 代码,这并不总是有效,因为被查询的数据的大小、数量和持续时间会随着时间而变化。
我试过了:
- 我已阅读所有说明使用
DoEvents
、BackgrgoundQuery = False
、 和CalculateUntilAsyncQueriesDone
方法和属性的 StackOverflow(和其他网站资源)。 - 我尝试按照此链接的建议创建一个类来创建查询更新事件之前/之后的事件(下面的代码示例中未显示)。
- 我尝试使用 Do Until/While 循环和 QueryTable 方法的 .Refreshing = True/False 属性来等待刷新完成。
- 我尝试将 Excel 菜单(菜单栏 --> 数据 --> 连接 --> 属性)中的 BackgroundQuery 属性设置为 False,如此处“subro”所建议的那样:等到 ActiveWorkbook.RefreshAll 完成 - VBA,带有菜单的图像这里:
代码:
Private Sub sht_sub_Refresh_AllConnections_dev()
'Name: sht_sub_Refresh_AllConnections_dev
'Purpose: An attempt at using VBA to wait for Queries to finish updating before displaying a message.
'Description: Waits for a hard coded period of time before dislpaying the message box.
'State: WIP.
'Dev: Needs a way to look at the connection stream to somehow detect when its finished.
'DECLARATIONS:
'------------'
Dim procName As String 'Stores this procedure's name.
Dim qTblLst As QueryTables 'A query table collection object.
Dim qTblObj As QueryTable 'A query table object.
Dim conLst As Connections 'A connection collection object.
Dim conObj As WorkbookConnection 'A connection object.
Dim idx As Long 'A loop counter.
'INITIALIZATIONS:
'---------------'
procName = "sht_sub_Refresh_AllConnections_dev" 'Store this procedure's name.
Linit.ini_Setup_Project 'Setup the project if needed.
Set conLst = ThisWorkbook.Connections 'Set the connections list object.
Set conObj = conLst.Item(conLst.Count) 'Set an initial connection object.
idx = 0 'As an exit if the do loop continues without end.
'MAIN CODE BODY:
'--------------'
'Turn off backgroundquery for each connection type.
For Each conObj In conLst 'For each connection object,
With conObj
Select Case .Type 'Check the connection type,
Case 1 'If its an OLEDB connection then,
.OLEDBConnection.BackgroundQuery = False 'Set it's backgroundquery property to false.
Case 2 'If its an ODBC connection the,
.ODBCConnection.BackgroundQuery = False 'Set it's backgroundquery property to false.
End Select
End With
Next conObj
ThisWorkbook.RefreshAll 'Refresh all connections.
'DEV: Using loops, DoEvents and a query name starting with the letters "zzzz" as suggsted here:
'https://social.technet.microsoft.com/Forums/en-US/bc3f7748-8a52-498d-951c-4566b8adf45a/in-excel-2016-power-queries-dont-refresh-in-the-background-anymore?forum=powerquery
'and here:
'https://www.myonlinetraininghub.com/excel-forum/vba-macros/pause-macro-until-power-queries-finished-refreshing
'Attempt to wait until the last connection has finished refreshing.
Do Until Linit.gvTbl_ZZZZZ.QueryTable.Refreshing = True 'Wait until the last table starts refreshing,
idx = idx + 1 'Icrement a loop count,
If idx > 3000 Then Exit Do 'If the loop goes longer then 3000 iterations exit,
Loop 'otherwise continue waiting.
VBA.DoEvents 'Do events before continueing (doens't work).
Do Until Linit.gvTbl_ZZZZZ.QueryTable.Refreshing = False 'Wait until the last table finishes refreshing,
idx = idx + 1 'Icrement a loop count,
If idx > 3000 Then Exit Do 'If the loop goes longer then 3000 iterations exit,
Loop 'otherwise continue waiting.
VBA.DoEvents 'Do events before continueing (doens't work).
'DEV: The following is an attempt to get connections to
' finish refreshing before code continues as suggested here:
'https://stackoverflow.com/questions/22083668/wait-until-activeworkbook-refreshall-finishes-vba
Application.CalculateUntilAsyncQueriesDone 'This is placed here as well as after the refresh.
VBA.DoEvents 'Do events before continueing (doens't work).
Application.EnableEvents = False 'Maybe turning off events helps? (nope...),
Application.ScreenUpdating = False 'This is reset in the procedure called as an argument to the next line:
Application.OnTime DateAdd("s", 3, Now), _
"Lwksh.sht_sub_Msg_RefreshDone" 'The called procedure just displays a message box.
Application.EnableEvents = True 'Restore events,
Application.ScreenUpdating = True 'Restore screen updating.
'MEMORY CLEANUP:
'--------------'
EXIT_CLEAN:
procName = Empty
Set qTblLst = Nothing
Set qTblObj = Nothing
Set conLst = Nothing
Set conObj = Nothing
idx = 0
End Sub
代码注释:
- 代码中任何以“Linit”开头的内容。是一个对象或变量,由代码的“INITIALIZATIONS:”部分中的“Linit.ini_Setup_Project”过程调用在过程之外全局设置。
- 例如,“Linit.gvTbl_ZZZZZ”是一个对象变量,它指向一个空的单行表,该表的名称以字符“zzzz”为星号,由 Power Query 生成并加载到 Excel 工作表中。该代码显示了网站的链接,在该网站上提出了使用这样的空表的建议。
问题:
- 由于 Power Query 没有内置回调让 Excel 知道它已完成更新任何刷新过程,这是一个失败的原因吗?
- 如果这不是一个丢失的原因,是否有任何其他方式(此处未描述)可用于在连接尚未完成刷新时以某种方式触发错误,或在连接完成时触发错误? (这里的想法是,如果错误不会阻止查询完成,则此错误可能会被捕获为检测天气的一种可能方式,或者刷新尚未完成)。
- 有没有办法直接使用 VBA 探测连接流以查找连接关闭或完成状态?
- 有什么方法可以通过调用 Excel 之外的程序直接访问刷新过程,该程序是用 C# 或 Python 等其他语言编写的?
- 你能想出其他任何可以尝试或测试的方法来完成这项工作吗?我会继续自己寻找答案,但经过一整年的搜索,我感觉有点不走运。