3

I want to handle the click on an CommandButton from within a class using the following class code

Option Explicit

Private m_First                     As MSForms.CommandButton
Private WithEvents evFirst          As MSForms.CommandButton

Property Get First() As MSForms.CommandButton
    Set First = m_First
End Property
Property Let First(ByRef o As MSForms.CommandButton)
    Set m_First = o
    Set evFirst = o
End Property

Private Sub evFirst_Click()
    MsgBox "It Worked!"
End Sub

In addition to it not working, am wondering why the reference for the Button in the form is different from that in the class, ie:

Sub Tester()
    Dim f As New UserForm1
    Dim o As New cButtonClass
    o.First = f.CommandButton1

    Dim k1 As LongLong: k1 = ObjPtr(o.First)
    Dim k2 As LongLong: k2 = ObjPtr(f.CommandButton1)
    Debug.Assert k1 = k2 'NOPE!
End Sub

Why doesn't this work? What is the fix?

14
  • 2
    There are 2 problems I see. First, in your class change the Property Let to Property Set. Second, in the form code you need to say Set o.First. There may be other issues, too. Commented Oct 10, 2018 at 19:14
  • 1
    Also, the evFirst object is identical to the m_First object. The WithEvents keyword simply allows you to write custom code for object you declared WithEvents for. Essentially, you don't need that m_First variable.... Commented Oct 10, 2018 at 19:29
  • 2
    It's not a coding style thing. It's about meeting expectations. Also, if your class has a default member, let-coercion will literally change how the member works, with identical code - that's objectively wrong, not a style thing at all. One is correct, the other is begging for future bugs to happen. Commented Oct 10, 2018 at 20:24
  • 3
    @Berryl - ObjPtr is not reliable after it has been wrapped in a Variant. You need to use VarPtr in that case. Debug.Assert VarPtr(o.First) = VarPtr(f.CommandButton1) should pass. Commented Oct 10, 2018 at 20:58
  • 1
    Depends what you're testing. If you're testing the form because that's where your logic is, you have a design problem (Smart UI [anti]-pattern) and need to refactor towards a more MVP-like solution. I don't write unit tests for view-level logic, so my one-liner answer would be "you don't". How would you go about doing the exact same thing in C#? You wouldn't, because you wouldn't have the form running the show in C# - so, do the same in VBA! (several articles about that on my blog) Commented Oct 10, 2018 at 21:12

1 Answer 1

2

Here is updated code reflecting the above comments. It works as expected. However, I don't have an answer yet for your other question concerning ObjPtr.

Here's the class code:

Option Explicit

Private WithEvents evFirst As MSForms.CommandButton

Property Get First() As MSForms.CommandButton
    Set First = evFirst
End Property

Property Set First(ByRef o As MSForms.CommandButton)
    Set evFirst = o
End Property

Private Sub evFirst_Click()
    MsgBox "Class Click"
End Sub

Here's the sheet code:

Option Explicit

Public Sub Tester()
    Dim f As UserForm1
    Dim o As cButtonClass

    Set f = New UserForm1
    Set o = New cButtonClass
    Set o.First = f.CommandButton1
    f.Show vbModal

    Dim k1 As LongPtr: k1 = ObjPtr(o.First)
    Dim k2 As LongPtr: k2 = ObjPtr(f.CommandButton1)
    Debug.Assert k1 = k2 'NOPE!
End Sub

Here's the userform code:

Private Sub CommandButton1_Click()
   MsgBox "UserForm Click"
End Sub
Sign up to request clarification or add additional context in comments.

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.