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

时间:2012-05-15 00:10:26

标签: excel vba excel-vba excel-2007

我在互联网上找到了很多资源几乎我想做什么,但并不完全。我有一个命名范围“daylist”。对于dayList中的每一天,我想在用户表单上创建一个按钮,该按钮将运行当天的宏。我能够add the buttons dynamically但不知道如何将daycell.text从命名范围,按钮,事件处理程序传递到宏:S继承我必须创建用户表单的代码:< / p>

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

为了解决上述问题,我目前提示用户输入他们想要运行的日期(例如Day1)并将其传递给宏并且它有效:

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,并运行一个宏,该宏使用相同的文本作为参数来选择工作表来完成它的工作。

任何帮助都会很棒。我已经看到动态写入vba代码的响应,以处理宏,但我相信必须有一些通过传递参数可以更优雅地完成,只是不确定如何。非常感谢提前!

2 个答案:

答案 0 :(得分:19)

我知道您已经接受了一个适合您的解决方案,并且比下面的方法简单得多,但如果您感兴趣,这将是您问题的更直接的答案。

您需要创建一个类来处理按钮单击,因此每次单击该按钮时它都会使用该类中的事件,您只需要执行此操作一次,然后为每个按钮创建一个新实例。要阻止这些类超出范围并丢失,它们需要存储在类级别声明中。在下面我已经将你的代码移动了一点。

在类模块中(我称之为cButtonHandler)

Public WithEvents btn As MSForms.CommandButton

Private Sub btn_Click()
    MsgBox btn.Caption
End Sub

使用事件,因为它允许您将大多数事件用于控件。我已将按钮生成代码移动到用户窗体中,如下所示:

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
'N.B 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

这有帮助吗?为了进一步阅读,我会指向Chip Pearson的网站,他在大多数主题上都有很棒的东西http://www.cpearson.com/excel/Events.aspx

请记住,VBA基于VB6,因此不是完全成熟的OO语言,例如,它不支持正常意义上的继承,只有接口继承

希望这会有所帮助:)

答案 1 :(得分:2)

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

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
  ' e.g., 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