Generic WinForms Control Finder

Ketsuekiame 2 Tallied Votes 303 Views Share

Now, before you say it already exists (because it does) I found that those didn't really have the generic ability to find any control on a form, regardless of containers.

So, here you have a piece of code which will find any control on a form and will also search inside any containers those controls might have.

There are two ways of using this code. First is by Control Type and the second is by Control Name.

In each example below, I have used the variable "myForm" which can be any kind of Windows Form. However, you can perform the following on any control you wish. So it could have been performed on "myPanel" or "myGridBox" etc.

To use by Control Type simply perform the following: myForm.FindControl<Label>(); This will return an IEnumerable<Label> containing every single label on your form, regardless of any container it is placed in. You will be able to iterate back up the tree using the "Control.Parent" attribute to see which container it belongs to.

Secondly, you can search by Control Name. You may also filter the Control Type: myForm.FindControl<Control>("myLabel"); The above code will search every control on the form until it finds the control named "myLabel". "myLabel", however, is pretty obviously a Label control. So in order to speed up the comparison (not by much in 99.9% of cases, but you never know) you can do the following: myForm.FindControl<Label>("myLabel"); And there you have it. I hope you guys can find some use out of it. Please if you do use the code snippet, I ask that you leave a comment in the top of your file.

PS. Important note - The contents will come back with no sorting. So you may find that 2 controls from your form come back, then 3 from your panel and then another 2 from your form. If you wish that level of sorting, I leave it as an exercise for the reader (however, it would be pretty easy to do in LINQ)

Please leave a quick thanks if you use the code and constructive criticism if you feel I could have done better :)

ddanbe commented: Great! +14
/* 
 * This code has been uploaded to Daniweb and may be used
 * freely for commercial and non-commercial applications.
 * Please visit us at http://www.daniweb.com
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MyExtensions
{
    public static class Extensions
    {
        public static IEnumerable<T> FindControl<T>(this Control parentControl, String name = "")
        {
            if (parentControl is T)
            {
                if (String.IsNullOrWhiteSpace(name))
                    yield return (T)(object)parentControl;
                else if (parentControl.Name.Equals(name))
                {
                    yield return (T)(object)parentControl;
                    yield break;
                }
            }
            
            var filteredControlList = from controlList in parentControl.Controls.Cast<Control>()
                                        where controlList is T || controlList.Controls.Count > 0
                                        select controlList;
            foreach (Control childControl in filteredControlList)
            {
                foreach(T foundControl in FindControl<T>(childControl, name))
                {
                    yield return foundControl;
                    if (!String.IsNullOrWhiteSpace(name))
                        yield break;
                }
            }
        }
    }
}
ddanbe 2,724 Professional Procrastinator Featured Poster

Looks like great stuff.
Surely will try this out soon :)

Ketsuekiame 860 Master Poster Featured Poster

Glad you like it, there is a much much simpler way to do this, but it involves leaving the comparison logic down to the consumer using function delegates. ie FindControl(myControl, p => p.Name.Equals("ControlName") && p is Label); The code for it is a lot shorter, however, I believe this way takes care of the most common two search methods. Search by name and Search By Type all at once without requiring the consumer to repetitively write a function delegate (and there by introduce copy/pasta bugs) :)

Franz_1 0 Newbie Poster

I've redesigned it for eworking on WinCE .NET CF:
The differences are String.IsNullOrEmpty() and parentControl.Controls.OfType<Control>()
It works like a charm!

    public static IEnumerable<T> FindControls<T>(this Control parentControl, String name) {
        if (parentControl is T) {
            if (String.IsNullOrEmpty(name))
                yield return (T)(object)parentControl;
            else if (parentControl.Name.Equals(name)) {
                yield return (T)(object)parentControl;
                yield break;
            }
        }
        var filteredControlList = from controlList in parentControl.Controls.OfType<Control>()
                                  where controlList is T || controlList.Controls.Count > 0
                                  select controlList;
        foreach (Control childControl in filteredControlList) {
            foreach (T foundControl in FindControls<T>(childControl, name)) {
                yield return foundControl;
                if (!String.IsNullOrEmpty(name))
                    yield break;
            }
        }
    }
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.