EXCEL VBA: код пользовательской формы, улучшение повторяющегося кода

Есть ли более эффективный способ написать этот код пользовательской формы?

Private Sub Userform_Initialize()

'do stuff

    With Item1_DropDown
        .AddItem "Monday"
        .AddItem "Tuesday"
        .AddItem "Wednesday"
    End With

    With Item2_DropDown
        .AddItem "Monday"
        .AddItem "Tuesday"
        .AddItem "Wednesday"
    End With

    With Item3_DropDown
        .AddItem "Monday"
        .AddItem "Tuesday"
        .AddItem "Wednesday"
    End With

'and so on, and so on. (I have about fifty of these 'With/End With' blocks)

End Sub

По сути, я хотел бы иметь возможность перестать писать так много этих блоков «With/End With». Прямо сейчас у меня есть этот тип кода в четырех моих пользовательских формах аналогичной конструкции. Это занимает так много текстового пространства, и это кажется пустой тратой времени. Есть лучший способ это сделать?

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

Спасибо,

Элиас


person Elias    schedule 16.07.2013    source источник
comment
Разбейте код addItem на отдельный подраздел, который принимает раскрывающийся список в качестве параметра. Затем вы можете назвать это как PopulateList Item1_DropDown. Вы также можете еще больше упростить, перебирая все элементы управления в форме, проверяя тип каждого, а затем, если это раскрывающийся список, передайте его своему подчиненному для заполнения. Если у вас есть несколько типов раскрывающихся списков в одной и той же форме, вы можете рассмотреть возможность использования какого-либо правила именования, которое вы можете использовать для определения того, как обрабатывается каждый из них.   -  person Tim Williams    schedule 17.07.2013
comment
О да; Я понимаю что ты имеешь ввиду. Я реализовал нечто подобное. По сути, я создал цикл For, который прокручивает каждый ComboBox с похожим названием и добавляет в него все элементы. Единственная некрасивая вещь в этом — это то, что у меня есть строка кода для каждого элемента.   -  person Elias    schedule 17.07.2013


Ответы (3)


Разделите свой код на подпрограммы :)

Предполагая, что ваши раскрывающиеся списки являются ComboBox элементами управления формой:

Private Sub Userform_Initialize()

    PopulateDropDown Item1_DropDown
    PopulateDropDown Item2_DropDown
    PopulateDropDown Item3_DropDown

End Sub

Sub PopulateDropDown(cb as MSForms.ComboBox)

    With cb
        .AddItem "Monday"
        .AddItem "Tuesday"
        .AddItem "Wednesday"
    End With

End Sub
person David Zemens    schedule 16.07.2013
comment
Спасибо. Это было очень ясно и легко реализовать. Я действительно должен был подумать об этом заранее. Спасибо. - person Elias; 17.07.2013
comment
Вопрос №1: Почему этот код не работает, когда я помещаю аргумент в скобки. Когда я пишу PopulateDropDown(Item1_DropDown), он выдает ошибку («требуется объект». Но когда я удаляю эти закрывающие скобки, он работает? - person Elias; 17.07.2013
comment
Вопрос № 2: Какие еще есть способы сделать что-то подобное? - person Elias; 17.07.2013
comment
# 1, потому что значение по умолчанию для поля со списком - это ... значение. Круглые скобки сообщают компилятору, что нужно оценить, что находится внутри них, поэтому вы вызываете функцию PopulateDropDown с понедельником или вторником и т. д., что явно не является ожидаемым объектом ComboBox. Подробное обсуждение см. в этом сообщении Daily Dose. . - person Doug Glancy; 17.07.2013
comment
# 1 запустите его без круглых скобок. - person David Zemens; 17.07.2013
comment
# 2 вы можете сделать это в цикле, но я не пробовал, что также может потребоваться дополнительная логика. Вы также можете установить combobox.list, используя split(var, ","), где var — строка с разделителями-запятыми. - person David Zemens; 17.07.2013

Кроме того, вы можете заполнить элементы, используя List:

Private Sub UserForm_Initialize()
    Me.ComboBox1.List = Array("Monday", "Tuesday", "Wednesday")
End Sub

Вы можете определить этот массив (вариант) один раз:

Private days As Variant

Private Sub UserForm_Initialize()
    days = Array("Monday", "Tuesday", "Wednesday")
    Me.ComboBox1.List = days

End Sub

Я бы также использовал свойство Tag (каждого поля со списком), чтобы различать те, которые должны быть заполнены этими значениями:

Private days As Variant

Private Sub UserForm_Initialize()
    Dim ctl As Control

    days = Array("Monday", "Tuesday", "Wednesday")

    For Each ctl In Me.Controls
        If TypeOf ctl Is ComboBox And ctl.Tag = "days" Then
            ctl.List = days
        End If
    Next ctl

End Sub
person Andy G    schedule 16.07.2013
comment
Да, да, да. Я должен был использовать List вместо AddItem. Я действительно предпочитаю загружать массив в Combobox вместо того, чтобы делать это один за другим. В качестве окончательного решения я использовал цикл For, который загружает один и тот же массив во все мои ComboBox. Большое тебе спасибо. - person Elias; 17.07.2013
comment
Вы очень кстати. Свойство Tag также полезно, так как вы можете просто установить его для тех полей со списком, которые необходимо заполнить, и не беспокоиться об их именах. - person Andy G; 17.07.2013
comment
Ах. Теперь я знаю, что делает это свойство Tag. Спасибо. Возможно, мне придется использовать это. - person Elias; 17.07.2013
comment
Аналогичный вопрос № 1: у меня есть около 45 меток, подписи которых могут быть изменены при использовании пользователем DoubleClick. К сожалению, у меня есть куча любительского повторяющегося кода для захвата двойных кликов по каждой метке. Таким образом, у меня есть 45 частных подпрограмм для моих 45 меток. Есть ли более эффективный способ сделать это? Я постараюсь провести некоторое исследование, пока эта страница обновляется. - person Elias; 17.07.2013
comment
Можно иметь один обработчик событий для нескольких элементов управления, но для этого требуется создание отдельного класса. пример. Я бы спросил, почему у вас так много элементов управления в одной форме (50 полей со списком, предположительно текстовые поля и т. д.).. но это зависит от вас;) - person Andy G; 17.07.2013
comment
А, спасибо. Я начал использовать модули классов. Предоставленная вами ссылка помогла мне начать работу (хотя последовало много исследований, испытаний и сомнений). Я использовал модуль класса (включенный в коллекции и теги управления), чтобы значительно сократить код пользовательской формы. - person Elias; 22.07.2013
comment
Кстати, мои пользовательские формы имеют многостраничное управление. Вот почему у меня так много выпадающих списков, меток, текстовых полей и кнопок. Я думал об использовании вместо этого элемента управления MultiTab, но решил, что, поскольку некоторые из моих страниц отличаются, это не будет хорошей идеей. Я никогда не использовал MultiTab; может быть, я был неправ (?). - person Elias; 22.07.2013
comment
Вы имеете в виду TabStrip? С элементом управления MultiPage вы должны заполнить каждую страницу различным набором элементов управления. С помощью TabStrip вы можете использовать один набор элементов управления и использовать событие Change (насколько я помню) TabStrip, чтобы просто изменить значения, отображаемые в этих элементах управления. Таким образом, TabStrip намного лучше, если каждая из ваших страниц содержит, по существу, один и тот же набор и расположение элементов управления. - person Andy G; 22.07.2013
comment
Наличие такого количества элементов управления в одной форме может привести к тому, что загрузка формы займет много времени, если вам нужно заполнить значения элемента управления, и это пугает пользователя. Как правило, это указывает на то, что структуру ваших данных (или базы данных) необходимо пересмотреть или упростить, добавив кнопки для создания других подчиненных (возможно, модальных) форм. Другой вариант — не заполнять страницы, кроме первой, до тех пор, пока пользователь впервые не посетит страницу (щелкнув ее вкладку), и отображать некоторые элементы управления только тогда, когда они необходимы. - person Andy G; 22.07.2013
comment
Ой. Ну, я обречен на данный момент. Я просто привык к Multipage, и мне не нравились непосредственные элементы управления на TabStrip. Поэтому я выбрал MultiPage. К счастью, мои пользовательские формы по-прежнему загружаются в мгновение ока. Я беспокоился о производительности, но это еще не было проблемой. Я посмотрю, как это происходит на других машинах. Спасибо - person Elias; 23.07.2013
comment
Всего в мои 5 пользовательских форм загружено более 620 значений. Одна из пользовательских форм фактически извлекает свои значения из всех других. Пока что ни один из них не требует заметного количества времени для загрузки. Я пробовал два ноутбука (2,00 ГГц и 1,30 ГГц каждый с 4 ГБ ОЗУ). Однако, когда я обновлю эту книгу, я попытаюсь использовать TabStrip. Что вы думаете об этом? - person Elias; 23.07.2013
comment
Если (когда!) дойдет до этого, один из вариантов - скопировать всю форму и удалить некоторые вкладки из этих двух форм, имея кнопку для отображения другой формы. Другой вариант — удалить элементы для элементов управления и установить их программно только в первый раз, когда они потребуются. Это все равно повлияет на производительность, но первоначальная загрузка формы будет быстрее, что беспокоит большинство пользователей. В любом случае удачи ;) - person Andy G; 23.07.2013

изучите настройку подпрограммы для ее заполнения. Подробнее здесь...

http://www.vbforums.com/showthread.php?607511-RESOLVED-Excel-VBA-Как-добавлять-те-же-элементы-в-несколько-полей-списков

person user2366842    schedule 16.07.2013
comment
Прохладно. Спасибо за ресурс. - person Elias; 17.07.2013
comment
Наблюдение № 1: из вашей связанной темы решение второго парня не работает для меня. MS Excel 2013, похоже, не распознает команду «Фигуры». Ошибка компиляции: метод или элемент данных не найден. Хотя, если я изменю его на Controls И удалю этот OLE-материал, он будет работать. - person Elias; 17.07.2013
comment
Если источник использует shapes, вероятно, потому что он работает с элементами управления на рабочем листе, а не с элементами управления на фирме пользователя. - person David Zemens; 17.07.2013