Finding an ancestor of a WPF dependency object
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); }
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.
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;
}
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)
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.
@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;
}
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..
Thanks!!!!!
This has been so useful today !
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.
Thanks, nice job! Don’t know why they gotta make it so danged hard?
I guess with versatility comes complexity.
Pavan,
Sounds good! I’ll check the suggestion and update the snippet accordingly. Thanks π
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.
Hey Colin
Hmm, good point. I might need that anyway soon, so I’ll look into it tonight π
Thanks a lot,
this stuff helps me much
Regards, Samir K
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,
Hey, I just wanted to thank you a dozen times, this is really awesome. But can someone please explain why you rely on VisualTreeHelper in GetParentObject? I tried to get the parent object recursively solely with VisualTreehelper and it didn’t work, because at some Point child would be content presenter, thus the parent (grid) had parent = null.
Many thanks! Works a treat and has saved me a lot of time.
Philipp,
Thank you for the great post and explanation.
May I ask though β what is the license on the code presented in this blog post and referenced C# file, if any?