Я делаю пользовательскую форму в VBA, где у меня есть текстовое поле, где я могу искать элементы. У меня около 30 текстовых полей, поэтому я хочу сократить код, используя цикл, вместо того, чтобы копировать и вставлять один и тот же код 30 раз.
Проблема: я не знаю, как пройти через общедоступную переменную
Public Variable: Public oEventHandler(Number) As New clsSearchableDropdown
oEventHandler would go from 1 to 30 (e.g oEventHandler2,oEventHandler3...oEventHandler30)
clsSearchableDropdown is the Class Module for the search feature
Text Box: TextBox(Number)
ListBox: ListBox(Number)
Вот исходный код (нет проблем только для сравнения):
With oEventHandler1
' Attach the textbox and listbox to the class
Set .SearchListBox = Me.ListBox1
Set .SearchTextBox = Me.TextBox1
' Default settings
.MaxRows = 10
.ShowAllMatches = True
.CompareMethod = vbTextCompare
.WindowsVersion = False
End With
Вот что я пытаюсь сделать:
Dim i As Integer
for i = 1 to 30
With Me.Controls.Item("oEventHandler" & i)
' Attach the textbox and listbox to the class
Set .SearchListBox = Me.Controls.Item("ListBox" & i)
Set .SearchTextBox = Me.Controls.Item("TextBox" & i)
' Default settings
.MaxRows = 10
.ShowAllMatches = True
.CompareMethod = vbTextCompare
.WindowsVersion = False
End With
Next i
Я знаю, что oEventHandler не является элементом управления, но есть ли аналогичный код, который я могу использовать для перебора общедоступной переменной?
@WordNerd Кажется, что это не работает, когда я запускаю его. Это дает мне ошибку времени выполнения (не удалось найти указанный объект) на: With Me.Controls.Item("oEventHandler" & i)
Я также зациклился, хотя все элементы управления в моей пользовательской форме и oEventHandler
не подошли.
Если приведенный ниже ответ Karma не работает для вашего проекта, вам может быть полезно создать собственный класс «коллекции» для ваших пользовательских объектов класса clsSearchableDropdown. Затем вы сможете повторять их как членов этой коллекции, как вы это делаете сейчас с пользовательскими элементами управления формы. См. этот ответ , а также эту документацию от MS по VB, которая должна быть справедливой и для VBA.
@WordNerd Спасибо! Вы направили меня в правильном направлении. Я разместил код, который работал для меня.
Если я правильно вас понял, в пользовательской форме у вас есть 30 текстовых полей и 30 списков, где каждое текстовое поле (N) предназначено для поиска значения в списке (N), расположенном под этим текстовым полем (N). Так это выглядит примерно так :
Слева находится TextBox01, под TextBox01 находится ListBox01
Справа находится TextBox02, под TextBox02 находится ListBox02.
Если анимация соответствует вашим ожиданиям....
Подготовка :
В модуле пользовательской формы:
Dim MyTextBoxes As Collection
Private Sub UserForm_Initialize()
'populate the ListBoxes with value in a named range
Dim LBname As String: Dim RGname As String: Dim i As Integer
For i = 1 To 2
LBname = "ListBox" & Format(i, "00")
RGname = "List" & Format(i, "00")
Controls(LBname).List = Application.Transpose(Range(RGname))
Next i
'add each TextBox to class
Set MyTextBoxes = New Collection
For Each ctl In Me.Controls
Set TextBoxClass = New Class1
If TypeName(ctl) = "TextBox" And InStr(ctl.Name, "TextBox") Then Set TextBoxClass.obj = ctl
MyTextBoxes.Add TextBoxClass
Next
End Sub
В модуле класса с именем Class1:
Private WithEvents tb As MSForms.TextBox
Property Set obj(t As MSForms.TextBox)
Set tb = t
End Property
Private Sub tb_Change()
Dim idx As String: Dim LBname As String: Dim arr
idx = Right(tb.Name, 2)
LBname = "ListBox" & idx
arr = Application.Transpose(Range("List" & idx))
With Userform1.Controls(LBname)
If tb.text = "" Then
.Clear
.List = arr
Else
.Clear
For i = LBound(arr, 1) To UBound(arr, 1)
If LCase(arr(i)) Like "*" & LCase(tb.value) & "*" Then .AddItem arr(i)
Next i
End If
End With
End Sub
Если в вашей пользовательской форме у вас есть другое текстовое поле, которое не следует использовать для поиска элементов в соответствующем списке, то, возможно, не называйте текстовое поле «TextBox», а что-то другое, например «blablabla».
если ваше существующее текстовое поле и список уже назвали что-то вроде ListBox1, ListBox2, ListBox3 и т. д., TextBox1, TextBox2, TextBox3 и т. д., то назовите именованный диапазон, например List1, List2, List3 и т. д. В модуле класса измените код для idx с помощью метода replace, что-то вроде idx = replace(tb.name,"TextBox","")
. Также в модуле Userform для LBname и RGname используйте метод замены.
Поскольку я ограничен в английском языке, мне жаль, что я не могу детализировать код для дальнейшего объяснения.
Спасибо, что нашли время написать комментарий и попытались мне помочь! Я смог понять, чего я хотел.
Вот код, который работал у меня:
' Make a New Collection
Dim coll As New Collection
' Add all Public Variables to Collection (n = Number 1 to 30)
coll.Add uQuote.oEventHandler(n)
(e.g oEventHandler1, oEventHandler2... oEventHandler30)
Dim i As Integer
for i = 1 to 30
With coll(i)
' Attach the textbox and listbox to the class
Set .SearchListBox = Me.Controls.Item("ListBox" & i)
Set .SearchTextBox = Me.Controls.Item("TextBox" & i)
' Default settings
.MaxRows = 10
.ShowAllMatches = True
.CompareMethod = vbTextCompare
.WindowsVersion = False
End With
Next i
Как сейчас написано, ваш ответ неясен. Пожалуйста, отредактируйте , чтобы добавить дополнительные сведения, которые помогут другим понять, как это отвечает на заданный вопрос. Вы можете найти больше информации о том, как писать хорошие ответы в справочном центре.
То, что вы предлагаете, не кажется мне экстремальным. Это не работает, когда вы пытаетесь это сделать? Объекты VBA UserForm имеют только 3 коллекции (страницы, вкладки и элементы управления), поэтому ваш пользовательский класс должен быть в одной из них. Вы можете циклически печатать название каждого предмета, пока не найдете то, что ищете. В качестве альтернативы вы можете зациклиться на списке и текстовых полях и запустить свой пользовательский класс поиска таким образом, а не наоборот.