web-dev-qa-db-fra.com

Comment obtenir TOUS les contrôles enfants d'un formulaire Windows Forms d'un type spécifique (Bouton/Zone de texte)?

Je dois obtenir tous les contrôles sur un formulaire de type x. Je suis presque 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 que les enfants utilisent une fonction récursive, mais Y at-il quelque chose de plus simple ou plus simple, comme peut-être le suivant?

Dim Ctrls = From ctrl In Me.Controls Where ctrl.GetType Is Textbox
102
Luis

Voici une autre option pour vous. Je l'ai testé en créant un exemple d'application, puis j'ai placé une GroupBox et une GroupBox dans la GroupBox initiale. À l'intérieur de la GroupBox imbriquée, j'ai placé 3 contrôles TextBox et un bouton. C'est le code que j'ai utilisé (même la récursion que vous cherchiez)

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 de chargement de formulaire, je voulais un compte de tous les contrôles à l'intérieur de 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 nombre approprié à chaque fois, donc je pense que cela fonctionnera parfaitement pour ce que vous cherchez :)

196
PsychoCoder

En C # (puisque vous l'avez marqué comme tel), vous pouvez utiliser une expression LINQ comme celle-ci:

List<Control> c = Controls.OfType<TextBox>().Cast<Control>().ToList();

Editer pour récursion:

Dans cet exemple, vous créez d'abord la liste de contrôles, puis appelez une méthode pour la renseigner. Comme la méthode est récursive, elle ne renvoie pas la liste, elle la met simplement à jour.

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);
    }
}

Cela peut être possible dans une instruction LINQ en utilisant la fonction Descendants, bien que je ne sois pas aussi familier avec cela. Voir cette page pour plus d'informations à ce sujet.

Edit 2 pour retourner une collection:

Comme @ProfK l'a suggéré, une méthode qui renvoie simplement les contrôles souhaités constitue 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;
}
30
JYelton

Ceci est une version améliorée de GetAllControls () récursive qui fonctionne réellement sur les vars privés:

    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
VictorEspina

J'ai combiné un tas des idées précédentes dans une méthode d'extension. Les avantages ici sont que vous récupérez l'énumérable correctement typé, 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
Entiat

Vous pouvez utiliser une requête LINQ pour cela. Cela interrogera tout sur le formulaire qui est de type TextBox

var c = from controls in this.Controls.OfType<TextBox>()
              select controls;
7
PsychoCoder

C'est peut-être l'ancienne technique, mais cela fonctionne comme un charme. J'ai utilisé la récursivité pour changer la couleur de toutes les étiquettes du contrôle. Cela 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
Aditya Bokade

J'aimerais modifier la réponse de PsychoCoders: comme l'utilisateur souhaite obtenir tous les contrôles d'un certain type, nous pourrions utiliser les 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
Adam

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 un TextBox à un PictureBox.

Donc, vous devez é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 ceci avecone Buttonet one 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
John Anthony Oliver

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
MiMenda

Une solution simple et propre (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;
    }
}

Obtenir tous les champs de texte:

List<TextBox> textboxes = myControl.GetAllControls<TextBox>();
1
Omar

Vous pouvez utiliser le code ci-dessous

public static class ExtensionMethods
{
    public static IEnumerable<T> GetAll<T>(this Control control)
    {
        var controls = control.Controls.Cast<Control>();

        return controls.SelectMany(ctrl => ctrl.GetAll<T>())
                                  .Concat(controls.OfType<T>());
    }
}
1
Santanu Sarkar
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
Muhammad
   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
Memo Arfaa

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 de if. en cela

1
DareDevil

Voici ma méthode d'extension pour Control, en utilisant LINQ, comme une adaptation de @PsychoCoder version:

Il faut plutôt une liste de types qui vous permette de ne pas avoir besoin de plusieurs appels de GetAll pour obtenir ce que vous voulez. Je l'utilise actuellement comme version surchargée.

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));
}

Usage:

//   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
gfache

Voici ma méthode d'extension. C'est très efficace et paresseux.

Usage:

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;
                }
            }
        }
    }
1
Jone Polvora

Voici une solution générique testée et fonctionnelle:

J'ai un grand nombre de contrôles UpDownNumeric, certains dans le formulaire principal, d'autres dans des groupbox dans le formulaire . Je souhaite que seul le dernier contrôle sélectionné change de couleur d'arrière-plan en vert, pour lequel je règle d'abord tous les autres en blanc en utilisant cette méthode: (peut aussi être étendu 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
samtal

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
ted

Tu peux essayer ça si tu veux :)

    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
rashi

Simplement:

For Each ctrl In Me.Controls.OfType(Of Button)()
   ctrl.Text = "Hello World!"
Next
0
Leebeedev

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 est uniquement disponible pour WPF .

0
Alex Rouillard
    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
Valeh Mikayilzadeh

Pour ceux qui recherchent une version VB du code C # d'Adam, écrite 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é la correspondance BaseType pour tous les contrôles personnalisés dérivés. Vous pouvez le supprimer ou même en faire un paramètre facultatif si vous le souhaitez.

Usage

Dim oButtons As IEnumerable(Of Button) = Me.GetControls(Of Button)()
0
SteveCinq

Bien que plusieurs autres utilisateurs aient publié des solutions adéquates, j'aimerais proposer 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
JamesFaix
    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
Koray