140 votes

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

J'ai besoin d'obtenir tous les contrôles d'un formulaire qui sont 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 itérer sur tous les contrôles ayant des enfants en utilisant une fonction récursive, mais mais n'y a-t-il pas quelque chose de plus simple ou de plus direct, comme ce qui suit ?

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

1 votes

0 votes

J'ai fait un Proposition d'API : Ajouter la propriété Descendants pour le contrôle sur github.com/dotnet/winforms pour cela. Si vous l'aimez, merci de l'upvoter.

260voto

PsychoCoder Points 3150

Voici une autre option pour vous. Je l'ai testé en créant une application exemple, j'ai ensuite mis un GroupBox et un GroupBox à l'intérieur du GroupBox initial. A l'intérieur de la GroupBox imbriquée, j'ai mis 3 contrôles TextBox et un bouton. Voici le code que j'ai utilisé (il inclut même la récursion que 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 de chargement du formulaire, je voulais un compte de tous les contrôles à l'intérieur du GroupBox initial.

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 bon compte à chaque fois, donc je pense que cela fonctionnera parfaitement pour ce que vous recherchez :)

25 votes

GetAll() défini ici est un très bon candidat pour une méthode d'extension de la classe Control.

0 votes

J'ai aimé la façon dont vous avez utilisé les expressions lambda. Où apprendre les expressions lambda en détail ?

0 votes

"'System.Windows.Forms.Control.ControlCollection' ne contient pas de définition pour 'Cast' et aucune méthode d'extension 'Cast' acceptant un premier argument de type 'System.Windows.Forms.Control.ControlCollection' n'a pu être trouvée (il vous manque une directive using ou une référence d'assemblage ?)". Je suis sur .NET 4.5 et "Controls" n'a pas de fonction / méthode / quoi que ce soit de "Cast". Que me manque-t-il ?

43voto

JYelton Points 14014

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

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

Modifier pour la récursion :

Dans cet exemple, vous créez d'abord la liste de contrôles, puis vous appelez une méthode pour la remplir. 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);
    }
}

Il peut être possible de faire cela dans une seule déclaration LINQ en utilisant la fonction Descendants bien que je ne sois pas aussi familier avec cette fonction. Voir cette page pour plus d'informations à ce sujet.

Editer 2 pour retourner une collection :

Comme l'a suggéré @ProfK, 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;
}

1 votes

Merci, C# ou VB, ça me va. Mais le problème est que Controls.OfType<TExtbox> ne renvoie que les enfants du contrôle courant (dans mon cas le formulaire), et je veux en un seul appel obtenir TOUS les contrôles dans le formulaire "récursivement" (enfants, sous-enfants, sous-sous-enfants, .....) dans une seule collection.

0 votes

Je m'attendrais à ce qu'une méthode appelée GetAllControls renvoie une collection de contrôles, que j'attribuerais à ControlList. Cela semble être une meilleure pratique.

0 votes

@ProfK Je suis d'accord avec vous ; je modifie l'exemple en conséquence.

14voto

VictorEspina Points 96

Il s'agit d'une version améliorée de la méthode 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>());
    }

12voto

Entiat Points 51

J'ai combiné plusieurs des idées précédentes en une seule méthode d'extension. Les avantages de cette méthode sont que vous récupérez l'énumérable correctement typée, et que l'héritage est géré correctement par la méthode 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)));
}

5voto

PsychoCoder Points 3150

Vous pouvez utiliser une requête LINQ pour ce faire. Ceci interrogera tout ce qui est de type TextBox dans le formulaire.

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

0 votes

Merci, mais le même problème que la réponse précédente, il ne renvoie que les chidls mais pas les subchilds, etc, et je veux tous les contrôles ensted. Je suis presque sûr d'avoir vu que c'est possible avec un appel de méthode unique, ce qui est nouveau dans .NET 3.5 ou 4.0, je me souviens avoir vu cela dans une démo quelque part.

0 votes

En ignorant l'absence de récurrence, est-ce que var c = this.Controls.OfType<TextBox>() donnent le même résultat ?

2 votes

@Dennis : Oui, c'est une question de préférence (généralement). Voir stackoverflow.com/questions/214500/ pour une discussion intéressante sur le sujet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X