Цикл по всем текстовым полям в форме, в том числе внутри группового поля
У меня есть несколько текстовых полей в winform, некоторые из них находятся внутри групповой коробки. Я попытался перебрать все текстовые поля в моей форме:
For Each c As Control In Me.Controls
If c.GetType Is GetType(TextBox) Then
' Do something
End If
Next
Но казалось, что они пропускаются внутри группового блока и зацикливаются только над другими текстовыми полями формы. Поэтому я добавил еще один цикл For Each для текстовых полей группы:
For Each c As Control In GroupBox1.Controls
If c.GetType Is GetType(TextBox) Then
' Do something
End If
Next
Интересно: есть ли способ перебрать все текстовые поля в форме, в том числе внутри группового блока, с помощью одного цикла For Each? Или какой-нибудь лучший / более элегантный способ сделать это?
Заранее спасибо.
4 ответа
Вы можете использовать эту функцию, linq может быть более элегантным способом.
Dim allTxt As New List(Of Control)
For Each txt As TextBox In FindControlRecursive(allTxt, Me, GetType(TextBox))
'....'
Next
Public Shared Function FindControlRecursive(ByVal list As List(Of Control), ByVal parent As Control, ByVal ctrlType As System.Type) As List(Of Control)
If parent Is Nothing Then Return list
If parent.GetType Is ctrlType Then
list.Add(parent)
End If
For Each child As Control In parent.Controls
FindControlRecursive(list, child, ctrlType)
Next
Return list
End Function
Вы хотели бы сделать, например, рекурсию(псевдокод, так как я не знаю VB):
Sub LoopControls (Control Container)
if (Container has Controls)
LoopControls(Container)
For Each c As Control In Container
if (Control is TextBox)
// do stuff
End Sub
Сначала вы передадите свою форму (мне) в сабвуфер, и она будет проходить по элементам управления в ней, ища те, которые содержат больше элементов управления.
Также проверьте этот вопрос: VB.NET - перебор элементов управления в объекте контейнера
Если вас не волнует порядок (и я не могу представить причину, по которой вы должны) элементов управления, вы можете сделать это итеративно следующим образом (это довольно просто, поэтому я не думаю, что какое-либо объяснение необходимо). Должно быть намного эффективнее любой рекурсии, особенно если у вас много вложенных элементов управления, хотя я сомневаюсь, что прирост производительности будет очевиден.
Public Function FindAllControlsIterative(ByRef parent As Control) As List(Of TextBox)
Dim list As New List(Of TextBox)
Dim ContainerStack As New Stack(Of Control)
ContainerStack.Push(parent)
While ContainerStack.Count > 0
For Each child As Control In ContainerStack.Pop().Controls
If child.HasChildren Then ContainerStack.Push(child)
If child.GetType Is GetType(TextBox) Then list.Add(child)
Next
End While
Return list
End Function
Вам нужно будет использовать рекурсию. Ниже приведено решение C# с использованием метода расширения, которое выходит за рамки вашего вопроса, но я просто извлек его из нашей среды.
static partial class ControlExtensions
{
public static void ApplyToMatchingChild(this Control parent, Action<Control> actionToApplyWhenFound, bool keepApplyingForever, params Func<Control, bool>[] matchingChildCriteria)
{
ControlEventHandler reapplyEventHandler = null;
if (keepApplyingForever)
{
reapplyEventHandler = (s, e) =>
{
ApplyToMatchingChild(e.Control, actionToApplyWhenFound, keepApplyingForever, matchingChildCriteria);
};
}
SearchForMatchingChildTypes(parent, actionToApplyWhenFound, reapplyEventHandler, matchingChildCriteria);
}
private static void SearchForMatchingChildTypes(Control control, Action<Control> actionToApplyWhenFound, ControlEventHandler reapplyEventHandler, params Func<Control, bool>[] matchingChildCriteria)
{
if (matchingChildCriteria.Any(criteria => criteria(control)))
{
actionToApplyWhenFound(control);
}
if (reapplyEventHandler != null)
{
control.ControlAdded += reapplyEventHandler;
}
if (control.HasChildren)
{
foreach (var ctl in control.Controls.Cast<Control>())
{
SearchForMatchingChildTypes(ctl, actionToApplyWhenFound, reapplyEventHandler, matchingChildCriteria);
}
}
}
}
И позвонить:
myControl.ApplyToMatchingChild(c => { /* Do Stuff to c */ return; }, false, c => c is TextBox);
Это будет применять функцию ко всем дочерним текстовым полям. Вы можете использовать keepApplyingForever
Параметр, обеспечивающий применение вашей функции при последующем добавлении дочерних элементов управления. Функция также позволит вам указать любое количество подходящих критериев, например, если элемент управления также является меткой или каким-либо другим критерием.
На самом деле мы используем это как удобный способ вызова нашего контейнера внедрения зависимостей для каждого UserControl, добавленного в нашу форму.
Я уверен, что у вас не должно быть особых проблем с преобразованием его в VB.NET. Кроме того, если вы не хотите использовать функцию "keepApplyingForever", то это тоже должно быть достаточно просто.