2015-10-26 720 views
1

我遇到竞争条件问题,我有两个QueryTables,每个都挂上了它自己的AfterRefresh事件。每个AfterRefresh事件都会执行一些copy'n'pasting以及一些计算。等待,直到Excel刷新全部(Ctrl + Alt + F5)完成 - VBA

现在,当用户单击刷新所有(Ctrl+Alt+F5)在Excel中,我希望有每个AfterRefresh处理程序执行,但只有在所有QueryTable刷新完全完成后。

我做StackOverflow上进行搜索,并someone suggested

Activeworkbook.RefreshAll 
DoEvents 

然而,这是假设,我们以编程方式触发RereshAll。在我的情况下,刷新全部是通过Excel中的内置刷新全部(Ctrl+Alt+F5)按钮完成的。因此,我没有看到我可以在哪里插入DoEvents(除非我创建了我自己的“全部刷新”按钮,但我想避免这样做)。

我试图搜索“Excel VBA互斥体”,但我没有找到任何特别的东西。那么,如何在每个AfterRefresh处理程序发生之前确保所有刷新都已完成?

感谢您的阅读!

更新:帮助调试..这里是我的VBA代码。

我有一个模块名为AutoOpen

Dim S As New DataCopy 
Dim U As New DataCopy 

Sub Auto_Open() 
    Set S.qt = ThisWorkbook.Sheets(1).QueryTables(2) 
    S.myWorkbookName = ThisWorkbook.Name 
    S.sWorksheetProcessName = "ProcessS" 
    S.sWorksheetDataColumnStart = 1 
    S.sWorksheetDataColumnEnd = 5 
    Set U.qt = ThisWorkbook.Sheets(1).QueryTables(1) 
    U.myWorkbookName = ThisWorkbook.Name 
    U.sWorksheetProcessName = "ProcessU" 
    U.sWorksheetDataColumnStart = 6 
    U.sWorksheetDataColumnEnd = 10 
End Sub 

我也有一类名为模块DataCopy

Public WithEvents qt As QueryTable 
Public myWorkbookName As String 
Public sWorksheetProcessName As String 
Public sWorksheetDataColumnStart As Integer 
Public sWorksheetDataColumnEnd As Integer 

Private Sub qt_AfterRefresh(ByVal Success As Boolean) 
    DataCopier 
End Sub 

Private Sub DataCopier() 
    'Debug.Print sWorksheetProcessName & "," & Application.CalculationState 
    Dim LastNRows As Integer 
    Dim sWorksheetDataName As String 

    ' How many rows to copy 
    LastNRows = 297 
    sWorksheetDataName = "Data" 

    Application.ScreenUpdating = False 

    ' Clear content in process tab 
    With Workbooks(myWorkbookName).Worksheets(sWorksheetProcessName) 
     .Range(.Cells(4, 1), .Cells(.Cells(Rows.Count, 1).End(xlUp).Row, 6)).ClearContents 
    End With 

    ' Copy to process Tab 
    With Workbooks(myWorkbookName).Worksheets(sWorksheetDataName) 
     LastRow = .Cells(Rows.Count, 1).End(xlUp).Row 
     FirstRow = LastRow - LastNRows 
     If FirstRow < 2 Then 
      FirstRow = 2 
     End If 
     .Range(.Cells(FirstRow, sWorksheetDataColumnStart), .Cells(LastRow, sWorksheetDataColumnEnd)).Copy _ 
      Destination:=Workbooks(myWorkbookName).Worksheets(sWorksheetProcessName).Range("A4") 
    End With 

    Debug.Print (sWorksheetProcessName & "," & sWorksheetDataColumnStart & "," & sWorksheetDataColumnEnd) 

    Application.ScreenUpdating = True 
End Sub 

由于竞争条件的,只有一个AfterRefresh处理程序成功地copy'n'pasting。另一个不工作,直到我再次单击刷新全部按钮(Ctrl+Alt+F5)

+0

也许每个'AfterRefresh'事件处理程序可以以'Activeworkbook.RefreshAll'结尾,后面跟着'DoEvents',也许有一些切换布尔公共变量来阻止回声事件。 –

+0

@JohnColeman,但用​​户已经点击了内置的全部刷新按钮,所以如果我为每个'AfterRefresh'添加'Activeworkbook.RefreshAll'并不意味着它实质上是刷新两次? 其实,在第二个想法,因为它是在'AfterRefresh'内,它会触发无限循环刷新?因为'AfterRefresh'会触发另一次刷新,这会再次触发'AfterRefresh' – Antony

+0

现在我可以更清楚地看到问题了。也许在每个事件处理程序的开始处包含一个定时器循环,在循环体中带有一个“DoEvents”。每个事件处理程序可以暂停几秒钟,同时允许刷新完成。它甚至可以像在每个处理程序的开始处包含一个“DoEvents”一样简单。你将需要某种kludge,因为Excel并不是专为多线程而设计的。另外 - 我不记得现在是什么,我曾经遇到过连续需要两个“DoEvents”的情况。作为一个实验开始2 @ –

回答

0

如果明确VBA触发Activeworkbook.RefreshAll然后之前DoEvents代码后DoEvents的作品,你想要在事件中运行时,处理程序应覆盖由Ctrl+Alt+F5触发刷新的情况。因此,开始每个事件处理程序的行DoEvents

0

变化查询不允许背景刷新,他们不会放弃控制权,直到刷新

Location of Background refresh

+0

这是可以从Connection属性UI完成的东西吗?或者这只在VBA中完成?因为如果它在代码中,我不知道是否需要投资逻辑来检查它是否已设置为False。尽管如此,DoEvents看起来还是可以做到的,但我也会尝试一下。 – Antony

+0

添加了哪里可以找到背景刷新选项的图片 – SeanC

+0

谢谢肖恩。我明白了为什么你有这个选择,但我不这样做,因为我的外部资源只是两个不断更新的CSV文件,而不是SQL连接。 – Antony