# Selecting form controls that are either inside or outside a rectangle

I have a function which loops over the controls on a form and checks to see if it's in a given rect, and then adds it or doesn't add it to a list<>. The parameter bContained is used to determine if we want a list of "in the rect" or "not in the rect" controls.

List<Control> getControls(Control container, Rectangle rect, bool bContained = false)
{
List<Control> Selected = new List<Control>();

rect = RectangleToClient(rect);

foreach (Control ctl in container.Controls)
{
//Do we want selected or non-selected controls
if (bContained)
{
foreach (Control ct in ctl.Controls) Selected.Add(ct);
}
else
{
foreach (Control ct in ctl.Controls) Selected.Add(ct);
}
}

return Selected;
}


You have an error there, unless you wanted to include all child controls regardless of whether they're contained. It should be:

if (rect.IntersectsWith(ctl.Bounds))
{
foreach (Control ct in ctl.Controls)
{
}
}


As for the test, instead of two cases you can use:

if(rect.IntersectsWith(ctl.Bounds) == bContained)

• Says, "cannot convert from 'System.Windows.Forms.Control.ControlCollection' to 'System.Collections.Generic.IEnumerable<System.Windows.Forms.Control>" – tobeypeters May 31 '18 at 0:23
• Didn't realize that only implemented the non-generic IEnumerable. A foreach loop works, in that case. – Errorsatz May 31 '18 at 0:28
• and the test won't work, in this case. bContained determines if we want all the "contained" or "non-contained" controls. – tobeypeters May 31 '18 at 0:30
• Yes, so if bContained is true, you want rect.IntersectsWith to be true; if it's false, you want it to be false - in other words, to equal bContained. – Errorsatz May 31 '18 at 0:31
• My bad ... Looked at it wrong. You are correct. But, doesn't matter. Everybody, over at stackoverflow said my function was junk and the wrong way to do it. Told me to use LINQ. Haven't figured out, how to do that. Although, to me the foreach() on the list<>, seems the same to me as just doing a foreach like above. – tobeypeters May 31 '18 at 1:07

You can make things more concise with LINQ. Here's one way that should work:

List<Control> getControls(Control container, Rectangle rect, bool bContained = false)
{
rect = RectangleToClient(rect);
return (from Control ctl in container.Controls
where rect.IntersectsWith(ctl.Bounds) == bContained
select new List<Control> { ctl }.Union(ctl.Controls.OfType<Control>()))
.SelectMany(x => x).ToList();
}


This basically does the same thing, filters the collection according to bContained. Creates a collection of lists that each contain the parent control and its sub-controls. Then flattens them into one list.

• if, I wanted to change all my foreach statements to use LINQ, how could I change: foreach (Control c in controlSelection) { c.BackColor = Color.Fuchsia; } – tobeypeters May 31 '18 at 3:33
• @tobeypeters - If you need to transform each element you are probably better off staying with the foreach loop. If needed, there is a ForEach method in the LINQ extensions which will accomplish the same task.controlSelection.ForEach(x => x.BackColor = Color.Fuchsia); – tinstaafl May 31 '18 at 3:45
• Ah ... This is. You sir, are very awesome. Thank you, for everything. I've meant to look into LINQ more and I will. – tobeypeters May 31 '18 at 3:47