我有一个 UserForm
我在其中添加了一个具有足够 Let 和 Get 语句的属性。我希望在属性更改时触发事件。事件,RaiseEvent
和例程已经说明,都在UserForm
模块。但是,我找不到将例程分配给事件的方法。
据我所知,这与类模块情况下通常的自定义事件不同,因为它没有在类模块中声明,我可以在常规模块中实例化其类。我所有的搜索都提供了类模块中的自定义事件或用户窗体中的内置事件的示例,但没有关于用户窗体中的自定义事件的 Material 。
这实际上让我想知道是否有可能在 UserForms 和 Private Subs 中创建自定义事件来处理这些事件。那么有可能做到吗?如果是这样,我错过了什么?如何制作 Private Sub UFStatus_StatusChange()
处理事件?
任何帮助将不胜感激!
到目前为止,代码都在 UserForm 模块中:
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
最佳答案
是的,但您需要先了解 VBA/COM 事件的工作原理。
一点背景...
注意到 VBE 代码 Pane 顶部的下拉菜单/组合框了吗?最左边的是列出所有可用的接口(interface)和事件提供者 - 最右边的是列出可用的成员和事件,这些成员和事件由最左边的下拉列表中选择的任何内容公开。
所以当你处理 Click
一些事件OkButton
在 UserForm1
的代码隐藏中,处理程序 stub 可能如下所示:
Private Sub OkButton_Click()
End Sub
签名以非常特殊的方式构造,始终以相同的方式构造,无论您是实现接口(interface)还是处理事件:
Private Sub [Provider]_[MemberName]([Args])
这强调了问题。无论你做什么,不要使用包含下划线的标识符命名事件(或接口(interface)成员)。如果发生事件,您将遇到编译错误:
Compile error: Invalid event name
对于接口(interface),您还会收到编译错误,VBE 会提示接口(interface)成员未实现。
这就是为什么一切都是
PascalCase
而不是 Upper_Snake_Case
在 VB 中。遵守约定,避免在公共(public)成员名称中使用下划线。如果您不确定接口(interface)是什么以及为什么我在有关事件的帖子中提到它们,请查找
Implements
关键字,知道接口(interface)和事件密切相关并且以非常相似的方式工作,并继续阅读;-)提供者
任何类都可以定义事件。一个
UserForm
作为一个类,它绝对可以定义事件,是的。您可以使用 Event
准确定义事件。关键词:Public Event SomethingHappened(ByVal SomeArg As Long)
定义事件的类是事件提供者——它是唯一允许引发它定义的事件的类。
您使用
RaiseEvent
引发事件关键字,并提供参数:Private Sub OnSomethingHappened()
RaiseEvent SomethingHappened(42)
End Sub
您何时以及为何提出事件完全取决于您的想象。
客户端
考虑
Click
CommandButton
的事件在 UserForm
: CommandButton
类可能有一个方法来监听涉及鼠标的 Win32 消息,当它决定处理左键单击时,它会引发它的 Click
事件,和一些东西和噗的OkButton_Click
程序运行。正确的?MSForms 自动为您做的部分是,当您添加
CommandButton
在表格上,并将其命名为 OkButton
,好吧这个OkButton
identifier 本质上成为表单上的公共(public)字段,就好像您添加了一个公共(public)的模块级变量一样:Public OkButton As MSForms.CommandButton
除了,它实际上看起来像这样:
Public WithEvents OkButton As MSForms.CommandButton
那
WithEvents
关键字使 OkButton
在左侧下拉列表中可用 - OkButton
成为事件提供者,其Click
事件可以被处理......在表单的代码隐藏中。CommandButton
类不知道也不关心它的 Click
的处理程序事件:事件提供者是OkButton
对象,客户端是 UserForm1
你在其中实现处理程序的类。换句话说,事件提供者和客户端是两个完全独立的类。
问题是
WithEvents
仅在类模块中是合法的。你可以让你的
UserForm1
事件提供者,但它不能处理自己的事件。在您的
UserForm1
中声明事件代码隐藏,并确保指定 ByVal
parameters 用于在处理程序站点上只读的参数 - 使用 ByRef
当处理程序可以修改值时,例如对于一些 Cancel As Boolean
处理程序返回时可以读取的参数:Public Event StatusChange(ByVal oldStatus As Long, ByVal newStatus As Long)
现在添加一个类模块,命名为
MyPresenter
:Option Explicit
Private WithEvents MyView As UserForm1
Private Sub Class_Initialize()
Set MyView = New UserForm1
End Sub
Private Sub Class_Terminate()
Set MyView = Nothing
End Sub
Public Sub ShowDialog()
MyView.Show
End Sub
选择
MyView
从最左边的下拉列表中;最右边的下拉菜单应包含 StatusChange
事件 - 并选择它应该创建一个处理程序 stub :Private Sub MyView_StatusChange(ByVal oldStatus As Long, ByVal newStatus As Long)
MsgBox "Status changed from " & oldStatus & " to " & newStatus & "!"
End Sub
现在,在您通常显示表单的标准/程序模块中,实例化该演示者类:
Public Sub Macro1()
With New MyPresenter
.ShowDialog
End With
End Sub
关于vba - 是否可以在自定义用户窗体中创建和处理自定义事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49660399/