Home > WPF > Finding Elements in the WPF Tree – Both Ways

Finding Elements in the WPF Tree – Both Ways

June 25th, 2009

A while ago I posted a helper method to traverse a (visual or logical) tree in order to find an element’s parent of a given type. The corresponding blog entry is here.

This time, I needed to search the other way: I wanted to find all descendants (direct or indirect childs) of a given element that match a given type. As a result, I came up with a complementary extension method that does the job for me:

//get all TextBox controls within the grid:
Grid container;
IEnumerable<TextBox> editors = container.FindChildren<TextBox>();

 

Usage Sample

As a sample, let’s use a window dialog which contains several images:

  • Images that were pasted into a RichTextBox control.
  • Images that are part of the UI, nested within the control hierarchy of a ribbon.

editor

 

In order to find all these Image elements you can see on the screenshot, this will do:

//start a the root (the window itself)
Window window = this;
foreach (Image img in window.FindChildren<Image>())
{
  Console.WriteLine("Image source: " + img.Source);
}

 

 

…the above snippet produces the following output:

Image source: pack://payload:,,wpf1,/Xaml/Image1.png

Image source: pack://payload:,,wpf1,/Xaml/Image2.png

Image source: pack://application:,,,/Shared/Images/Ribbon/Paste_32x32.png

Image source: System.Windows.Media.Imaging.FormatConvertedBitmap

Image source: System.Windows.Media.Imaging.FormatConvertedBitmap

Image source: pack://application:,,,/Shared/Images/Ribbon/EditUndo.png

Image source: pack://application:,,,/Shared/Images/Ribbon/EditRedo.png

Image source: pack://application:,,,/Shared/Images/Ribbon/Bold.png

Image source: System.Windows.Media.Imaging.BitmapFrameEncode

[…]

 

Accordingly, in order to analyze only the contents of the rich text editor, just start on a lower level of the tree:

//only examine the contents of the editor
RichTextBox editor = this.txtContent;
foreach (Image img in editor.FindChildren<Image>())
{
  Console.WriteLine("Image source: " + img.Source);
}

 

Implementation


(Download link at the end of the posting)

/// <summary> /// Analyzes both visual and logical tree in order to find all elements /// of a given type that are descendants of the <paramref name="source"/> /// item. /// </summary> /// <typeparam name="T">The type of the queried items.</typeparam> /// <param name="source">The root element that marks the source of the /// search. If the source is already of the requested type, it will not /// be included in the result.</param> /// <returns>All descendants of <paramref name="source"/> that match the /// requested type.</returns> public static IEnumerable<T> FindChildren<T>(this DependencyObject source) where T : DependencyObject { if (source != null) { var childs = GetChildObjects(source); foreach (DependencyObject child in childs) { //analyze if children match the requested type if (child != null && child is T) { yield return (T) child; } //recurse tree foreach (T descendant in FindChildren<T>(child)) { yield return descendant; } } } } /// <summary> /// This method is an alternative to WPF's /// <see cref="VisualTreeHelper.GetChild"/> method, which also /// supports content elements. Do note, that for content elements, /// this method falls back to the logical tree of the element. /// </summary> /// <param name="parent">The item to be processed.</param> /// <returns>The submitted item's child elements, if available.</returns> public static IEnumerable<DependencyObject> GetChildObjects( this DependencyObject parent) { if (parent == null) yield break;

if (parent is ContentElement || parent is FrameworkElement) { //use the logical tree for content / framework elements foreach (object obj in LogicalTreeHelper.GetChildren(parent)) { var depObj = obj as DependencyObject; if (depObj != null) yield return (DependencyObject) obj; } } else { //use the visual tree per default int count = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < count; i++) { yield return VisualTreeHelper.GetChild(parent, i); } } }

 

For your convenience, I’ve put together a simple helper class that contains the helper methods to search for both children and parent elements in the tree:

http://www.hardcodet.net/uploads/2009/06/UIHelper.cs

 

Happy coding :)


Author: Categories: WPF Tags:
  1. IgorP
    August 12th, 2009 at 16:26 | #1

    Excellent! Thanks

  2. Bobo
    August 20th, 2009 at 13:52 | #2

    Great article. I have one problem and I was wandering if you can help me. I have TabControl with few tabs on it. When I call FindChildren method with TabControl reference as argument in order to find TextBlock controls on all tab pages result is empty. Of course I have at least one TextBlock control on each tab page.

  3. Bobo
    August 20th, 2009 at 14:16 | #3

    @Bobo
    I just want to correct my previous post. FindChildren method returns only controls from the first tab page. controls on other tab pages are not returned.

  4. Anonymous
    August 28th, 2009 at 21:37 | #4

    @Bobo
    I ran into the same problem. This worked for me, although as far as I can tell it essentially does the exact same thing.

    private IEnumerable FindLogicalChildren(DependencyObject depObj) where T : DependencyObject
    {
    if (depObj != null)
    {
    foreach (object childObj in LogicalTreeHelper.GetChildren(depObj))
    {
    DependencyObject child = childObj as DependencyObject;
    if (child != null && child is T)
    {
    yield return (T)child;
    }
    foreach (T childOfChild in FindLogicalChildren(child))
    {
    yield return childOfChild;
    }
    }
    }
    }

  5. September 14th, 2009 at 19:43 | #5

    The method failed for FrameworkElements, which require to traverse the logical rather than the visual tree. This is fixed now – thanks for reporting :)

  6. Mr. X
    October 11th, 2009 at 12:39 | #6

    I am using your FindChildren in my Project, but now i run into a problem:
    The method doesn’t find child elements that are collapsed or hidden. Is there a way to find them anyway?

  7. Samar
    April 9th, 2010 at 11:41 | #7

    FANTASTIC..!!!!

  8. Alex
    November 8th, 2011 at 20:12 | #8

    Hi!
    How about traversing the visual tree of a DataGrid? It doesn’t work with VisualTreeHelper.I actually need to find all column headers, whether string, controls or datatemplates.

  9. David
    May 4th, 2012 at 20:36 | #9

    Excellent thanks saved me time

  10. Marcos
    September 6th, 2012 at 12:55 | #10

    Very good!

    I updated this method to search by name.

    public static IEnumerable
    FindChildrenByName(this DependencyObject source, string name)
    where T : DependencyObject
    {
    if (source != null)
    {
    var childs = GetChildObjects(source);
    foreach (DependencyObject child in childs)
    {
    //analyze if children match the requested type
    if (child != null && child is T)
    {
    FrameworkElement fwk = null;
    try
    {
    fwk = (FrameworkElement)child;
    }
    catch{
    }

    if (fwk != null && fwk.Name.Equals(name))
    {
    yield return (T)child;
    }
    }

    //recurse tree
    foreach (T descendant in FindChildrenByName(child,name))
    {
    yield return descendant;
    }
    }
    }
    }

    Marcos

  1. March 4th, 2013 at 10:49 | #1