以编程方式确定VBA中OnClick事件的事件处理程序

时间:2017-09-20 17:59:55

标签: vba ms-access

我正在开发跟踪MS Access VBA中执行路径的应用程序。但是,我遇到了一个问题。您似乎可以动态分配处理程序来控制OnClick和VBA中的其他此类事件,但我没有看到从这些控件中读取事件的方法。例如:

With Forms("Order Entry").Controls("OK") 
 If .OnClick = "" Then 
 .OnClick = "fnProcessOrder" 
 End If 
End With 

这会将“fnProcessOrder”函数动态分配给“OK”按钮。但是,如果您尝试获取该按钮的值,则可以得到:

With Forms("Order Entry").Controls("OK") 
  Debug.Print "My Handler -->" + .OnClick
EndWith

结果:

--> [Event Procedure]

没有进一步的细节。这似乎是微软在这里所说的行为: https://msdn.microsoft.com/en-us/vba/access-vba/articles/commandbutton-onclick-property-access

但我想知道是否还有更进一步。

我原以为这些信息会出现在MSysObjects和MSysQueries表中,但我没有看到它。我尝试使用Database Documenter提取我的测试数据库,我也从生成的文档中获取了该值。

有没有办法找到分配给控件的VBA函数?

2 个答案:

答案 0 :(得分:1)

实际上,代码对我来说并不合适。如果要将VBA函数分配给事件过程,则必须使用与此类似的语法:.OnClick = "=fnProcess()"注意文字=() - 这些是进行访问的必需条件认识到你想要调用一个函数。

作为额外的考虑,你可以有一个类模块(我们称之为Observer),其代码类似于:

Private WithEvents ctlInterest As Access.Textbox

Public Sub Init(TargetControl As Access.Control)
  Set ctlInterest = TargetControl
  With ctlInterst
    If .OnClick = "" Then
      .OnClick = "[Event Procedure]"
    End If
  End With
End Sub

Private ctlInterest_OnClick()
  fnProcess()
End Sub

然后你可以用这样的形式部署类......

Private objObserver As Observer

Private Sub Form_Load()
  Set objObserver = New Observer
  objObserver.Init(Me.txtInterest)
End Sub   

将产生相同的结果,最终得到多个事件处理程序。

有关详情,this可能会有所帮助。

结合函数表达式可能的事实以及它可以随时动态分配的事实,它几乎需要代码路径分析来识别一般中所有事件处理程序的可能入口点。这意味着你需要Rubberduck的解析器。请注意,静态代码路径分析已计划但尚未实现,AIUI。

另外,我想确定 - 如果您打算构建一个用于错误处理的调用堆栈跟踪,您可能需要查看第三方加载项。例如,商业产品vbWatchDog可以在VBA中提供该信息。

答案 1 :(得分:1)

这将返回事件的完整代码。如果您只在该事件中使用一个子,它应该可以工作。

Public Sub TestModulePrinter()
    Dim subName As String
    With Forms("Order Entry").Controls("OK")
      subName = "Private Sub " & .Name & "_OnClick()"
    End With
    Dim i As Long
    Dim IsPrinting As Boolean
    With Forms("Order Entry").Module
        For i = 1 To .CountOfLines
            If Not IsPrinting Then
                If Trim(.Lines(i, 1)) = subName Then
                    IsPrinting = True
                End If
            Else
                If Trim(.Lines(i, 1)) = "End Sub" Then
                    IsPrinting = False
                Else
                    Debug.Print .Lines(i, 1)
                End If
            End If
        Next i
    End With
End Sub