设定:
问题描述
使用任何技术的任意组合等待Power Query完成刷新后,如下所示的“我尝试过的事情:”部分所述。根据“查询和连接”窗格中显示的刷新指示器(微调器?)图标,可以在Power Query表完成更新之前显示消息框并执行任何其他代码。
上述语句的例外是Application类的“OnTime”方法,如下面的“代码”部分所示,它似乎不会中断电源查询刷新的轮询。但是,此方法的问题在于它使用硬编码的时间来暂停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
代码备注:
问题
感谢您抽出时间和精力解决此问题。 如果我忘记包含任何信息或者可能需要重新编写某些内容以使此问题更加清晰,请在评论中告诉我。
答案 0 :(得分:0)
我了解您的痛苦@neurojelly。我去过那里。但事实证明,该解决方案非常简单,并且没有使用VBA。
在“查询属性”窗口中,您需要取消选中“启用后台刷新”,然后使用DoEvents
。
我肯定知道这是可行的,因为我已经使用这种方法一年多了。
请找到包含代码的示例文件的链接。
https://drive.google.com/open?id=1ZLxSMEXPLda3QhaQoTyGGv3_sC-tpN-X
至于第二个问题,可以使用Iferror/OnEror
方法来检测查询是否返回错误,但是不一定要检查查询中的错误。它标识查询本身是否返回错误弹出窗口,在运行VBA代码时默认情况下会跳过该错误弹出窗口。此方法在大多数时间有效,但并非总是如此。
答案 1 :(得分:0)
这是一种解决方法
Sub MyProcedure()
'
' Some procedures
'
Call ActiveWorkbook.RefreshAll
Call NotifyWhenRefreshComplete
End Sub
Private Sub NotifyWhenRefreshComplete()
Const PulseTimer As Currency = TimeValue("00:00:01")
Dim b1 As Boolean, b2 As Boolean
b1 = Sheet1.Range("ListObject1").ListObject.QueryTable.Refreshing
b2 = Sheet1.Range("ListObject2").ListObject.QueryTable.Refreshing
If b1 Or b2 Then
Call Application.OnTime(Now + PulseTimer, "NotifyWhenRefreshComplete")
Else
Call MsgBox("Refresh Complete.", vbOKOnly)
End If
End Sub
ListObject1和ListObject2是已发布的表。仅需要发布表才能检查刷新是否完成。您不必检查未发布的表。
但是,如果您有很多已发布的表,那么遍历所有ActiveWorkbook.connections
并检查每个wbConn.OLEDBConnection.Refreshing
状态是否都返回到false
状态并替换就好了。 b1
和b2
布尔值。
注意:出于某些原因,我拒绝使用DoEvents
,我希望我的用户能够在连接仍在运行时继续使用Excel,并在刷新完成时向他们提示消息。但是,如果要使用Do... Loop
迭代而不是如上所述的OnTime
调用程序来实现它,请继续。
最后,在Public WithEvents qt As QueryTable
下有一个回调,查找qt.refreshing
。您也可以使用此方法。
希望这会有所帮助。
答案 2 :(得分:-1)
我在ThisWorkbook.Connections.Ranges(1).ListObject.QueryTable上调用.Refresh方法,使用BackgroundQuery:= False。它看起来非常可靠 - 到目前为止,我的Macro可能已经将该方法称为10,000次,误差/冻结率非常低。
据我了解,当PQ在Excel 2016的对象模型中正确公开时,所有这些DoEvents变通办法等都已过时。
这是一个粗略的代码示例:
Dim cn As WorkbookConnection
For Each cn In ThisWorkbook.Connections
cn.Ranges(1).ListObject.QueryTable.Refresh BackgroundQuery:=False
Next cn
在For循环中,您可以检查cn.Name以控制各个查询的执行。 Name属性遵循工作簿连接名称,例如"查询 - " &安培; PQ查询名称。