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

11

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

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

Чтобы обойти вышеупомянутую проблему, я в настоящее время предлагаю пользователю ввести день, который он хочет запустить (например, День 1), и передать его макросу, и он работает:

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

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

Не знаю, как я могу захватить события щелчка внутри рабочей таблицы, хотя, если бы я мог, это могло бы сделать для более простого взаимодействия, условного форматирования и т. Д .... BiGXERO
эт выглядит близко к тому, что вы хотите. OTOH, почему бы не создать комбинированный список, который заполняется изdaylist и единственная командная кнопка, которая может прочитать выбранное значение? andy holaday
Я добавил это как ответ, потому что поле для комментариев не очень хорошо для размещения многострочного кода. andy holaday
Думаю, мне придется пойти на этот выбор. Поскольку макрос используется для проверки и выгрузки данных, я надеялся, что смогу создать небольшой экран запуска, который будет заполняться строкой для каждой даты и отображать количество записей, количество ошибок и т. Д. кнопки для каждой из них просто сделали бы это немного более удобным для пользователя. Однако, если я не найду более простого решения, я думаю, что ваше предложение будет победителем. BiGXERO
Еще одна мысль ... Возможно, вы могли бы составить таблицу соответствующей информации (номер дня, # записи, # ошибки и т. Д.) В специальном рабочем листе и фиксировать там события кликов. andy holaday

Ваш Ответ

2   ответа
18

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

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

В модуле класса (я назвал его cButtonHandler)

Public WithEvents btn As MSForms.CommandButton

Private Sub btn_Click()
    MsgBox btn.Caption
End Sub

With events используется, так как позволяет вам использовать большинство событий для элемента управления. Я переместил код генерации кнопок в форму пользователя, как показано ниже:

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.asp

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

Надеюсь это поможет :

Спасибо огромное за подробный ответ. Все еще остается вопрос, использовать ли пользовательскую форму или листы, но это отличный ответ, особенно, как вы упомянули, классы, кажется, избегаются (или супер технически объяснены). Я изменил ваш ответ на реальный, поскольку именно он отвечает на вопро BiGXERO
Ваш ответ вселил в меня уверенность (после того, как я закончил текущее решение на основе листов), чтобы попробовать пользовательские формы. 2 вопроса, которые, как мне кажется, помогут мне и другим новичкам, 1) Если я хочу сослаться на конкретный btn в коллекции (например, чтобы изменить заголовок или сделать его невидимым) позже, могу ли я обратиться к нему, используя коллекции ссылка (например, collBtns.btn [2]) 2) Знаете ли вы какой-либо онлайн-ресурс, который объясняет коллекции, обработчики событий и т. д. Имея Java-фон noob, мне нравится идея быть более объектно-ориентированной и менее управляемой событиями BiGXERO
Я изменил вышесказанное, чтобы ответить на твои вопросы SWa
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
Я думаю, ты что-то делаешь. Если я использую метод пересечения, то используйте что-то по линииcells(dayCell, 6, чтобы перебрать мой именованный диапазон, это могло бы стать достойным крика простым решением моей проблемы. Проверим и доложим. Большое спасибо BiGXERO
оно работает! чтобы проверить код, который я использую:Sub intersectCallingMacro() Range("D8").Select 'used for testing only Set target = ActiveCell If Not Application.Intersect(target, Range("A1:M100")) Is Nothing Then Call testIntersect(ActiveCell.Value) End If End Sub Sub testIntersect(dayString As String) Dim xDayString As String xDayString = dayString Sheets(xDayString).Activate End Sub (извиняюсь за форматирование кода) Передает значение ячейки в макрос в качестве параметра. УДИВИТЕЛЬНО BiGXERO

Похожие вопросы