Finding an ancestor of a WPF dependency object
Feb 1st, 2008 by Philipp Sumi
This is a simple snippet which helps you to find a specified parent of a given WPF dependency object somewhere in its visual tree:
/// <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>(DependencyObject child) where T : DependencyObject { //get parent item DependencyObject parentObject = VisualTreeHelper.GetParent(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); } }
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);
Do note that TryFindParent uses VisualTreeHelper.GetParent to determine its parent, which does not support ContentElements (e.g. Run). If you want to handle content elements as well, Ed Ball has a simple alternative that falls back to the logical tree.
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); }
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
Ed,
Thanks for your addition! I’ve updated the article accordingly.
Thanks, easy to use, works!
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.