表刷新后的Excel VBA QueryTable回调函数 [英] Excel VBA QueryTable callback function after table refresh

查看:308
本文介绍了表刷新后的Excel VBA QueryTable回调函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写/维护一个Excel VBA应用程序,其中有多个QueryTables链接到MS SQL Server数据库。应用程序的用户可以通过操作Excel文档上的各种UI控件来将SQL查询更改为每个表。



我遇到的QueryTables之一是使用多线程。文档上的每个QueryTable具有在运行查询后必须还原的原始状态。例如,如果QueryTable1具有基本查询

 从example_table 
/ pre>

,用户选择控件上的某些输入来创建

 从example_table选择*其中object_oid ='10'

我需要恢复原始状态。下面的代码是我目前正在完成这个

  Sub RefreshDataQuery()
'依赖关系:Microsoft Scripting Dictionary(HashTable)对象的运行时(Tools->引用)

Dim querySheet As Worksheet
Dim interface As Worksheet

设置querySheet = Worksheets(QTable )
Set interface = Worksheets(Interface)

Dim sh As Worksheet
Dim qt As QueryTable
Dim qtDict As New Scripting.Dictionary

设置qtDict = UtilFunctions.CollectAllQueryTablesToDict

设置qt = qtDict.Item(从fred2查询

'''构建SQL查询字符串'''
Dim sqlQueryString As String
Dim originalQueryCache As String
originalQueryCache = qt.CommandText
sqlQueryString = qt.CommandText

QueryBuilder.BuildSQLQueryStringFromInterface接口,sqlQueryString

MsgBox sqlQueryString

qt.CommandText = sqlQueryString

如果不是qt是没有了
qt.Refresh
Else
'错误消息和处理这里
'切出来保持代码短
结束如果


'''CLEAN UP'''

'恢复原始基本SQL查询
'问题在这里
'这个或任何其他改变的语句会出错如果查询仍然刷新
qt.CommandText = originalQueryCache
'其他原始状态恢复代码如下...

'免费字典
设置qtDict = Nothing


End Sub

理想情况下,如果我在写这个另一种现代语言,我将创建一个回调函数或运行刷新在我自己的线程与完成通知程序。我花了很多时间研究如何获得qt.Refresh调用的回调函数,但没有运气。我意识到我可以黑客一下,但是我宁愿不从事坏的做法,因为许多人将来必须保持这一点。



此应用程序必须支持Excel 2010版本和向上



那么如何为VBA函数创建一个回调函数这是在单独的线程运行?或者,我应该看另一种方法吗?

解决方案

QueryTables事件不会被公开,除了通过自定义类模块和WithEvents关键字。首先,创建一个名为CQtEvents的自定义类模块,并将其放在其中:

  Private WithEvents mQryTble As QueryTable 
Private msOldSql作为String

公共属性集QryTble(ByVal QryTble As QueryTable):设置mQryTble = QryTble:End Property
公共属性获取QryTble()作为QueryTable:Set QryTble = mQryTble:End Property
公共属性让OldSql(ByVal sOldSql As String):msOldSql = sOldSql:End Property
公共属性获取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

设置qtDict = UtilFunctions.CollectAllQueryTablesToDict
设置qt = qtDict.Item(从fred2查询

sqlQueryString = qt.CommandText
QueryBuilder.BuildSQLQueryStringFromInterface接口,sqlQueryString

'为事件创建类,存储旧的sql
设置clsQtEvents =新的CQtEvents
设置clsQtEvents.QryTble = qt
clsQtEvents.OldSql = qt.CommandText

qt.CommandText = sqlQueryString

如果不是qt是没有,然后
qt.Refresh完成后,类中的事件将触发
Else
'错误消息和处理这里
结束如果

End Sub

由于您使用WithEvents定义mQryTble,所以它在事件(BeforeRefresh和AfterRefresh)中都会显示在类中。通过将CQtEvents.QryTble设置为您的QueryTable,该类然后监听该QueryTable上的事件。在更改之前,CommandText存储在OldSql中。然后当刷新完成时,事件触发并恢复CommandText。当然不是刷新是在事件中完成的,但我假设你想要旧的sql语句,如果它被刷新或重新处理。



接下来,你应该考虑做一个集合类来保存一堆QtEvents实例。我以你的代码处理为例,但你真的在做更多的事情。然后,您可以将CollectAllQueryTables移动到该集合类中,并将BuildSQL部分移动到CQtEvents类中。


I am writing/maintaining an Excel VBA application where there are multiple QueryTables linked to MS sql server databases. Users of the application can alter the SQL query to each table by manipulating various UI controls on the Excel document.

One of the issues I have come across with QueryTables is there use of multi threading. Each QueryTable on the document has an original state that must be restored after a query is ran. For instance, if QueryTable1 had a base query of

Select * from example_table

and the user selected certain inputs on the controls to create

Select * from example_table Where object_oid = '10'

I would need the original state to be restored. The code below is a snapshot of how I am currently accomplishing this

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

Ideally, if I was writing this in another modern language, I would create a callback function or run the refresh in my own thread with a completion notifier. I spent a good chunk of time researching how to get a callback function for the qt.Refresh call, but am having no luck. I realize I could 'hack' around this a bit but I would prefer to not engage in bad practices as many people will have to maintain this in the future.

This application must support Excel 2010 versions and upward

So how can I create a callback function for VBA functions that are run in separate threads? Or, should I be looking at another approach?

解决方案

The QueryTables events aren't exposed except through a custom class module and the WithEvents keyword. First, create a custom class module named CQtEvents and put this in it:

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

That's two properties: one to hold the QueryTable and one to store the old sql. Then your procedure would look something like

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

Because you define mQryTble with WithEvents, its two events (BeforeRefresh and AfterRefresh) are exposed in the class. By setting CQtEvents.QryTble to your QueryTable, the class then listens for events on that QueryTable. The CommandText is stored in OldSql before it's changed. Then when the Refresh is done, the event fires and the CommandText is restored. Of course not Refresh is done in the event, but I assume you want the old sql statement in there if it's refreshed or reprocessed.

Next, you should consider making a collection class to hold a bunch of QtEvents instances. I presume your code processes one as an example, but you're really doing more. Then you can move your CollectAllQueryTables inside that collection class and move the BuildSQL part inside the CQtEvents class.

这篇关于表刷新后的Excel VBA QueryTable回调函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆