Coder Perfect

Find all controls in WPF Window by type

Problem

I’m seeking for a way to sort all of the controls on the Window by type.

Find all TextBoxes, for example, or all controls that implement a given interface.

Asked by Andrija

Solution #1

This ought to suffice:

public static IEnumerable<T> FindVisualChilds<T>(DependencyObject depObj) where T : DependencyObject
{
    if (depObj == null) yield return (T)Enumerable.Empty<T>();
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
    {
        DependencyObject ithChild = VisualTreeHelper.GetChild(depObj, i);
        if (ithChild == null) continue;
        if (ithChild is T t) yield return t;
        foreach (T childOfChild in FindVisualChilds<T>(ithChild)) yield return childOfChild;
    }
}

Then you go over the controls one by one, like this.

foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
    // do something with tb here
}

Answered by Bryce Kahle

Solution #2

The simplest method is:

IEnumerable<myType> collection = control.Children.OfType<myType>(); 

The root element of the window is called control.

EDIT: As pointed out in the comments, this is incorrect. This only goes up one notch. For a more in-depth option, see the acceptable answer.

Answered by Joel

Solution #3

I modified @Bryce Kahle’s response to utilize LogicalTreeHelper as suggested by @Mathias Lykkegaard Lorenzen.

It appears to be working properly. 😉

public static IEnumerable<T> FindLogicalChildren<T> ( DependencyObject depObj ) where T : DependencyObject
{
    if( depObj != null )
    {
        foreach( object rawChild in LogicalTreeHelper.GetChildren( depObj ) )
        {
            if( rawChild is DependencyObject )
            {
                DependencyObject child = (DependencyObject)rawChild;
                if( child is T )
                {
                    yield return (T)child;
                }

                foreach( T childOfChild in FindLogicalChildren<T>( child ) ) 
                {
                    yield return childOfChild;
                }
            }
        }
    }
}

(As @Benjamin Berry and @David R pointed out, it still won’t verify tab controls or Grids inside GroupBoxes.) (I also deleted the superfluous child!= null, as suggested by @noonand.)

Answered by Simon F

Solution #4

Depending on whatever tree you’re interested in, use the VisualTreeHelper or LogicalTreeHelper helper classes. They both give techniques for obtaining an element’s offspring (although the syntax differs a little). I frequently use these classes to identify the first instance of a particular type, but you could easily extend them to find all objects of that kind:

public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type)
{
    if (obj != null)
    {
        if (obj.GetType() == type)
        {
            return obj;
        }

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            DependencyObject childReturn = FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type);
            if (childReturn != null)
            {
                return childReturn;
            }
        }
    }

    return null;
}

Answered by Oskar

Solution #5

I discovered that the line VisualTreeHelper.GetChildrenCount(depObj); used in several of the examples above does not yield a non-zero count for GroupBoxes, especially when the GroupBox contains a Grid with children elements. I assume this is due to the fact that the GroupBox can only have one child, which is stored in its Content attribute. There is no such thing as a GroupBox. Property that is suitable for children. I’m sure I didn’t do it very well, but here’s how I changed the first “FindVisualChildren” example in this chain:

public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject 
{ 
    if (depObj != null) 
    {
        int depObjCount = VisualTreeHelper.GetChildrenCount(depObj); 
        for (int i = 0; i <depObjCount; i++) 
        { 
            DependencyObject child = VisualTreeHelper.GetChild(depObj, i); 
            if (child != null && child is T) 
            { 
                yield return (T)child; 
            }

            if (child is GroupBox)
            {
                GroupBox gb = child as GroupBox;
                Object gpchild = gb.Content;
                if (gpchild is T)
                {
                    yield return (T)child; 
                    child = gpchild as T;
                }
            }

            foreach (T childOfChild in FindVisualChildren<T>(child)) 
            { 
                yield return childOfChild; 
            } 
        }
    }
} 

Answered by David R

Post is based on https://stackoverflow.com/questions/974598/find-all-controls-in-wpf-window-by-type