将事件处理程序分配给在VBA中dynamic创build的用户表单上的控件

我发现互联网上的许多资源几乎做我想做的,但不完全。我有一个命名范围“daylist”。 对于dayList中的每一天,我想在用户窗体上创build一个button来运行当天的macros。 我能够dynamic添加button,但不知道如何将daycell.text从命名范围,button,事件处理程序传递到macros:S Heres我必须创build用户表单的代码:

Sub addLabel() ReadingsLauncher.Show vbModeless Dim theLabel As Object Dim labelCounter As Long Dim daycell As Range Dim btn As CommandButton Dim btnCaption As String For Each daycell In Range("daylist") btnCaption = daycell.Text Set theLabel = ReadingsLauncher.Controls.Add("Forms.Label.1", btnCaption, True) With theLabel .Caption = btnCaption .Left = 10 .Width = 50 .Top = 20 * labelCounter End With Set btn = ReadingsLauncher.Controls.Add("Forms.CommandButton.1", "runButton", True) With btn .Caption = "Run Macro for " & btnCaption .Left = 80 .Width = 80 .Top = 20 * labelCounter ' .OnAction = "btnPressed" End With labelCounter = labelCounter + 1 Next daycell End Sub 

为了解决上述问题,我目前提示用户input他们想要运行的date(例如Day1),并将其传递给macros,

 Sub B45runJoinTransactionAndFMMS() loadDayNumber = InputBox("Please type the day you would like to load:", Title:="Enter Day", Default:="Day1") Call JoinTransactionAndFMMS(loadDayNumber) End Sub Sub JoinTransactionAndFMMS(loadDayNumber As String) xDayNumber = loadDayNumber Sheets(xDayNumber).Activate -Do stuff End Sub 

因此,对于我的每个runButton,都需要显示daycell.text,并运行一个使用相同文本作为参数的macros来select工作表来完成它的工作。

任何帮助都是极好的。 我看到了dynamic写入vba代码的响应,以处理macros,但我相信必须有一些通过传递参数可以做得更加优雅,只是不知道如何。 提前谢谢了!

我知道你已经接受了一个现在可以为你工作的解决scheme,比下面更简单,但如果你有兴趣,这将是对你的问题更直接的答案。

您需要创build一个类来处理button点击,所以每次点击button时,都会使用类中的事件,您只需要执行一次,然后为每个button创build一个新的实例。 为了阻止这些超出范围和丢失的类,它们需要存储在一个类的声明中。 在下面我已经移动你的代码一点。

在类模块(我称之为cButtonHandler)

 Public WithEvents btn As MSForms.CommandButton Private Sub btn_Click() MsgBox btn.Caption End Sub 

使用事件是因为它允许您使用控件的大部分事件。 我已经将button生成代码移动到用户窗体中,如下所示:

 Dim collBtns As Collection Private Sub UserForm_Initialize() Dim theLabel As Object Dim labelCounter As Long Dim daycell As Range Dim btn As CommandButton Dim btnCaption As String 'Create a variable of our events class Dim btnH As cButtonHandler 'Create a new collection to hold the classes Set collBtns = New Collection For Each daycell In Range("daylist") btnCaption = daycell.Text Set theLabel = ReadingsLauncher.Controls.Add("Forms.Label.1", btnCaption, True) With theLabel .Caption = btnCaption .Left = 10 .Width = 50 .Top = 20 * labelCounter End With Set btn = ReadingsLauncher.Controls.Add("Forms.CommandButton.1", "runButton", True) With btn .Caption = "Run Macro for " & btnCaption .Left = 80 .Width = 80 .Top = 20 * labelCounter 'Create a new instance of our events class Set btnH = New cButtonHandler 'Set the button we have created as the button in the class Set btnH.btn = btn 'Add the class to the collection so it is not lost 'when this procedure finishes collBtns.Add btnH End With labelCounter = labelCounter + 1 Next daycell End Sub 

然后我们可以从一个单独的例程调用useform:

 Sub addLabel() ReadingsLauncher.Show vbModeless End Sub 

VBA中的类没有特别好的涵盖在许多VBA书籍中(一般你需要阅读VB6书籍才能理解),但是一旦你理解了它们以及它们的工作方式,它们就变得非常有用:)

希望这可以帮助

编辑 – 解决额外的查询

要引用集合中的对象,可以通过键或索引来完成。 要使用密钥,您需要在将项目添加到集合时添加它,所以:

 collBtns.Add btnH 

会成为

 collBtns.Add btnH, btnCaption 

为此,密钥必须是唯一的。 您可以参考如下:

 'We refer to objects in a collection via the collection's key 'Or by it's place in the collection 'So either: MsgBox collBtns("Monday").btn.Caption 'or: MsgBox collBtns(1).btn.Caption 'We can then access it's properties and methods 'NB you won't get any intellisense collBtns("Monday").btn.Enabled = False 

如果需要,还可以将其他属性/方法添加到您的类中,例如:

 Public WithEvents btn As MSForms.CommandButton Private Sub btn_Click() MsgBox btn.Caption End Sub Public Property Let Enabled(value As Boolean) btn.Enabled = value End Property 

然后将被访问:

 collBtns("Monday").Enabled = False 

这有帮助吗? 为了进一步阅读,我会指向你的芯片皮尔逊的网站,他有大多数的主题http://www.cpearson.com/excel/Events.aspx伟大的东西;

请记住,VBA基于VB6,所以不是一个完全成熟的OO语言,例如,它不支持在正常意义上的inheritance,只有接口inheritance

希望这可以帮助 :)

捕获单击工作表上的示例。 把它放在工作表模块中:

 Private Sub Worksheet_SelectionChange(ByVal Target As Range) ' eg, range(A1:E1) is clicked If Not Application.Intersect(Target, Range("A1:E1")) Is Nothing Then MsgBox "You clicked " & Target.Address End If End Sub