248

I'm looking for a way to find all controls on Window by their type,

for example: find all TextBoxes, find all controls implementing specific interface etc.

akjoshi
  • 15,374
  • 13
  • 103
  • 121
Andrija
  • 14,037
  • 18
  • 60
  • 87
  • 1
    I also wrote a blog post on the topic: [Modifying a ControlTemplate at Runtime](http://wpfexperiments.blogspot.com/2012/08/modifying-controltemplate-in-style-at.html) – Adolfo Perez Aug 31 '12 at 12:52

17 Answers17

470

This should do the trick:

public static IEnumerable<T> FindVisualChildren<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 FindVisualChildren<T>(ithChild)) yield return childOfChild;
    }
}

then you enumerate over the controls like so

foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
    // do something with tb here
}
T.Todua
  • 53,146
  • 19
  • 236
  • 237
Bryce Kahle
  • 8,159
  • 1
  • 23
  • 26
  • 74
    Note: If you're trying to get this to work and finding that your Window (for instance) has 0 visual children, try running this method in the Loaded event handler. If you run it in the constructor (even after InitializeComponent()), the visual children aren't loaded yet, and it won't work. – Ryan Lundy Aug 28 '09 at 15:02
  • FxCop will catch this, refer http://msdn.microsoft.com/library/ms182150(VS.90).aspx – Nitin Chaudhari Sep 03 '10 at 10:14
  • this will not work for childs which are not loaded but present. For example hidden by Expander. How to enumerate through all? – Konstantin Salavatov Apr 10 '12 at 05:41
  • 27
    Switching from VisualTreeHelper to LogicalTreeHelpers will cause invisible elements to be included too. – Mathias Lykkegaard Lorenzen Jun 04 '12 at 05:47
  • 12
    Isn't the line "child != null && child is T" redundant? Should it not just read "child is T" – noonand May 16 '13 at 18:10
  • How to do the same from background thread to avoid error `The calling thread cannot access this object because a different thread owns it.`?`this.gForm.Dispatch(p => p.GetChildOfType());` - getting such an error when accessing to children. `Dispatch` - my custom extension method to access UI elements from background thread. – angularrocks.com Sep 11 '13 at 17:21
  • 2
    I would turn it into an extension method with just insering a `this` before `DependencyObject` => `this DependencyObject depObj` – Johannes Wanzek Oct 07 '14 at 05:25
  • 2
    @JohannesWanzek Don't forget you would also need to change the bit where you call it on the child : foreach(ChildofChild.FindVisualChildren()){bla bla bla} – Will Dec 22 '14 at 01:54
  • Could somebody pass VB.NET equivalent code for this (struggling with yield...) – Nuts Jan 27 '15 at 13:20
  • 1
    But, It will create N2 (Square). any other way? – Gul Ershad Jun 12 '15 at 19:20
  • +1 for the @MathiasLykkegaardLorenzen answer! Using foreach (var child in LogicalTreeHelper.GetChildren(depObj)) fixes the problem with the invisible elements. – Vasil Popov Oct 07 '15 at 12:35
  • 1
    I wanted to use this method in this scenario; I have a TabControl with 2 tabs and 1 ListBox inside each tab. I want to be able to access ListBox of currently selected Tab. So in tabControl_SelectionChanged event handler I call your method (FindVisualChildren(this)), which gets me IEnumerable of ListBoxes. Well, it's a IEnumerable which has only 1 ListBox - and the problem is, that this ListBox is the one from PREVIOUSLY selected tab - not the current one. What's wrong? – mnj Sep 09 '16 at 10:48
  • I posted a solution in case you need the elements in descending order. The accepted solution will return the elements in a more or less random fashion. – lauxjpn Jun 09 '19 at 18:21
  • @MathiasLykkegaardLorenzen Can you post an example of how this would work? – Luke Vanzweden May 18 '21 at 14:18
  • 2
    `return;` won't compile. – Lei Yang Oct 22 '21 at 05:50
  • @LeiYang Looks like that changed was made without testing. I've rolled back the change since it doesn't serve any real purpose. – Clonkex Jan 20 '22 at 22:19
74

This is the easiest way:

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

where control is the root element of the window.

EDIT - As pointed out in the comments. This only goes one level deep. See the accepted answer for an option that goes deeper.

Joel
  • 16,474
  • 17
  • 72
  • 93
  • 1
    what do you mean "root element" ? What should I write to connect with my mainwindow form? – deadfish Dec 30 '11 at 16:02
  • 1
    I get it, in xaml view I had to set name for grid `here buttons` and then I could use `Anata_wa_yoru_o_shihai_suru_ai.Children.OfType();` – deadfish Dec 30 '11 at 16:41
  • 73
    This does not answer the question that was asked. It only returns child controls one level deep. – Jim May 06 '13 at 19:43
  • @Joel In your response you may want to mention that your suggested code would work for controls only one level deep. It will help other readers. But your code is still very useful - and it worked for my `special` case (thank you). – nam Sep 03 '20 at 04:45
  • I like your code, it applies to the situation I encountered, and it is simple and intuitive – Ian Dec 30 '21 at 03:23
30

I adapted @Bryce Kahle's answer to follow @Mathias Lykkegaard Lorenzen's suggestion and use LogicalTreeHelper.

Seems to work okay. ;)

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;
                }
            }
        }
    }
}

(It still won't check tab controls or Grids inside GroupBoxes as mentioned by @Benjamin Berry & @David R respectively.) (Also followed @noonand's suggestion & removed the redundant child != null)

Simon F
  • 1,299
  • 1
  • 11
  • 14
  • 1
    been looking for a while how to clear all my text boxes, I have multiple tabs and this is the only code that worked:) thanks – JohnChris Jan 12 '17 at 14:28
  • 1
    this worked for me better, gives a flat result Enumerator for nested children (MenuItems in my case) – dba Jul 31 '20 at 11:33
16

Use the helper classes VisualTreeHelper or LogicalTreeHelper depending on which tree you're interested in. They both provide methods for getting the children of an element (although the syntax differs a little). I often use these classes for finding the first occurrence of a specific type, but you could easily modify it to find all objects of that type:

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;
}
Oskar
  • 7,945
  • 5
  • 36
  • 44
  • +1 for explanation and post but Bryce Kahle posted function that fully works Thanks – Andrija Jun 10 '09 at 23:18
  • This does not fix the issue of the question, and also the answer with the generic type is much clearer. Combining it with the use of VisualTreeHelper.GetChildrenCount(obj) will fix the issue. However is useful to be considered as an option. – Vasil Popov Oct 07 '15 at 12:38
11

I found that the line, VisualTreeHelper.GetChildrenCount(depObj);, used in several examples above does not return a non-zero count for GroupBoxes, in particular, where the GroupBox contains a Grid, and the Grid contains children elements. I believe this may be because the GroupBox is not allowed to contain more than one child, and this is stored in its Content property. There is no GroupBox.Children type of property. I am sure I did not do this very efficiently, but I modified the first "FindVisualChildren" example in this chain as follows:

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; 
            } 
        }
    }
} 
David R
  • 111
  • 1
  • 2
6

Here is yet another, compact version, with the generics syntax:

    public static IEnumerable<T> FindLogicalChildren<T>(DependencyObject obj) where T : DependencyObject
    {
        if (obj != null) {
            if (obj is T)
                yield return obj as T;

            foreach (DependencyObject child in LogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>()) 
                foreach (T c in FindLogicalChildren<T>(child)) 
                    yield return c;
        }
    }
QT-1
  • 900
  • 14
  • 21
4

Small change to the recursion to so you can for example find the child tab control of a tab control.

    public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type)
    {
        if (obj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);

                if (child.GetType() == type)
                {
                    return child;
                }

                DependencyObject childReturn = FindInVisualTreeDown(child, type);
                if (childReturn != null)
                {
                    return childReturn;
                }
            }
        }

        return null;
    }
4

To get a list of all childs of a specific type you can use:

private static IEnumerable<DependencyObject> FindInVisualTreeDown(DependencyObject obj, Type type)
{
    if (obj != null)
    {
        if (obj.GetType() == type)
        {
            yield return obj;
        }

        for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type))
            {
                if (child != null)
                {
                    yield return child;
                }
            }
        }
    }

    yield break;
}
Michael
  • 41
  • 1
2

Do note that using the VisualTreeHelper does only work on controls that derive from Visual or Visual3D. If you also need to inspect other elements (e.g. TextBlock, FlowDocument etc.), using VisualTreeHelper will throw an exception.

Here's an alternative that falls back to the logical tree if necessary:

http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways

Philipp
  • 301
  • 2
  • 4
2

My version for C++/CLI

template < class T, class U >
bool Isinst(U u) 
{
    return dynamic_cast< T >(u) != nullptr;
}

template <typename T>
    T FindVisualChildByType(Windows::UI::Xaml::DependencyObject^ element, Platform::String^ name)
    {
        if (Isinst<T>(element) && dynamic_cast<Windows::UI::Xaml::FrameworkElement^>(element)->Name == name)
        {
            return dynamic_cast<T>(element);
        }
        int childcount = Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(element);
        for (int i = 0; i < childcount; ++i)
        {
            auto childElement = FindVisualChildByType<T>(Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(element, i), name);
            if (childElement != nullptr)
            {
                return childElement;
            }
        }
        return nullptr;
    };
Whiso
  • 236
  • 2
  • 4
2

For some reason, none of the answers posted here helped me to get all controls of given type contained in a given control in my MainWindow. I needed to find all menu items in one menu to iterate them. They were not all direct descendants of the menu, so i managed to collect only the first lilne of them using any of the code above. This extension method is my solution for the problem for anyone who will continue to read all the way down here.

public static void FindVisualChildren<T>(this ICollection<T> children, DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            var brethren = LogicalTreeHelper.GetChildren(depObj);
            var brethrenOfType = LogicalTreeHelper.GetChildren(depObj).OfType<T>();
            foreach (var childOfType in brethrenOfType)
            {
                children.Add(childOfType);
            }

            foreach (var rawChild in brethren)
            {
                if (rawChild is DependencyObject)
                {
                    var child = rawChild as DependencyObject;
                    FindVisualChildren<T>(children, child);
                }
            }
        }
    }

Hope it helps.

αNerd
  • 528
  • 1
  • 6
  • 11
2

@Bryce, really nice answer.

VB.NET version:

Public Shared Iterator Function FindVisualChildren(Of T As DependencyObject)(depObj As DependencyObject) As IEnumerable(Of T)
    If depObj IsNot Nothing Then
        For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(depObj) - 1
            Dim child As DependencyObject = VisualTreeHelper.GetChild(depObj, i)
            If child IsNot Nothing AndAlso TypeOf child Is T Then
                Yield DirectCast(child, T)
            End If
            For Each childOfChild As T In FindVisualChildren(Of T)(child)
                Yield childOfChild
            Next
        Next
    End If
End Function

Usage (this disables all TextBoxes in a window):

        For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
          tb.IsEnabled = False
        Next
Andrea Antonangeli
  • 1,242
  • 1
  • 21
  • 32
2

For this and more use cases you can add flowing extension method to your library:

 public static List<DependencyObject> FindAllChildren(this DependencyObject dpo, Predicate<DependencyObject> predicate)
    {
        var results = new List<DependencyObject>();
        if (predicate == null)
            return results;


        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dpo); i++)
        {
            var child = VisualTreeHelper.GetChild(dpo, i);
            if (predicate(child))
                results.Add(child);

            var subChildren = child.FindAllChildren(predicate);
            results.AddRange(subChildren);
        }
        return results;
    }

Example for your case:

 var children = dpObject.FindAllChildren(child => child is TextBox);
2

And this is how it works upwards

    private T FindParent<T>(DependencyObject item, Type StopAt) where T : class
    {
        if (item is T)
        {
            return item as T;
        }
        else
        {
            DependencyObject _parent = VisualTreeHelper.GetParent(item);
            if (_parent == null)
            {
                return default(T);
            }
            else
            {
                Type _type = _parent.GetType();
                if (StopAt != null)
                {
                    if ((_type.IsSubclassOf(StopAt) == true) || (_type == StopAt))
                    {
                        return null;
                    }
                }

                if ((_type.IsSubclassOf(typeof(T)) == true) || (_type == typeof(T)))
                {
                    return _parent as T;
                }
                else
                {
                    return FindParent<T>(_parent, StopAt);
                }
            }
        }
    }
1

I wanted to add a comment but I have less than 50 pts so I can only "Answer". Be aware that if you use the "VisualTreeHelper" method to retrieve XAML "TextBlock" objects then it will also grab XAML "Button" objects. If you re-initialize the "TextBlock" object by writing to the Textblock.Text parameter then you will no longer be able to change the Button text using the Button.Content parameter. The Button will permanently show the text written to it from the Textblock.Text write action (from when it was retrieved --

foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
   tb.Text = ""; //this will overwrite Button.Content and render the 
                 //Button.Content{set} permanently disabled.
}

To work around this, you can try using a XAML "TextBox" and add methods (or Events) to mimic a XAMAL Button. XAML "TextBox" is not gathered by a search for "TextBlock".

Lifygen
  • 21
  • 4
  • That's the difference between the visual and the logical tree. The visual tree contains _every_ control (including the ones a control is made of, which are defined in the controls template) while the logical tree only contains the _actual_ controls (without those defined in templates). There is a nice visualization of this concept here: [link](https://www.wpftutorial.net/LogicalAndVisualTree.html) – lauxjpn Sep 02 '19 at 22:40
-1

I found it easier without Visual Tree Helpers:

foreach (UIElement element in MainWindow.Children) {
    if (element is TextBox) { 
        if ((element as TextBox).Text != "")
        {
            //Do something
        }
    }
};
Rafael Ventura
  • 284
  • 3
  • 13
-1

The accepted answer returns the discovered elements more or less unordered, by following the first child branch as deep as possible, while yielding the discovered elements along the way, before backtracking and repeating the steps for not yet parsed tree branches.

If you need the descendent elements in descending order, where the direct children will be yielded first, then their children and so on, the following algorithm will work:

public static IEnumerable<T> GetVisualDescendants<T>(DependencyObject parent, bool applyTemplates = false)
    where T : DependencyObject
{
    if (parent == null || !(child is Visual || child is Visual3D))
        yield break;

    var descendants = new Queue<DependencyObject>();
    descendants.Enqueue(parent);

    while (descendants.Count > 0)
    {
        var currentDescendant = descendants.Dequeue();

        if (applyTemplates)
            (currentDescendant as FrameworkElement)?.ApplyTemplate();

        for (var i = 0; i < VisualTreeHelper.GetChildrenCount(currentDescendant); i++)
        {
            var child = VisualTreeHelper.GetChild(currentDescendant, i);

            if (child is Visual || child is Visual3D)
                descendants.Enqueue(child);

            if (child is T foundObject)
                yield return foundObject;
        }
    }
}

The resulting elements will be ordered from nearest to farthest. This will be useful e.g. if you are looking for the nearest child element of some type and condition:

var foundElement = GetDescendants<StackPanel>(someElement)
                       .FirstOrDefault(o => o.SomeProperty == SomeState);
lauxjpn
  • 4,749
  • 1
  • 20
  • 40