Как получить все дочерние элементы управления формы Windows Forms определенного типа (кнопка / текстовое поле)?
мне нужно получить все элементы управления в форме типа x. я уверен, что видел этот код однажды в прошлом, который использовал что-то вроде этого:
dim ctrls() as Control
ctrls = Me.Controls(GetType(TextBox))
Я знаю, что могу перебирать все элементы управления, получая детей с помощью рекурсивной функции, но
есть ли что-то проще или проще, может быть, как следующее?
Dim Ctrls = From ctrl In Me.Controls Where ctrl.GetType Is Textbox
23 ответов:
вот еще один вариант для вас. Я проверял это путем создания примера приложения, затем я поставил группа и группа внутри первичной группы. Внутри вложенного GroupBox я поставил 3 элемента управления TextBox и кнопку. Это код, который я использовал (даже включает рекурсию, которую вы искали)
public IEnumerable<Control> GetAll(Control control,Type type) { var controls = control.Controls.Cast<Control>(); return controls.SelectMany(ctrl => GetAll(ctrl,type)) .Concat(controls) .Where(c => c.GetType() == type); }чтобы проверить его в событии загрузки формы, я хотел подсчитать все элементы управления внутри начального GroupBox
private void Form1_Load(object sender, EventArgs e) { var c = GetAll(this,typeof(TextBox)); MessageBox.Show("Total Controls: " + c.Count()); }и он возвращал правильный счет каждый раз, так что я думаю это будет отлично работать для того, что вы ищете:)
В C# (так как вы отметили его как таковой) вы можете использовать выражение LINQ следующим образом:
List<Control> c = Controls.OfType<TextBox>().Cast<Control>().ToList();
изменить для рекурсии:
в этом примере сначала создается список элементов управления, а затем вызвать метод, чтобы заполнить его. Поскольку метод рекурсивный, он не возвращает список, он просто обновляет его.
List<Control> ControlList = new List<Control>(); private void GetAllControls(Control container) { foreach (Control c in container.Controls) { GetAllControls(c); if (c is TextBox) ControlList.Add(c); } }это может быть возможно сделать в одном операторе LINQ с помощью
Descendantsфункция, хотя я не так хорошо знаком с ней. Смотрите на этой странице для получения дополнительной информации об этом.Изменить 2, чтобы вернуть коллекцию:
как @Профк предложил метод, который просто возвращает нужные элементы управления, наверное, лучше практика. Чтобы проиллюстрировать это, я изменил код следующим образом:
private IEnumerable<Control> GetAllTextBoxControls(Control container) { List<Control> controlList = new List<Control>(); foreach (Control c in container.Controls) { controlList.AddRange(GetAllTextBoxControls(c)); if (c is TextBox) controlList.Add(c); } return controlList; }
Это улучшенная версия рекурсивного GetAllControls (), которая фактически работает на частных vars:
private void Test() { List<Control> allTextboxes = GetAllControls(this); } private List<Control> GetAllControls(Control container, List<Control> list) { foreach (Control c in container.Controls) { if (c is TextBox) list.Add(c); if (c.Controls.Count > 0) list = GetAllControls(c, list); } return list; } private List<Control> GetAllControls(Control container) { return GetAllControls(container, new List<Control>()); }
Я объединил кучу предыдущих идей в метод расширения. Преимущества здесь в том, что вы получаете правильно набранный перечислимый назад, плюс наследование обрабатывается правильно
OfType().public static IEnumerable<T> FindAllChildrenByType<T>(this Control control) { IEnumerable<Control> controls = control.Controls.Cast<Control>(); return controls .OfType<T>() .Concat<T>(controls.SelectMany<Control, T>(ctrl => FindAllChildrenByType<T>(ctrl))); }
для этого можно использовать запрос LINQ. Это будет запрашивать все в форме, которая является типом TextBox
var c = from controls in this.Controls.OfType<TextBox>() select controls;
это может быть древняя техника, но она работает как шарм. Я использовал рекурсию, чтобы изменить цвет всех меток контроля. Он отлично работает.
internal static void changeControlColour(Control f, Color color) { foreach (Control c in f.Controls) { // MessageBox.Show(c.GetType().ToString()); if (c.HasChildren) { changeControlColour(c, color); } else if (c is Label) { Label lll = (Label)c; lll.ForeColor = color; } } }
Я хотел бы изменить PsychoCoders ответ: как пользователь хочет получить все элементы управления определенного типа, мы могли бы использовать дженерики, следующим образом:
public IEnumerable<T> FindControls<T>(Control control) where T : Control { // we can't cast here because some controls in here will most likely not be <T> var controls = control.Controls.Cast<Control>(); return controls.SelectMany(ctrl => FindControls<T>(ctrl)) .Concat(controls) .Where(c => c.GetType() == typeof(T)).Cast<T>(); }таким образом, мы можем вызвать функцию следующим образом:
private void Form1_Load(object sender, EventArgs e) { var c = FindControls<TextBox>(this); MessageBox.Show("Total Controls: " + c.Count()); }
не забывайте, что вы также можете иметь текстовое поле в другое управление кроме контейнерные элементы управления тоже. Вы даже можете добавить текстовое поле в PictureBox.
Так что вам также нужно проверить, если
someControl.HasChildren = Trueв любой рекурсивной функции.
Это результат, который я получил от макета для тестирования этого кода:
TextBox13 Parent = Panel5 TextBox12 Parent = Panel5 TextBox9 Parent = Panel2 TextBox8 Parent = Panel2 TextBox16 Parent = Panel6 TextBox15 Parent = Panel6 TextBox14 Parent = Panel6 TextBox10 Parent = Panel3 TextBox11 Parent = Panel4 TextBox7 Parent = Panel1 TextBox6 Parent = Panel1 TextBox5 Parent = Panel1 TextBox4 Parent = Form1 TextBox3 Parent = Form1 TextBox2 Parent = Form1 TextBox1 Parent = Form1 tbTest Parent = myPicBoxпопробовать одна кнопка и один RichTextBox на a форма.
Option Strict On Option Explicit On Option Infer Off Public Class Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim pb As New PictureBox pb.Name = "myPicBox" pb.BackColor = Color.Goldenrod pb.Size = New Size(100, 100) pb.Location = New Point(0, 0) Dim tb As New TextBox tb.Name = "tbTest" pb.Controls.Add(tb) Me.Controls.Add(pb) Dim textBoxList As New List(Of Control) textBoxList = GetAllControls(Of TextBox)(Me) Dim sb As New System.Text.StringBuilder For index As Integer = 0 To textBoxList.Count - 1 sb.Append(textBoxList.Item(index).Name & " Parent = " & textBoxList.Item(index).Parent.Name & System.Environment.NewLine) Next RichTextBox1.Text = sb.ToString End Sub Private Function GetAllControls(Of T)(ByVal searchWithin As Control) As List(Of Control) Dim returnList As New List(Of Control) If searchWithin.HasChildren = True Then For Each ctrl As Control In searchWithin.Controls If TypeOf ctrl Is T Then returnList.Add(ctrl) End If returnList.AddRange(GetAllControls(Of T)(ctrl)) Next ElseIf searchWithin.HasChildren = False Then For Each ctrl As Control In searchWithin.Controls If TypeOf ctrl Is T Then returnList.Add(ctrl) End If returnList.AddRange(GetAllControls(Of T)(ctrl)) Next End If Return returnList End Function End Class
С помощью отражения:
// Return a list with all the private fields with the same type List<T> GetAllControlsWithTypeFromControl<T>(Control parentControl) { List<T> retValue = new List<T>(); System.Reflection.FieldInfo[] fields = parentControl.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); foreach (System.Reflection.FieldInfo field in fields) { if (field.FieldType == typeof(T)) retValue.Add((T)field.GetValue(parentControl)); } } List<TextBox> ctrls = GetAllControlsWithTypeFromControl<TextBox>(this);
вот решение.
https://stackoverflow.com/a/19224936/1147352
Я написал этот кусок кода и выбрал только панели, вы можете добавить больше переключателей или ifs. в нем
public List<Control> GetAllChildControls(Control Root, Type FilterType = null) { List<Control> AllChilds = new List<Control>(); foreach (Control ctl in Root.Controls) { if (FilterType != null) { if (ctl.GetType == FilterType) { AllChilds.Add(ctl); } } else { AllChilds.Add(ctl); } if (ctl.HasChildren) { GetAllChildControls(ctl, FilterType); } } return AllChilds; }
вот мой метод расширения для
Control, используя LINQ, как адаптацию @PsychoCoder версия:Он принимает список типов вместо этого, что позволяет вам не нужно несколько вызовов
GetAllчтобы получить то, что вы хотите. В настоящее время я использую его как перегрузите версию.public static IEnumerable<Control> GetAll(this Control control, IEnumerable<Type> filteringTypes) { var ctrls = control.Controls.Cast<Control>(); return ctrls.SelectMany(ctrl => GetAll(ctrl, filteringTypes)) .Concat(ctrls) .Where(ctl => filteringTypes.Any(t => ctl.GetType() == t)); }использование:
// The types you want to select var typeToBeSelected = new List<Type> { typeof(TextBox) , typeof(MaskedTextBox) , typeof(Button) }; // Only one call var allControls = MyControlThatContainsOtherControls.GetAll(typeToBeSelected); // Do something with it foreach(var ctrl in allControls) { ctrl.Enabled = true; }
IEnumerable<Control> Ctrls = from Control ctrl in Me.Controls where ctrl is TextBox | ctrl is GroupBox select ctr;Лямбда-Выражения
IEnumerable<Control> Ctrls = Me.Controls.Cast<Control>().Where(c => c is Button | c is GroupBox);
чистое и простое решение (C#):
static class Utilities { public static List<T> GetAllControls<T>(this Control container) where T : Control { List<T> controls = new List<T>(); if (container.Controls.Count > 0) { controls.AddRange(container.Controls.OfType<T>()); foreach (Control c in container.Controls) { controls.AddRange(c.GetAllControls<T>()); } } return controls; } }получить все текстовые поля:
List<TextBox> textboxes = myControl.GetAllControls<TextBox>();
Я изменил из @PsychoCoder. Теперь можно найти все элементы управления (включая вложенные).
public static IEnumerable<T> GetChildrens<T>(Control control) { var type = typeof (T); var allControls = GetAllChildrens(control); return allControls.Where(c => c.GetType() == type).Cast<T>(); } private static IEnumerable<Control> GetAllChildrens(Control control) { var controls = control.Controls.Cast<Control>(); return controls.SelectMany(c => GetAllChildrens(c)) .Concat(controls); }
Это может работать:
Public Function getControls(Of T)() As List(Of T) Dim st As New Stack(Of Control) Dim ctl As Control Dim li As New List(Of T) st.Push(Me) While st.Count > 0 ctl = st.Pop For Each c In ctl.Controls st.Push(CType(c, Control)) If c.GetType Is GetType(T) Then li.Add(CType(c, T)) End If Next End While Return li End FunctionЯ думаю, что функция для получения всех элементов управления, о которых вы говорите, доступна только для WPF.
вот проверенное и работающее общее решение:
У меня есть большое количество UpDownNumeric элементов управления, некоторые в основной форме, некоторые в groupboxes в форме. Я хочу, чтобы только один последний выбранный элемент управления изменил цвет назад на зеленый, для которого я сначала установил все остальные на белый, используя этот метод: (можно также расширить до внуков)
public void setAllUpDnBackColorWhite() { //To set the numericUpDown background color of the selected control to white: //and then the last selected control will change to green. foreach (Control cont in this.Controls) { if (cont.HasChildren) { foreach (Control contChild in cont.Controls) if (contChild.GetType() == typeof(NumericUpDown)) contChild.BackColor = Color.White; } if (cont.GetType() == typeof(NumericUpDown)) cont.BackColor = Color.White; } }
вы можете попробовать это, если вы хотите :)
private void ClearControls(Control.ControlCollection c) { foreach (Control control in c) { if (control.HasChildren) { ClearControls(control.Controls); } else { if (control is TextBox) { TextBox txt = (TextBox)control; txt.Clear(); } if (control is ComboBox) { ComboBox cmb = (ComboBox)control; if (cmb.Items.Count > 0) cmb.SelectedIndex = -1; } if (control is CheckBox) { CheckBox chk = (CheckBox)control; chk.Checked = false; } if (control is RadioButton) { RadioButton rdo = (RadioButton)control; rdo.Checked = false; } if (control is ListBox) { ListBox listBox = (ListBox)control; listBox.ClearSelected(); } } } } private void btnClear_Click(object sender, EventArgs e) { ClearControls((ControlCollection)this.Controls); }
хотя несколько других пользователей опубликовали адекватные решения, я хотел бы опубликовать более общий подход, который может быть более полезным.
это в значительной степени основано на ответе Джилтона.
public static IEnumerable<Control> AllControls( this Control control, Func<Control, Boolean> filter = null) { if (control == null) throw new ArgumentNullException("control"); if (filter == null) filter = (c => true); var list = new List<Control>(); foreach (Control c in control.Controls) { list.AddRange(AllControls(c, filter)); if (filter(c)) list.Add(c); } return list; }
public static IEnumerable<T> GetAllControls<T>(this Control control) where T : Control { foreach (Control c in control.Controls) { if (c is T) yield return (T)c; foreach (T c1 in c.GetAllControls<T>()) yield return c1; } }
public IEnumerable<T> GetAll<T>(Control control) where T : Control { var type = typeof(T); var controls = control.Controls.Cast<Control>().ToArray(); foreach (var c in controls.SelectMany(GetAll<T>).Concat(controls)) if (c.GetType() == type) yield return (T)c; }
вот мой метод расширения. Это очень эффективно и лениво.
использование:
var checkBoxes = tableLayoutPanel1.FindChildControlsOfType<CheckBox>(); foreach (var checkBox in checkBoxes) { checkBox.Checked = false; }код:
public static IEnumerable<TControl> FindChildControlsOfType<TControl>(this Control control) where TControl : Control { foreach (var childControl in control.Controls.Cast<Control>()) { if (childControl.GetType() == typeof(TControl)) { yield return (TControl)childControl; } else { foreach (var next in FindChildControlsOfType<TControl>(childControl)) { yield return next; } } } }
для тех, кто ищет версию VB кода Адама C#, написанного как расширение
Controlкласс:''' <summary>Collects child controls of the specified type or base type within the passed control.</summary> ''' <typeparam name="T">The type of child controls to include. Restricted to objects of type Control.</typeparam> ''' <param name="Parent">Required. The parent form control.</param> ''' <returns>An object of type IEnumerable(Of T) containing the control collection.</returns> ''' <remarks>This method recursively calls itself passing child controls as the parent control.</remarks> <Extension()> Public Function [GetControls](Of T As Control)( ByVal Parent As Control) As IEnumerable(Of T) Dim oControls As IEnumerable(Of Control) = Parent.Controls.Cast(Of Control)() Return oControls.SelectMany(Function(c) GetControls(Of T)(c)).Concat(oControls.Where(Function(c) c.GetType() Is GetType(T) Or c.GetType().BaseType Is GetType(T)) End Functionпримечание: Я добавил
BaseTypeсоответствие для любых производных пользовательских элементов управления. Вы можете удалить это или даже сделать его необязательным параметром, если хотите.использование
Dim oButtons As IEnumerable(Of Button) = Me.GetControls(Of Button)()
Comments