2013-08-07 58 views
0

我正在編寫/維護一個Excel VBA應用程序,其中有多個QueryTable鏈接到MS sql server數據庫。通過操作Excel文檔上的各種UI控件,應用程序的用戶可以將SQL查詢更改爲每個表。表刷新後的Excel VBA QueryTable回調函數

我遇到的QueryTables的一個問題是使用多線程。文檔上的每個QueryTable都有一個原始狀態,在查詢運行後必須進行恢復。例如,如果QueryTable1具有

Select * from example_table 

一個基本查詢和選擇上的控件某些輸入來創建

Select * from example_table Where object_oid = '10' 

我需要原始狀態要恢復的用戶。下面的代碼是我當前如何完成這個

Sub RefreshDataQuery() 
'Dependencies: Microsoft Scripting Runtime (Tools->References) for Dictionary (HashTable) object 

Dim querySheet As Worksheet 
Dim interface As Worksheet 

Set querySheet = Worksheets("QTable") 
Set interface = Worksheets("Interface") 

Dim sh As Worksheet 
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 ''' 
Dim sqlQueryString As String 
Dim originalQueryCache As String 
originalQueryCache = qt.CommandText 
sqlQueryString = qt.CommandText 

QueryBuilder.BuildSQLQueryStringFromInterface interface, sqlQueryString 

MsgBox sqlQueryString 

qt.CommandText = sqlQueryString 

If Not qt Is Nothing Then 
    qt.Refresh 
Else 
    'Error Messages and handling here 
    ' Cut out to keep code short 
End If 


''' CLEAN UP ''' 

'Restore the original base SQL query 
' Problem is here 
' This, or any other altering statement, will error out if the query is still refreshing 
qt.CommandText = originalQueryCache 
' Other original state restoring code below... 

' Free the dictionary 
Set qtDict = Nothing 


End Sub 

理想的快照,如果我是在另一種現代語言寫這個,我會創造一個回調函數或者是在我自己的線程與完成通知運行刷新。我花了大量的時間研究如何獲得qt.Refresh調用的回調函數,但沒有運氣。我意識到我可以'繞'這一點,但我寧願不參與不好的做法,因爲很多人將來不得不維護這一點。

此應用程序必須支持Excel 2010的版本和向上

所以,我怎麼可以創建了在獨立線程中運行VBA函數的回調函數?或者,我應該看看另一種方法嗎?

+0

對不起,不知道我是否正確。是[這個'AfterRefresh事件'](http://msdn.microsoft.com/en-us/library/ff835922%28v=office.14%29.aspx)可能是你在找什麼? –

+0

可能就是這樣!我需要的是刷新功能已完成運行的某種指標。 –

回答

1

QueryTables事件不會公開,除非通過自定義類模塊和WithEvents關鍵字。首先,創建一個自定義類模塊名爲CQtEvents,並把這個在它:

Private WithEvents mQryTble As QueryTable 
Private msOldSql As String 

Public Property Set QryTble(ByVal QryTble As QueryTable): Set mQryTble = QryTble: End Property 
Public Property Get QryTble() As QueryTable: Set QryTble = mQryTble: End Property 
Public Property Let OldSql(ByVal sOldSql As String): msOldSql = sOldSql: End Property 
Public Property Get OldSql() As String: OldSql = msOldSql: End Property 

Private Sub mQryTble_AfterRefresh(ByVal Success As Boolean) 

    Me.QryTble.CommandText = Me.OldSql 

End Sub 

這兩個屬性:一個持有的QueryTable和一個存放舊的SQL。然後,你的程序將看起來像

Sub RefreshDataQuery() 

    Dim interface As Worksheet 
    Dim qt As QueryTable 
    Dim qtDict As New Scripting.Dictionary 
    Dim clsQtEvents As CQtEvents 
    Dim sqlQueryString As String 

    Set qtDict = UtilFunctions.CollectAllQueryTablesToDict 
    Set qt = qtDict.Item("Query from fred2") 

    sqlQueryString = qt.CommandText 
    QueryBuilder.BuildSQLQueryStringFromInterface interface, sqlQueryString 

    'Create class for events and store old sql 
    Set clsQtEvents = New CQtEvents 
    Set clsQtEvents.QryTble = qt 
    clsQtEvents.OldSql = qt.CommandText 

    qt.CommandText = sqlQueryString 

    If Not qt Is Nothing Then 
     qt.Refresh 'after this is done, the event in the class will fire 
    Else 
     'Error Messages and handling here 
    End If 

End Sub 

因爲你WITHEVENTS定義mQryTble,它的兩個事件(BeforeRefresh和AfterRefresh)暴露在類。通過將CQtEvents.QryTble設置爲QueryTable,該類將監聽該QueryTable上的事件。 CommandText在更改之前存儲在OldSql中。然後,刷新完成後,事件觸發並恢復CommandText。當然不是刷新是在事件中完成的,但我假設你想在那裏刷新或重新處理舊的sql語句。

接下來,您應該考慮製作一個集合類來容納一堆QtEvents實例。我認爲你的代碼是一個例子,但你真的做得更多。然後,您可以將CollectionAllQueryTables移動到該集合類中,並將BuildSQL部件移入CQtEvents類中。

+0

非常感興趣且有幫助 –

+0

只是好奇,但爲什麼你選擇使用'讓'而不是'設置'myQryTble的第三個屬性? –

+0

OldSql是一個字符串,所以我們是適當的屬性。Set用於對象(QueryTables,Workbooks,Ranges),Let用於非對象數據類型(String,Long,Double,Boolean) –