Home > WPF > Finding an ancestor of a WPF dependency object

Finding an ancestor of a WPF dependency object

February 1st, 2008

This is a simple snippet which helps you to find a specified parent of a given WPF dependency object somewhere in its visual tree:

(Snippet updated 2009.09.14)

 

/// <summary>
/// Finds a parent of a given item on the visual tree.
/// </summary>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="child">A direct or indirect child of the
/// queried item.</param>
/// <returns>The first parent item that matches the submitted
/// type parameter. If not matching item can be found, a null
/// reference is being returned.</returns>
public static T TryFindParent<T>(this DependencyObject child)
    where T : DependencyObject
{
  //get parent item
  DependencyObject parentObject = GetParentObject(child);

  //we've reached the end of the tree
  if (parentObject == null) return null;

  //check if the parent matches the type we're looking for
  T parent = parentObject as T;
  if (parent != null)
  {
    return parent;
  }
  else
  {
    //use recursion to proceed with next level
    return TryFindParent<T>(parentObject);
  }
}

/// <summary>
/// This method is an alternative to WPF's
/// <see cref="VisualTreeHelper.GetParent"/> method, which also
/// supports content elements. Keep in mind that for content element,
/// this method falls back to the logical tree of the element!
/// </summary>
/// <param name="child">The item to be processed.</param>
/// <returns>The submitted item's parent, if available. Otherwise
/// null.</returns>
public static DependencyObject GetParentObject(this DependencyObject child)
{
  if (child == null) return null;
  
  //handle content elements separately
  ContentElement contentElement = child as ContentElement;
  if (contentElement != null)
  {
    DependencyObject parent = ContentOperations.GetParent(contentElement);
    if (parent != null) return parent;

    FrameworkContentElement fce = contentElement as FrameworkContentElement;
    return fce != null ? fce.Parent : null;
  }

  //also try searching for parent in framework elements (such as DockPanel, etc)
  FrameworkElement frameworkElement = child as FrameworkElement;
  if (frameworkElement != null)
  {
    DependencyObject parent = frameworkElement.Parent;
    if (parent != null) return parent;
  }

  //if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
  return VisualTreeHelper.GetParent(child);
}

 

 

This snippet works with arbitrary dependency objects that are of Type Visual or Visual3D. So let’s say you need a reference to the Window that hosts a given Button control somewhere, all you need is this:

Button myButton = ...
Window parentWindow = UIHelper.TryFindParent<Window>(myButton);

 

The above TryFindParent method also makes it easy to get an item at a given position. The method below performs a hit test based on a given position. If hit testing does not return the requested item (e.g. a clicked CheckBox on a tree, while you are keen on the TreeViewItem that hosts the CheckBox), the procedure delegates the lookup to TryFindParent.

This comes in very handy for mouse-related events if you just need to now what’s under your mouse pointer:

/// <summary>
/// Tries to locate a given item within the visual tree,
/// starting with the dependency object at a given position. 
/// </summary>
/// <typeparam name="T">The type of the element to be found
/// on the visual tree of the element at the given location.</typeparam>
/// <param name="reference">The main element which is used to perform
/// hit testing.</param>
/// <param name="point">The position to be evaluated on the origin.</param>
public static T TryFindFromPoint<T>(UIElement reference, Point point)
  where T:DependencyObject
{
  DependencyObject element = reference.InputHitTest(point)
                               as DependencyObject;
  if (element == null) return null;
  else if (element is T) return (T)element;
  else return TryFindParent<T>(element);
}

Author: Categories: WPF Tags:
  1. February 6th, 2008 at 20:23 | #1

    We also needed a method like this. The problem we had with VisualTreeHelper.GetParent is that it doesn’t work with “content elements” like Hyperlink, Run, etc.:
    http://code.logos.com/blog/2008/02/finding_ancestor_elements_in_w.html

  2. February 7th, 2008 at 17:07 | #2

    Ed,
    Thanks for your addition! I’ve updated the article accordingly.

  3. AVEbrahimi
    April 24th, 2008 at 15:21 | #3

    Thanks, easy to use, works!

  4. July 16th, 2008 at 10:24 | #4

    Thank you for this contribution. May I suggest, though, that you name your method something other than TryFindParent? As stated in your blog entry’s title, the method is for locating an ancestor, not just for getting the parent. Also I think “Try” is unnecessary since that would mean the name of every method that may return null should be prefixed with “Try” which is no one’s standard. So simply “FindAncestor” seems like a good name in my opinion.

  5. Jeffrey Sadeli
    March 24th, 2009 at 19:11 | #5

    Hi Philipp, your (and Ed Ball’s) GetParentObject method ‘almost’ work in my case. Doing a GetParentObject on an element inside a WPF Label for example will fail. This extra code fixes that problem:

    // try searching for a parent in content controls (such as Label, etc)
    ContentControl contentControl = child as ContentControl;
    if (contentControl != null)
    {
    DependencyObject parent = contentControl.Parent;
    if (parent != null) return parent;
    }

  6. April 28th, 2009 at 13:41 | #6

    this does not work in next case:
    page
    ___tabcontrol
    ______tabitem1
    __________Dockpanel
    _______________Button
    ______tabitem2

    if i try to find ancestor of button on first step
    i get dockpanel, but on the second step i get null (T_T)

    • April 28th, 2009 at 16:05 | #7

      Pasza,

      I’m not sure how to interpret your feedback. Given your hierarchy, the second tab indeed does not contain a dock panel, does it? If you want, you can send me a sample and I’ll have a look at it.

  7. Jeffrey Sadeli
    July 8th, 2009 at 18:28 | #8

    @Philipp Sumi

    Pasza is right, GetParentObject(Dockpanel) returns null where it ‘should’ be returning tabitem1. Here’s the fix to it (add it to the growing list of if-else statement). XD

    // try searching for parent in framework elements (such as DockPanel, etc)
    FrameworkElement frameworkElement = child as FrameworkElement;
    if (frameworkElement != null)
    {
    DependencyObject parent = frameworkElement.Parent;
    if (parent != null) return parent;
    }

  8. Andrei
    February 7th, 2010 at 21:51 | #9

    Good article. I don’t understand why in WPF isnt’t as simple as it is in C# to find the ancestor of an element. In c# is very easy to write this.Parent.Parent, but in WPF is too complicated..

  9. John
    March 3rd, 2010 at 18:20 | #10

    Thanks!!!!!

  10. August 12th, 2010 at 21:50 | #11

    This has been so useful today !

  11. August 17th, 2010 at 20:35 | #12

    One change I would suggest is to do the VisualTreeHelper lookup before the FrameworkElement.Parent. That way you don’t short-circuit the tree lookup by jumping to a Logical tree element. FrameworkElement.Parent skips lot of elements in between, which may be useful.

  12. September 23rd, 2010 at 17:37 | #13

    Thanks, nice job! Don’t know why they gotta make it so danged hard?
    I guess with versatility comes complexity.

  13. October 30th, 2010 at 09:21 | #14

    Pavan,

    Sounds good! I’ll check the suggestion and update the snippet accordingly. Thanks :)

  14. December 1st, 2010 at 11:22 | #15

    Hi Philipp,

    Google landed me back on your blog again ;-)

    Now if you’d be so kind as to write a Silverlight implementation of ContentOperations I would be able to find the Popup which is the parent of this pesky control.

    Regards, Colin E.

  15. December 1st, 2010 at 11:27 | #16

    Hey Colin

    Hmm, good point. I might need that anyway soon, so I’ll look into it tonight ;)

  16. samir
    August 25th, 2011 at 05:09 | #17

    Thanks a lot,
    this stuff helps me much
    Regards, Samir K

  17. Luojana
    January 18th, 2013 at 08:30 | #18

    HI,

    I have used this “VisualTreeHelper.GetParent(child)”, and it is throwing exception while parsing ‘FlowDocument’ as its not derived from Visual. How to get rid of this?
    (note : it was happen while parsing RichTextBox)

    Thanks,

  1. March 21st, 2009 at 20:38 | #1
  2. March 24th, 2009 at 12:19 | #2
  3. June 25th, 2009 at 13:54 | #3
  4. December 30th, 2010 at 11:22 | #4
  5. December 30th, 2010 at 11:27 | #5
  6. August 31st, 2012 at 12:40 | #6