将参数传递给事件处理程序过程

时间:2017-08-14 17:05:36

标签: vba excel-vba excel

我正在使用userform上的一个按钮生成脚本字典,使用它来填充列表框,然后需要使用表单上的第二个按钮来使用相同的字典。我已经使用早期绑定声明了我的字典:

Dim ISINDict As New Scripting.Dictionary

或后期绑定

Dim ISINDict as Object
...
Set ISINDict = CreateObject("Scripting.Dictionary")

当我尝试将字典传递给其他按钮时:

Private Sub OKButton_Click(ISINDict as Scripting.Dictionary) 'if early binding
Private Sub OKButton_Click(ISINDict as Object) 'if late binding

我收到以下错误:“程序声明与该行上具有相同名称的事件或程序的描述不匹配。

任何想法我做错了什么?

2 个答案:

答案 0 :(得分:1)

事件处理程序具有特定签名,由特定接口拥有:您无法更改签名,否则该成员将不匹配接口定义的签名,并且无法编译 - 正如您所观察到的那样。

为什么?

假设您有一个CommandButton类,它处理本机Win32消息并调度它们 - 可能看起来像这样:

Public Event Click()

Private Sub HandleNativeWin32Click()
    RaiseEvent Click
End Sub

现在代码中的其他地方,您希望使用该类并处理其Click事件:

Private WithEvents MyButton As CommandButton

Private Sub MyButton_Click()
    'button was clicked
End Sub

请注意,处理程序方法名为[EventSource]_[EventName] - 这是VBA中的硬连接,您无法更改它。如果您尝试与名称中包含下划线的公共成员建立接口,则会遇到问题。这就是为什么一切都是PascalCase(没有下划线),无论你在标准库中的哪个位置。

因此,编译器知道您正在处理MyButton.Click事件,因为有一个名为MyButton_Click的方法。然后它查看参数 - 如果存在不匹配,则表示存在错误:该参数不在接口上,那么事件提供程序将如何提供该参数?。因此它会抛出编译时错误,告诉您需要进行签名匹配,或重命名该过程,使其看起来不再处理MyButton.Click

当您将控件放到表单上时,您基本上可以免费获得Public WithEvents Button1 As CommandButton模块级变量:这就是您在代码中使用Button1来引用该特定按钮的方式,以及它的Click处理程序如何命名为Button1_Click。请注意,如果重命名按钮但不重命名处理程序,则该过程将不再处理按钮的Click事件。您可以在表单设计器上使用Rubberduck重构/重命名工具,在不破坏代码的情况下正确重命名控件。

VBA中的变量可以位于以下三个范围之一:全局,模块或过程级别。

当你这样做时:

Sub DoSomething()
    Dim foo
End Sub

您正在声明本地范围变量

每个模块顶部都有一个声明部分,您可以在其中声明模块范围变量(以及其他内容)。

Option Explicit
Private foo

Sub DoSomething()
End Sub

此处foo模块范围变量:该模块中的每个过程都可以访问它 - 读取和写入。

因此,如果您希望在程序之间传递数据并且无法更改其签名,那么您的下一个最佳选择是声明模块范围变量。

[故意忽略全球范围]

关于As New - 请考虑一下:

Public Sub Test()
    Dim foo As Collection
    Set foo = New Collection
    Set foo = Nothing
    foo.Add 42
    Debug.Print foo.Count
End Sub

此代码因运行时错误91“对象变量未设置”而爆炸,因为foo.Add执行时foo的引用为Nothing,这意味着没有有效对象使用指针。现在考虑一下:

Public Sub Test()
    Dim foo As New Collection
    Set foo = Nothing
    foo.Add 42
    Debug.Print foo.Count
End Sub

此代码输出1,因为As New以一种奇怪的,不直观且令人困惑的方式使对象保持活着状态。在可能的情况下尽量避免使用As New

答案 1 :(得分:0)

在模块级别声明字典并将其填入button-1-click事件处理程序。然后可以在按钮2单击事件处理程序中简单地重复使用它。因此,不需要将字典传递给事件处理程序,这也是不可能的。 HTH

  

表单模块

Option Explicit

' Declare dictionary at the user form module level
Private ISINDict As Scripting.Dictionary

Private Sub CommandButton1_Click()
    FillDictionary
End Sub

Private Sub CommandButton2_Click()
    ' Use the dictionary filled in event handler of CommandButton-1
End Sub

Private Sub FillDictionary()
    With ISINDict
        .Add "Key-1", "Itm-1"
        .Add "Key-2", "Itm-2"
        .Add "Key-3", "Itm-3"
    End With
End Sub

Private Sub UserForm_Initialize()
    Set ISINDict = New Scripting.Dictionary
End Sub