Archive

Archive for June, 2009

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_32×32.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 :)

Tags: