是否可以在自定义用户窗体中创建和处理自定义事件?

LFB*_*LFB 7 excel vba excel-vba

我有一个UserForm带有足够的Let和Get语句的属性.当房产发生变化时,我希望事件发生.RaiseEvent已经说明了事件和例程,所有这些都在UserForm模块中.但是,我找不到将例程分配给事件的方法.

据我所知,这与通常的类模块中的自定义事件情况不同,因为它未在类模块中声明,其类I可以在常规模块中实例化.我的所有搜索都提供了类模块中的自定义事件或UserForms中的内置事件的示例,但没有关于UserForms中自定义事件的材料.

这实际上让我想知道是否可以在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
Run Code Online (Sandbox Code Playgroud)

Mat*_*don 13

是的,但您需要了解VBA/COM事件的工作原理.

一点背景......

注意VBE代码窗格顶部的下拉/组合框?最左边的列表是列出所有可用的接口事件提供者 - 最右边的列出了最左边下拉列表中所选内容所公开的可用成员事件.

因此,当您在代码隐藏中处理Click某些事件时,处理程序存根可能如下所示:OkButtonUserForm1

Private Sub OkButton_Click()

End Sub
Run Code Online (Sandbox Code Playgroud)

无论您是实现接口还是处理事件,签名都以非常特殊的方式构建,始终以相同的方式构建:

Private Sub [Provider]_[MemberName]([Args])
Run Code Online (Sandbox Code Playgroud)

这很重要.无论您做什么,都不要使用包含下划线的标识符来命名事件(或接口成员).在事件的情况下,您将遇到编译错误:

编译错误:无效的事件名称

对于接口,您还会收到编译错误,VBE抱怨接口成员未实现.

这就是为什么一切PascalCase,而不是Upper_Snake_Case在VB.坚持惯例,避免在公共成员名称中下划线.

如果您不确定接口是什么以及为什么我在关于事件的帖子中提及它们,请查找Implements关键字,知道接口和事件非常密切相关并以非常类似的方式工作,并继续阅读;-)


提供者

任何都可以定义事件.一个UserForm是一类,它可以绝对确定的事件,是的.您可以使用Event关键字定义事件的确切方式:

Public Event SomethingHappened(ByVal SomeArg As Long)
Run Code Online (Sandbox Code Playgroud)

定义事件的类是一个事件提供者 - 它是唯一允许引发它定义的事件的类.

您使用RaiseEvent关键字引发事件,并提供参数:

Private Sub OnSomethingHappened()
    RaiseEvent SomethingHappened(42)
End Sub
Run Code Online (Sandbox Code Playgroud)

你举办活动的时间和原因完全取决于你的想象力.


客户端

考虑Click的一个事件CommandButtonUserForm:在CommandButton类可能有监听涉及鼠标的Win32消息的方法,当它决定处理左键点击,它会引发它的Click事件,以及一些东西和噗的OkButton_Click程序运行.对?

MSForms自动为你做的部分是,当你CommandButton在表单上添加一个并命名它时OkButton,这个OkButton标识符本质上就成了表单上的公共字段,好像你已经添加了一个公共的模块级变量:

Public OkButton As MSForms.CommandButton
Run Code Online (Sandbox Code Playgroud)

除此之外,它看起来像这样:

Public WithEvents OkButton As MSForms.CommandButton
Run Code Online (Sandbox Code Playgroud)

WithEvents关键字OkButton在左侧下拉列表中可用 - OkButton成为事件提供者,并且Click可以在表单的代码隐藏中处理其事件.

CommandButton类并不知道也不关心它的处理程序Click事件:事件提供者是OkButton对象,而客户UserForm1你在执行处理程序类.

换句话说,事件提供者和客户端是两个完全独立的类.


问题是WithEvents在类模块中只是合法的.

您可以创建UserForm1一个事件提供程序,但它无法处理自己的事件.

UserForm1代码隐藏中声明事件,并确保ByVal为处理程序站点上的只读参数指定参数 - ByRef在处理程序可以修改值时使用,例如对于Cancel As Boolean处理程序时可以读取的某些参数收益:

Public Event StatusChange(ByVal oldStatus As Long, ByVal newStatus As Long)
Run Code Online (Sandbox Code Playgroud)

现在添加一个类模块,调用它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
Run Code Online (Sandbox Code Playgroud)

MyView从最左边的下拉列表中选择; 最右边的下拉列表应该包含StatusChange事件 - 选择它应该创建一个处理程序存根:

Private Sub MyView_StatusChange(ByVal oldStatus As Long, ByVal newStatus As Long)
    MsgBox "Status changed from " & oldStatus & " to " & newStatus & "!"
End Sub
Run Code Online (Sandbox Code Playgroud)

现在,在您通常显示表单的标准/过程模块中,实例化该演示者类:

Public Sub Macro1()
    With New MyPresenter
        .ShowDialog
    End With
End Sub
Run Code Online (Sandbox Code Playgroud)

  • 史诗!!顺便说一句,这个解释非常清楚!非常感谢!! (2认同)