2

I am generating a scripting dictionary using one button on a userform, using it to populate a listbox, and then need to use that same dictionary using a second button on the form. I have declared my dictionary either using early binding as so:

Dim ISINDict As New Scripting.Dictionary

or late binding as so

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

When I try to pass the dictionary to the other button like so:

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

I get the following error: "Procedure declaration does not match description of event or procedure having the same name" on that line.

Any ideas what I'm doing wrong?

6
  • 4
    You cant add a parameter to the Click event. You need to find some other way to do what you are trying to do, like declaring the dictionary as a form level variable, then you can access it from either Click event. Commented Aug 14, 2017 at 17:13
  • ^ what he said. Also why on Earth would you want to late-bind the scripting runtime?? Are any of your users running IE5.5 or Windows XP? Commented Aug 14, 2017 at 17:23
  • FWIW you shouldn't declare a local variable As New. Dim foo As New Collection / Set foo = Nothing / Debug.Print foo.Count -> what do you expect to get? runtime error 91? If so, you've been bitten by the unintuitive behavior of As New. Commented Aug 14, 2017 at 17:25
  • Thank you all! I will take this into account. @braX How do a declare the dictionary as a form level variable? That seems like the best option! @ Mat's Mug I was doing that only to test it, as I was getting errors with the early binding and wanted to make sure it wasn't related - I'm pretty new to VBA so forgive my ignorance if there's no way it could have been! I'm confused by your second comment. Could you elaborate? Commented Aug 14, 2017 at 17:35
  • Dimension it outside of all subroutines. Commented Aug 14, 2017 at 17:38

2 Answers 2

1

An event handler has a specific signature, owned by a specific interface: you can't change the signature, otherwise the member won't match the interface-defined signature and that won't compile - as you've observed.

Why is that?

Say you have a CommandButton class, which handles native Win32 messages and dispatches them - might look something like this:

Public Event Click()

Private Sub HandleNativeWin32Click()
    RaiseEvent Click
End Sub

Now somewhere else in the code, you want to use that class and handle its Click event:

Private WithEvents MyButton As CommandButton

Private Sub MyButton_Click()
    'button was clicked
End Sub

Notice the handler method is named [EventSource]_[EventName] - that's something hard-wired in VBA, and you can't change that. And if you try to make an interface with public members that have underscores in their names, you'll run into problems. That's why everything is PascalCase (without underscores) no matter where you look in the standard libraries.

So the compiler knows you're handling the MyButton.Click event, because there's a method named MyButton_Click. Then it looks at the parameters - if there's a mismatch, something is wrong: that parameter isn't on the interface, so how is the event provider going to supply that parameter?. So it throws a compile-time error, telling you you need to either make the signature match, or rename the procedure so that it doesn't look like it's handling MyButton.Click anymore.

When you drop a control onto a form, you're basically getting a Public WithEvents Button1 As CommandButton module-level variable, for free: that's how you can use Button1 in your code to refer to that specific button, and also how its Click handler procedure is named Button1_Click. Note that if you rename the button but not the handler, the procedure will no longer handle the button's Click event. You can use Rubberduck's refactor/rename tool on the form designer to correctly rename a control without breaking the code.


Variables in VBA can be in one of three scopes: global, module, or procedure level.

When you do:

Sub DoSomething()
    Dim foo
End Sub

You're declaring a local-scope variable.

Every module has a declarations section at the top, where you can declare module-scope variables (and other things).

Option Explicit
Private foo

Sub DoSomething()
End Sub

Here foo is a module-scope variable: every single procedure in that module can access it - read and write.

So if you have data you want to pass between procedures and you can't alter their signatures, your next best option is to declare a module-scope variable.

[ignores global scope on purpose]


About As New - consider this:

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

This code blows up with run-time error 91 "object variable not set", because when foo.Add executes, foo's reference is Nothing, which means there's no valid object pointer to work with. Now consider this:

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

This code outputs 1, because As New keeps the object alive in a weird, unintuitive and confusing way. Avoid As New where possible.

Sign up to request clarification or add additional context in comments.

Comments

0

Declare the dictionary at the module level and fill it in button-1-click event handler. Then it can be simply re-used in button-2-click event handler. So there is no need to pass the dictionary to event handlers which is not possible either. HTH

Form module

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.