问题描述
我有以下代码以编程方式/动态创建框架并添加一个选项按钮:
I have the following code that programmatically / dynamically creates a frame and adds an option button:
Private Sub ComboBox1_Change()
Dim cb1234Frame As MsForms.Frame
Dim opbtn1 As MsForms.OptionButton
Set cb1234Frame = RT_Graph_Form.Controls.Add("Forms.Frame.1")
With cb1234Frame
.Top = 132
.Left = 12
.Height = 30
.Width = 144
.Caption = "Number of Graphs to Display"
End With
Set opbtn1 = cb1234Frame.Controls.Add("Forms.OptionButton.1")
With opbtn1
.Top = 6
.Left = 6
.Height = 18
.Width = 21.75
.Caption = "1"
End With
End Sub
但是这不起作用:
Private Sub opbtn1_Click()
MsgBox "Test Successful!!"
End Sub
推荐答案
问题在于事件处理程序需要在编译时绑定:您不能为动态创建的控件创建事件处理程序。
The problem is that event handlers need to be bound at compile-time: you cannot create an event handler for a dynamically created control.
添加新的类模块转到项目,将其命名为 DynamicOptionButton
。此类的作用是包装MSForms控件并对其进行编译时引用:
Add a new class module to your project, call it DynamicOptionButton
. The role of this class is to wrap the MSForms control and have a compile-time reference to it:
Option Explicit
Private WithEvents Wrapper As MSForms.OptionButton
Public Sub Initialize(ByVal ctrl As MSForms.OptionButton)
Set Wrapper = ctrl
End Sub
Private Sub Wrapper_Click()
MsgBox "Works!"
End Sub
请注意,只有一部分事件可以处理:什么事件可用,具体取决于您使用- MSForms.Control
声明包装引用的接口,其中有许多事件(和属性), MSForms .OptionButton
还有另一套设置:您可能需要声明两个接口(例如,同一对象的2个包装器)才能访问所有成员。
Note that only a subset of the events will be available to handle: what events are available, depend on the interface you're declaring the wrapper reference with - MSForms.Control
has a number of events (and properties), MSForms.OptionButton
has another set: you may need to declare both interfaces (i.e. 2 wrappers for the same object) in order to access all the members.
现在在表单的声明部分中,您需要保留对所有包装的引用,否则对象将超出范围,并且处理程序将无法正常工作。 集合
可以做到:
Now in your form's declarations section, you'll need to hold a reference to all wrappers, otherwise the objects just fall out of scope and the handlers won't work. A Collection
can do that:
Option Explicit
Private ControlWrappers As Collection
Private Sub UserForm_Initialize()
Set ControlWrappers = New Collection
End Sub
'...
Private Sub CreateOptionButton()
Dim ctrl As MSForms.OptionButton
Set ctrl = Me.Controls.Add("Forms.OptionButton.1")
'set properties...
Dim wrap As DynamicOptionButton
Set wrap = New DynamicOptionButton
wrap.Initialize ctrl
ControlWrappers.Add wrap
End Sub
小心不要在表单自身的代码中引用表单的类名:global-scope RT_Graph_Form
标识符是指VBA控制的默认实例自动实例化的对象,该对象可能是也可能不是所显示的实际表单实例。您要将动态控件添加到 Me.Controls
,而不是 RT_Graph_Form.Controls
。
Be careful to never reference the form's class name in the form's own code-behind: the global-scope RT_Graph_Form
identifier refers to a VBA-controlled "default instance" auto-instantiated object that may or may not be the actual form instance that's being shown. You want to add your dynamic controls to Me.Controls
, not RT_Graph_Form.Controls
.
现在,我们可以处理运行时产生的控件事件,但是还有另一个问题: DynamicOptionButton
类中的事件处理程序没有引用
Now, we can handle events of controls spawned at run-time, but there's another problem: the event handler in the DynamicOptionButton
class has no reference to the form it's on!
还是它?
每个MSForms控件的父项
属性;您可以通过递归地使用 Parent
属性,直到返回的引用为<$ c,来获取父 UserForm
$ c> UserForm -从那里您可以访问公开公开的所有内容。
Every MSForms control has a Parent
property; you can get ahold of the parent UserForm
by recursively going up the Parent
property until the returned reference is a UserForm
- and from there you can access everything that's publicly exposed.
这篇关于click事件不适用于以编程方式/动态创建的选项按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!