本文介绍了是否可以在自定义用户窗体中创建和处理自定义事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个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背后的代码中处理某些OkButtonClick事件时,处理程序存根可能看起来像这样:

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.

考虑UserFormCommandButtonClick事件: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

这篇关于是否可以在自定义用户窗体中创建和处理自定义事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-27 08:29