セットアップ:
問題の説明
以下に示す「試したこと:」セクションで説明されているように、任意の手法を任意に組み合わせて使用し、PowerQueryが更新を完了するのを待った後。 [クエリと接続]ペインに表示される更新インジケーター(スピナー?)アイコンに従って、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
コードノート:
質問
この問題にご関心をお寄せいただき、ありがとうございます。情報を含めるのを忘れた場合、またはこの質問をより明確にするために何かを言い換える必要がある場合は、コメントで知らせてください。
@neurojellyの痛みを理解しました。私はそこに行ったことがあります。しかし、結局のところ、解決策は非常に単純であり、VBAを使用していません。 [クエリのプロパティ]ウィンドウで、[バックグラウンド更新を有効にする]のチェックを外してチェックを外し、DoEvents
を使用する必要があります。私はこの方法を1年以上使用しているので、これが機能することは確かです。
コードが含まれているサンプルファイルへのリンクを見つけてください。
https://drive.google.com/open?id=1ZLxSMEXPLda3QhaQoTyGGv3_sC-tpN-X
2番目の質問については、Iferror/OnEror
メソッドを使用して、クエリがエラーを返すかどうかを検出できますが、必ずしもクエリのエラーをチェックするわけではありません。クエリ自体がエラーポップアップを返しているかどうかを識別します。エラーポップアップは、VBAコードの実行中にデフォルトでスキップされます。この方法はほとんどの場合機能しますが、常に機能するとは限りません。
これが回避策です
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で作業を続行できるようにし、更新が完了したらメッセージを表示するようにします。ただし、上記のようにOnTime
呼び出し元ではなく、Do... Loop
反復を使用して実装する場合は、先に進んでください。
最後に、Public WithEvents qt As QueryTable
の下にコールバックがあり、qt.refreshing
を探します。この方法も使用できます。
お役に立てれば。