Comment obtenir tous les contrôles enfants D'un formulaire Windows Forms d'un type spécifique (bouton / zone de texte)?
J'ai besoin d'obtenir tous les contrôles sur un formulaire qui sont de type X. Je suis sûr d'avoir vu ce code une fois dans le passé qui utilisait quelque chose comme ceci:
dim ctrls() as Control
ctrls = Me.Controls(GetType(TextBox))
Je sais que je peux parcourir tous les contrôles pour obtenir des enfants en utilisant une fonction récursive, mais y at-il quelque chose de plus facile ou plus simple, peut-être comme ce qui suit?
Dim Ctrls = From ctrl In Me.Controls Where ctrl.GetType Is Textbox
23 réponses
Voici une autre option pour vous. Je l'ai testé en créant un exemple d'application, puis j'ai mis un GroupBox et un GroupBox dans le GroupBox initial. Dans le GroupBox imbriqué, j'ai mis 3 contrôles de zone de texte et un bouton. C'est le code que j'ai utilisé (inclut même la récursivité vous recherchiez)
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);
}
Pour le tester dans l'événement form load, je voulais un compte de tous les contrôles dans la GroupBox initiale
private void Form1_Load(object sender, EventArgs e)
{
var c = GetAll(this,typeof(TextBox));
MessageBox.Show("Total Controls: " + c.Count());
}
Et il a retourné le compte approprié à chaque fois, donc je pense que cela fonctionnera parfaitement pour ce que vous cherchez :)
En C# (puisque vous l'avez étiqueté en tant que tel), vous pouvez utiliser une expression LINQ comme ceci:
List<Control> c = Controls.OfType<TextBox>().Cast<Control>().ToList();
Modifier la récursivité:
Dans cet exemple, vous créez d'abord la liste des contrôles, puis appelez une méthode pour la remplir. Puisque la méthode est récursive, elle ne renvoie pas la liste, elle la met à jour simplement.
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);
}
}
Il peut être possible de le faire dans une instruction LINQ en utilisant la fonction Descendants
, bien que je ne sois pas aussi familier avec elle. Voir cette page pour plus d' informations sur que.
Modifier 2 pour retourner une collection:
Comme @ProfK l'a suggéré, une méthode qui renvoie simplement les contrôles souhaités est probablement une meilleure pratique. Pour illustrer cela, j'ai modifié le code comme suit:
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;
}
Ceci est une version améliorée de la fonction récursive getallcontrols() qui fonctionne réellement sur les variables privées:
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>());
}
J'ai combiné un tas des idées précédentes en une méthode d'extension. Les avantages ici sont que vous obtenez l'énumérable correctement tapé, plus l'héritage est géré correctement par 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)));
}
Vous pouvez utiliser une requête LINQ pour ce faire. Cela interrogera tout sur le formulaire qui est de type TextBox
var c = from controls in this.Controls.OfType<TextBox>()
select controls;
C'est peut-être la technique ancienne, mais ça marche comme un charme. J'ai utilisé la récursivité pour changer la couleur de toutes les étiquettes du contrôle. Il fonctionne très bien.
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;
}
}
}
Je voudrais modifier PsychoCoders réponse: tant que l'utilisateur veut obtenir tous les contrôles d'un certain type, nous pourrions utiliser des génériques de la manière suivante:
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>();
}
De Cette façon, nous pouvons appeler la fonction comme suit:
private void Form1_Load(object sender, EventArgs e)
{
var c = FindControls<TextBox>(this);
MessageBox.Show("Total Controls: " + c.Count());
}
N'oubliez pas que vous pouvez également avoir une zone de texte dans d'autres contrôles autres que les contrôles de conteneur. Vous pouvez même ajouter une zone de texte à une zone D'image.
Vous devez donc également vérifier si
someControl.HasChildren = True
Dans toute fonction récursive.
C'est le résultat que j'ai eu d'une mise en page pour tester ce code:
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
Essayez-le avec les un Bouton et un RichTextBox sur un formulaire.
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
En utilisant la réflexion:
// 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);
Voici la Solution.
Https://stackoverflow.com/a/19224936/1147352
J'ai écrit ce morceau de code et sélectionné uniquement les panneaux, vous pouvez ajouter plus de commutateurs ou ifs. en elle
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;
}
Voici ma méthode d'extension pour Control
, en utilisant LINQ, comme une adaptation de @ PsychoCoder version:
Il faut une liste de type à la place qui vous permet de ne pas avoir besoin de plusieurs appels de GetAll
pour obtenir ce que vous voulez. Je l'utilise actuellement comme une surcharge version.
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));
}
Utilisation:
// 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;
Expressions Lambda
IEnumerable<Control> Ctrls = Me.Controls.Cast<Control>().Where(c => c is Button | c is GroupBox);
Une solution propre et facile (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;
}
}
Récupère toutes les zones de texte:
List<TextBox> textboxes = myControl.GetAllControls<TextBox>();
J'ai modifié de @ PsychoCoder. Tous les contrôles peuvent être trouvés maintenant (inclure imbriqué).
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);
}
, Cela peut fonctionner:
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
Je pense que la fonction pour obtenir tous les contrôles dont vous parlez n'est disponible que pour WPF.
Voici une solution générique testée et fonctionnelle:
J'ai un grand nombre de contrôles UpDownNumeric, certains dans le formulaire principal, certains dans les groupboxes dans le formulaire. Je veux que seul le dernier contrôle sélectionné change de couleur en vert, pour lequel j'ai d'abord mis tous les autres en blanc, en utilisant cette méthode: (peut également s'étendre aux petits-enfants)
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;
}
}
Vous pouvez essayer si vous voulez :)
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);
}
Bien que plusieurs autres utilisateurs aient posté des solutions adéquates, j'aimerais publier une approche plus générale qui pourrait être plus utile.
Ceci est largement basé sur la réponse de JYelton.
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;
}
Voici ma méthode D'Extension. Il est très efficace et est paresseux.
Utilisation:
var checkBoxes = tableLayoutPanel1.FindChildControlsOfType<CheckBox>();
foreach (var checkBox in checkBoxes)
{
checkBox.Checked = false;
}
Le code est:
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;
}
}
}
}
Pour ceux qui recherchent une version VB du code C # D'Adam écrit comme une extension de la classe 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
Remarque: j'ai ajouté BaseType
correspondant à tous les contrôles personnalisés dérivés. Vous pouvez supprimer cela ou même en faire un paramètre facultatif si vous le souhaitez.
Utilisation
Dim oButtons As IEnumerable(Of Button) = Me.GetControls(Of Button)()