I have a UserForm
to which I added a property with the adequate Let and Get statements. I would like to have an event fire when the property changes. Event, RaiseEvent
and Routine have been stated, all in the UserForm
module. However, I can't find a way to assign the routine to the event.
For what I know, this is different from the usual Custom Events in Class Modules situation, because it is not declared in a Class Module whose Class I can instantiate in a regular module. All my searches provided examples to Custom Events in Class Modules or Built-In Events in UserForms, but no material on Custom Events in UserForms.
This actually made me wonder if it is at all possible to create Custom Events in UserForms and Private Subs to handle those Events. So is it possible to do it? And if so, what am I missing? How can I make Private Sub UFStatus_StatusChange()
handle the event?
Any help would be apreciated!
So far, the code goes, all in the UserForm module:
Public Event StatusChange (old_status As Long, current_status As Long)
Dim old_status As Long
Private current_status As Long
Public Property Let UFStatus (RHS As Long)
old_status = current_status
current_status = RHS
RaiseEvent StatusChange(old_status, current_status)
End Property
Private Sub UFStatus_StatusChange()
MsgBox("Status changed from " & old_status & "to " & current_status)
End Sub
Yes, but you need to understand how VBA/COM events work first.
A bit of background...
Notice the dropdowns/comboboxes at the top of the VBE's code panes? The leftmost one is listing all available interfaces and event providers - the rightmost one is listing the available members and events exposed by whatever is selected in the leftmost dropdown.
So when you handle the
Click
event of someOkButton
in the code-behind ofUserForm1
, the handler stub might look like this:The signature is constructed in a very particular way, always in the same manner, regardless of whether you're implementing an interface or handling an event:
That underscore matters. Whatever you do, DO NOT name an event (or interface member) with an identifier that contains an underscore. In the case of an event, you'll hit a compile error:
In the case of an interface, you'll also get a compile error, with the VBE complaining that interface members aren't implemented.
This is why everything is
PascalCase
and notUpper_Snake_Case
in VB. Stick to the convention, avoid underscores in public member names.If you're not sure what interfaces are and why I'm mentioning them in a post about events, lookup the
Implements
keyword, know that interfaces and events are very intimately related and work in a very similar fashion, and keep reading ;-)The Provider
Any class can define events. A
UserForm
being a class, it can absolutely define events, yes. You define events exactly how you've done, using theEvent
keyword:The class that defines the event is an event provider - it is the only class that is allowed to raise the events it defines.
You raise events using the
RaiseEvent
keyword, and provide the arguments:When and why you raise events is entirely up to your imagination.
The Client
Consider the
Click
event of aCommandButton
on aUserForm
: theCommandButton
class probably has a method that listens for Win32 messages involving the mouse, and when it decides to handle a left-button click, it raises itsClick
event, and something something and poof theOkButton_Click
procedure runs. Right?The part MSForms is automagically doing for you, is that when you add a
CommandButton
on the form, and name itOkButton
, well thisOkButton
identifier essentially becomes a public field on the form, as if you had added a public, module-level variable:Except, it actually looks like this:
That
WithEvents
keyword makes theOkButton
available in the left-side dropdown -OkButton
becomes an event provider, and itsClick
event can be handled... in the form's code-behind.The
CommandButton
class doesn't know or care about a handler for itsClick
event: the event provider is theOkButton
object, and the client is theUserForm1
class you're implementing the handlers in.In other words, the event provider, and the client, are two completely separate classes.
The catch is that
WithEvents
is only legal in a class module.You can make your
UserForm1
an event provider, but it cannot handle its own events.Declare the events in your
UserForm1
code-behind, and make sure you specifyByVal
parameters for parameters that are meant to be read-only at the handler site - useByRef
when the handler can modify the value, e.g. for someCancel As Boolean
parameter that you can read when the handler returns:Now add a class module, call it
MyPresenter
:Select
MyView
from the leftmost dropdown; the rightmost dropdown should contain theStatusChange
event - and selecting it should create a handler stub:Now, in the standard/procedural module where you were normally showing your form, instantiate that presenter class instead: