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
97
demandé sur Vadim Ovchinnikov 2010-08-06 01:07:00

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 :)

186
répondu PsychoCoder 2012-02-27 22:15:12

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;
}
28
répondu JYelton 2012-10-09 15:51:16

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>());
    }
12
répondu VictorEspina 2011-07-29 14:23:20

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)));
}
8
répondu Entiat 2013-03-11 15:03:49

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;
7
répondu PsychoCoder 2010-08-05 21:18:13

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;
            }
    }
}
5
répondu Aditya Bokade 2013-03-11 14:55:26

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());
}
4
répondu Adam 2014-05-09 08:39:55

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
3
répondu John Anthony Oliver 2013-03-11 15:01:49

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);
2
répondu MiMenda 2016-06-22 08:29:22

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

1
répondu DareDevil 2017-05-23 10:31:09
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;
}
1
répondu Muhammad 2015-05-09 20:00:01

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;
}
1
répondu gfache 2016-08-16 10:47:47
   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);
1
répondu Memo Arfaa 2016-12-05 20:09:21

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>();
1
répondu Omar 2017-03-08 15:35:31

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);
}
0
répondu ted 2012-04-09 15:59:28

, 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.

0
répondu Alex Rouillard 2013-03-11 14:50:09

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;
       }
    }   
0
répondu samtal 2014-01-28 12:37:05

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);
    }
0
répondu rashi 2015-08-13 13:37:00

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;
}
0
répondu JamesFaix 2015-12-29 02:41:15
    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;
        }
    }
0
répondu Koray 2016-10-11 06:53:14
    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;
    }
0
répondu Valeh Mikayilzadeh 2017-02-01 05:46:23

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;
                }
            }
        }
    }
0
répondu Jone Polvora 2017-07-02 15:31:13

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)()
0
répondu SteveCinq 2017-12-30 02:12:01