49

I have multiple pictureboxes and I need to load random images into them during runtime. So I thought it would be nice to have a collection of all pictureboxes and then assign images to them using a simple loop. But how should I do it? Or maybe are there any other better solutions to such problem?

drv
  • 493
  • 1
  • 4
  • 4

9 Answers9

92

Using a bit of LINQ:

foreach(var pb in this.Controls.OfType<PictureBox>())
{
  //do stuff
}

However, this will only take care of PictureBoxes in the main container.

Femaref
  • 60,705
  • 7
  • 138
  • 176
  • 24
    This will not consider PictureBoxes that are nested in other containers. – cdhowie Jan 07 '11 at 21:25
  • 1
    Alternatively... foreach(PictureBox pb in this.Controls.OfType()) { //do stuff } Not sure it makes a huge difference, but I tend to have a preference for explicit declaration. – Scooter Oct 14 '16 at 19:23
  • What about for something like this: https://stackoverflow.com/questions/47182809/how-to-count-the-checkboxes-in-a-asp-net-form/47182982#47182982 – Si8 Nov 08 '17 at 15:11
29

You could use this method:

public static IEnumerable<T> GetControlsOfType<T>(Control root)
    where T : Control
{
    var t = root as T;
    if (t != null)
        yield return t;

    var container = root as ContainerControl;
    if (container != null)
        foreach (Control c in container.Controls)
            foreach (var i in GetControlsOfType<T>(c))
                yield return i;
}

Then you could do something like this:

foreach (var pictureBox in GetControlsOfType<PictureBox>(theForm)) {
    // ...
}
cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • 1
    I think that's actually wordier than just `root.Controls.OfType`, though. – Dan Tao Jan 07 '11 at 21:26
  • 6
    Oh I see, you made it go down to unlimited depth. Gotcha. – Dan Tao Jan 07 '11 at 21:26
  • I'm doing something similar. I tried this answer's method, but `Control` became an ambiguous reference between `System.Web.UI` and `System.Windows.Forms`. I could find `ContainerControl` only in the Forms namespace, not under Web. And ContainerControl wouldn't accept a Web.UI Control. Has anybody else ran into this issue? (I'll work around my problem for now, but really really want to use this simple and elegant answer.) – Codes with Hammer Dec 28 '15 at 14:55
  • Strangely enough `Panel` does not inherit from `ContainerControl`, I had to go one level up in the Winforms class hierarchy and use `ScrollableControl` in order to include panel. I'm not sure yet what other issues that this may cause. I may update to only support the containers that I'm looking for. – Jesse Oct 12 '16 at 15:46
  • @CodeswithHammer I think is not the right solution (the linked one) because enumeration doesn't filter control's type. – tedebus Feb 08 '17 at 08:52
  • @tedebus To be fair, you can simply slap an `OfType()` query on the resulting enumerable. – cdhowie Feb 09 '17 at 17:15
  • @cdhowie Where is the right place to insert OfType()? At the end of the final search (the second code in the link), not in the initial one, is it right? – tedebus Feb 13 '17 at 13:30
  • @tedebus `EnumerateControlsRecursive(container).OfType()` for example – cdhowie Feb 13 '17 at 17:17
  • @cdhowie Yes, of course it works so. But... in this manner you first collect ALL the controls in your recursive function THEN you filter what you are really looking for. I think that it is not the right solution even if it works anyway. The solution that you wrote as (good) answer is instead more elegant and it answered correctly to the question IMHO. I don't understand why you continue to defend CodeswithHammer's link (a generic recursive solution) while there is a better one that was written... just by you! :/ A single question is what happen if T is a container: Did it continue to dig? – tedebus Feb 15 '17 at 00:54
  • @tedebus Actually, no, that's not the case. IEnumerables are (usually) lazily evaluated; the `OfType()` filter is applied *during the recursive enumeration of the controls*. TBH, having one query to obtain an enumerable of all of the controls and then applying type filtering to the results is more correct than the way I did it. Each query should do one thing and do it well; my code combines the task of two queries into one, and does not provide any benefit for doing so. – cdhowie Feb 17 '17 at 19:50
  • @cdhowie Sorry, I don't understand a thing, but it may be I'm a little stupid... If you say that "the OfType() filter is applied during the recursive enumeration of the controls", how can it works if you are looking for a non-container control? The first round would exclude all ContainerControls so it can't go inside them during next round. Isn't so? A simple example: a page containing a label and a panel containing other labels: the first foreach should exclude the panel and the final result will contain the first label only. Is what I say wrong? – tedebus Feb 19 '17 at 01:07
  • @tedebus Both the answer linked above and my own answer do a depth-first recursive scan of the entire tree, but the returned enumerable does not contain the results. When `IEnumerable.GetEnumerator()` is called on it, an enumerator object is return that actually performs the scan, but *only one result at a time* each time `IEnumerator.MoveNext()` is called. This means you could do `EnumerateControlsRecursive().OfType().First()` to get the first found `PictureBox` control. *The tree scan stops after the first result is found* in this case. – cdhowie Feb 19 '17 at 19:03
  • @tedebus Consider the case of generators of infinite sequences, such as the positive integers, prime numbers, or a Fibonacci sequence. These enumerables cannot store the entire result sequence, because that sequence is infinite. Instead, they produce one item at a time, each time the enumerator is advanced. That's exactly what's happening here: the enumerator produces one control at a time, and filters (such as `OfType()`) are applied *during enumeration.* – cdhowie Feb 19 '17 at 19:05
  • 2
    A year later... This did not work for when items were in Group Boxes that were children of the parent container. Apparently they are not a ContainerControl? That's crazy. – Ronnie W Jul 26 '18 at 18:43
9

If you're at least on .NET 3.5 then you have LINQ, which means that since ControlCollection implements IEnumerable you can just do:

var pictureBoxes = Controls.OfType<PictureBox>();
Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • 3
    This will not consider PictureBoxes that are nested in other containers. – cdhowie Jan 07 '11 at 21:25
  • 2
    Right. In my defense, the OP didn't really specify whether that was a requirement; but still, it would probably make sense to treat it as such. – Dan Tao Jan 07 '11 at 21:27
5

I use this generic recursive method:

The assumption of this method is that if the control is T than the method does not look in its children. If you need also to look to its children you can easily change it accordingly.

public static IList<T> GetAllControlsRecusrvive<T>(Control control) where T :Control 
{
    var rtn = new List<T>();
    foreach (Control item in control.Controls)
    {
        var ctr = item as T;
        if (ctr!=null)
        {
            rtn.Add(ctr);
        }
        else
        {
            rtn.AddRange(GetAllControlsRecusrvive<T>(item));
        }

    }
    return rtn;
}
giammin
  • 18,620
  • 8
  • 71
  • 89
5

A simple function, easy to understand, recursive, and it works calling it inside any form control:

private void findControlsOfType(Type type, Control.ControlCollection formControls, ref List<Control> controls)
    {
        foreach (Control control in formControls)
        {
            if (control.GetType() == type)
                controls.Add(control);
            if (control.Controls.Count > 0)
                findControlsOfType(type, control.Controls, ref controls);
        }
    }

You can call it on multiple ways. To get the Buttons:

List<Control> buttons = new List<Control>();
findControlsOfType(typeof(Button), this.Controls, ref buttons);

To get the Panels:

List<Control> panels = new List<Control>();
findControlsOfType(typeof(Panel), this.Controls, ref panels);

etc.

Zelkovar
  • 119
  • 1
  • 5
  • This worked great for me, I just altered the if statement a little: if (control.GetType() == type || control.GetType().IsSubclassOf(type)) – blind Skwirl Sep 09 '20 at 17:04
3

Here's another version since the existing provided ones weren't quite what I had in mind. This one works as an extension method, optionally, and it excludes checking the root/parent container's type. This method is basically a "Get all descendent controls of type T" method:

public static System.Collections.Generic.IEnumerable<T> ControlsOfType<T>(this System.Web.UI.Control control) where T: System.Web.UI.Control{
    foreach(System.Web.UI.Control childControl in control.Controls){
        if(childControl is T) yield return (T)childControl;
        foreach(var furtherDescendantControl in childControl.ControlsOfType<T>()) yield return furtherDescendantControl;
    }
}
scradam
  • 1,053
  • 11
  • 11
1
    public static List<T> FindControlByType<T>(Control mainControl,bool getAllChild = false) where T :Control
    {
        List<T> lt = new List<T>();
        for (int i = 0; i < mainControl.Controls.Count; i++)
        {
            if (mainControl.Controls[i] is T) lt.Add((T)mainControl.Controls[i]);
            if (getAllChild) lt.AddRange(FindControlByType<T>(mainControl.Controls[i], getAllChild));
        }
        return lt;
    }
MiMFa
  • 981
  • 11
  • 14
1

This to me is by far the easiest. In my application, I was trying to clear all the textboxes in a panel:

    foreach (Control c in panel.Controls)
    {
        if (c.GetType().Name == "TextBox")
        {
            c.Text = "";
        }
    }
Richard Martin
  • 131
  • 1
  • 9
0

Takes Control as container into account:

        private static IEnumerable<T> GetControlsOfType<T>(this Control root)
        where T : Control
    {
        if (root is T t)
            yield return t;

        if (root is ContainerControl || root is Control)
        {
            var container = root as Control;
            foreach (Control c in container.Controls)
                foreach (var i in GetControlsOfType<T>(c))
                    yield return i;
        }
    }
Martin.Martinsson
  • 1,894
  • 21
  • 25