Назначение обработчиков событий элементам управления в форме пользователя, динамически создаваемой в 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