问题描述
我有一个UserForm
,在其中添加了带有适当的Let和Get语句的属性.我想在属性更改时触发事件.已经在UserForm
模块中声明了事件RaiseEvent
和例程.但是,我找不到将例程分配给事件的方法.
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.
据我所知,这与类模块"中通常的自定义事件"情况不同,因为它不是在可以在常规模块中实例化其类的类模块"中声明的.我所有的搜索都提供了类模块中的自定义事件或UserForms中的内置事件的示例,但没有UserForms中的自定义事件的材料.
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.
这实际上使我想知道是否有可能在UserForms和Private Subs中创建自定义事件来处理这些事件.那有可能做到吗?如果是这样,我想念的是什么?如何让Private Sub UFStatus_StatusChange()
处理事件?
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?
任何帮助将不胜感激!
到目前为止,代码都包含在UserForm模块中:
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
推荐答案
是的,但是您需要先了解VBA/COM事件是如何工作的.
Yes, but you need to understand how VBA/COM events work first.
是否注意到VBE的代码窗格顶部的下拉列表/组合框?最左边的一个列出所有可用的接口和事件提供程序-最右边的一个列出所有可用的成员和事件通过最左侧的下拉菜单中选择的内容即可显示.
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.
因此,当您在UserForm1
背后的代码中处理某些OkButton
的Click
事件时,处理程序存根可能看起来像这样:
So when you handle the Click
event of some OkButton
in the code-behind of UserForm1
, the handler stub might look like this:
Private Sub OkButton_Click()
End Sub
无论是实现接口还是处理事件,签名都是以非常特殊的方式构造的,始终采用相同的方式:
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:
Private Sub [Provider]_[MemberName]([Args])
下划线很重要.无论您做什么,都请勿使用包含下划线的标识符来命名事件(或接口成员).如果发生事件,您将遇到编译错误:
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:
在使用接口的情况下,您还会收到编译错误,VBE会抱怨未实现接口成员.
In the case of an interface, you'll also get a compile error, with the VBE complaining that interface members aren't implemented.
这就是为什么在VB中所有内容都是PascalCase
而不是Upper_Snake_Case
的原因.遵守约定,避免在公共成员名称中加下划线.
This is why everything is PascalCase
and not Upper_Snake_Case
in VB. Stick to the convention, avoid underscores in public member names.
如果您不确定什么是接口以及为什么我在有关事件的帖子中提到它们,请查找Implements
关键字,知道接口和事件之间有着密切的联系,并且以非常相似的方式工作,并且继续阅读;-)
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 ;-)
任何 class 都可以定义事件. UserForm
是一个类,它可以绝对定义事件,是的.您可以使用Event
关键字来准确定义事件的完成方式:
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 the Event
keyword:
Public Event SomethingHappened(ByVal SomeArg As Long)
定义事件的类是事件 provider -它是唯一允许引发其定义的事件的类.
The class that defines the event is an event provider - it is the only class that is allowed to raise the events it defines.
您可以使用RaiseEvent
关键字引发事件,并提供参数:
You raise events using the RaiseEvent
keyword, and provide the arguments:
Private Sub OnSomethingHappened()
RaiseEvent SomethingHappened(42)
End Sub
何时以及为何发起活动完全取决于您的想象力.
When and why you raise events is entirely up to your imagination.
考虑UserForm
上CommandButton
的Click
事件:CommandButton
类可能具有侦听涉及鼠标的Win32消息的方法,并且当它决定处理左键单击时,它引发它的Click
事件,并运行某些东西和OkButton_Click
过程的of声.对吧?
Consider the Click
event of a CommandButton
on a UserForm
: the CommandButton
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 its Click
event, and something something and poof the OkButton_Click
procedure runs. Right?
MSForms为您自动完成的部分是,当您在表单上添加CommandButton
并将其命名为OkButton
时,该OkButton
标识符实际上就变成了表单上的公共字段,就像您添加了一个公共的模块级变量:
The part MSForms is automagically doing for you, is that when you add a CommandButton
on the form, and name it OkButton
, well this OkButton
identifier essentially becomes a public field on the form, as if you had added a public, module-level variable:
Public OkButton As MSForms.CommandButton
除了,它实际上看起来像这样:
Except, it actually looks like this:
Public WithEvents OkButton As MSForms.CommandButton
该WithEvents
关键字使OkButton
在左侧的下拉列表中可用-OkButton
成为事件提供程序,并且其Click
事件可以在...中处理表单的代码隐藏.
That WithEvents
keyword makes the OkButton
available in the left-side dropdown - OkButton
becomes an event provider, and its Click
event can be handled... in the form's code-behind.
CommandButton
类不知道或关心其Click
事件的处理程序:事件提供者是OkButton
对象,而 client 是UserForm1
类您正在其中实现处理程序.
The CommandButton
class doesn't know or care about a handler for its Click
event: the event provider is the OkButton
object, and the client is the UserForm1
class you're implementing the handlers in.
换句话说,事件提供者和客户端是两个完全独立的类.
In other words, the event provider, and the client, are two completely separate classes.
要注意的是,WithEvents
仅在类模块中是合法的.
The catch is that WithEvents
is only legal in a class module.
您可以将您的UserForm1
设置为事件提供程序,但是它不能处理自己的事件.
You can make your UserForm1
an event provider, but it cannot handle its own events.
在代码UserForm1
中声明事件,并确保为要在处理程序站点上只读的参数指定ByVal
参数-当处理程序可以修改值时,请使用ByRef
,例如对于处理程序返回时可以读取的某些Cancel As Boolean
参数:
Declare the events in your UserForm1
code-behind, and make sure you specify ByVal
parameters for parameters that are meant to be read-only at the handler site - use ByRef
when the handler can modify the value, e.g. for some Cancel As Boolean
parameter that you can read when the handler returns:
Public Event StatusChange(ByVal oldStatus As Long, ByVal newStatus As Long)
现在添加一个类模块,将其命名为MyPresenter
:
Now add a class module, call it 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
事件-选择该事件应创建一个处理程序存根:
Select MyView
from the leftmost dropdown; the rightmost dropdown should contain the StatusChange
event - and selecting it should create a handler stub:
Private Sub MyView_StatusChange(ByVal oldStatus As Long, ByVal newStatus As Long)
MsgBox "Status changed from " & oldStatus & " to " & newStatus & "!"
End Sub
现在,在通常显示表单的标准/过程模块中,实例化该presenter类:
Now, in the standard/procedural module where you were normally showing your form, instantiate that presenter class instead:
Public Sub Macro1()
With New MyPresenter
.ShowDialog
End With
End Sub
这篇关于是否可以在自定义用户窗体中创建和处理自定义事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!