Назначение обработчиков событий элементам управления в форме пользователя, динамически создаваемой в VBA

Я нашел много ресурсов в интернете, что делать почти что я хочу сделать, но не совсем.У меня есть именованный диапазон "daylist". Для каждого дня в списке я хочу создать кнопку в форме пользователя, которая будет запускать макрос для этого дня. Я в состоянии добавить кнопки динамически но не знаю, как передать daycell.текст из именованного диапазона, к кнопке, к обработчику событий, к макро: S Heres код, который я должен создать пользователю форма:

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

Так для каждого из моих runButtons, он должен отображать daycell.текст и запустите макрос, который использует тот же текст в качестве параметра для выбора листа для выполнения его материала.

любая помощь будет удивительным. Я видел ответы, которые динамически записывают код vba для обработки макросов, но я поверьте, должен быть какой-то способ, который можно сделать немного более элегантно через параметры прохождения, просто не уверен, как. Большое спасибо заранее!

2 ответов


Я знаю, что вы приняли решение, которое будет работать для вас и намного проще, чем ниже, но если вам интересно, это будет более прямой ответ на ваш вопрос.

вам нужно создать класс для обработки щелчков кнопки, поэтому каждый раз, когда кнопка нажата, она использует событие в классе, вам нужно сделать это только один раз, а затем создать новый экземпляр для каждой кнопки. Чтобы эти классы не выходили из области видимости и не терялись, их необходимо хранить в классе декларация уровня. Ниже я немного переместил ваш код.

в модуле класса (я назвал его 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, чтобы понять), однако, как только вы их поймете и как они работают, они станут невероятно полезными:)

надеюсь, что это помогает

EDIT-для решения дополнительных запросов

чтобы ссылаться на объекты в коллекции, это делается либо через ключ, либо через индекс. Чтобы использовать ключ, вам нужно добавить его, как вы добавляете элемент в коллекцию, так:

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

это поможет? Для дальнейшего чтения я бы указал вам на сайт чипа Пирсона, у него есть отличный материал по большинству тем http://www.cpearson.com/excel/Events.aspx

просто помните, что VBA основан на VB6, поэтому не полностью оперившийся язык OO, например, не поддерживает наследование в обычном смысле, только наследование интерфейса

надеюсь, что это помогает :)


пример ловли нажмите на лист. Поместите это в модуль рабочего листа:

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