namespace AntDesignWPF.Helpers { using System.Collections.Generic; using System.Windows; using System.Windows.Media; using System.Windows.Media.Media3D; /// /// Helper methods for UI-related tasks. /// This class was obtained from Philip Sumi (a fellow WPF Disciples blog) /// http://www.hardcodet.net/uploads/2009/06/UIHelper.cs /// public static class TreeHelper { #region Find Parent /// /// Finds a parent of a given item on the visual tree. /// /// The type of the queried item. /// A direct or indirect child of the /// queried item. /// The first parent item that matches the submitted /// type parameter. If not matching item can be found, a null /// reference is being returned. public static T TryFindParent(this DependencyObject child) where T : DependencyObject { //get parent item var parentObject = child.GetParentObject(); //we've reached the end of the tree if (parentObject == null) return null; //check if the parent matches the type we're looking for if (parentObject is T parent) { return parent; } else { //use recursion to proceed with next level return parentObject.TryFindParent(); } } /// /// This method is an alternative to WPF's /// method, which also /// supports content elements. Keep in mind that for content element, /// this method falls back to the logical tree of the element! /// /// The item to be processed. /// The submitted item's parent, if available. Otherwise /// null. public static DependencyObject GetParentObject(this DependencyObject child) { if (child == null) return null; //handle content elements separately if (child is ContentElement contentElement) { var parent = ContentOperations.GetParent(contentElement); if (parent != null) return parent; return contentElement is FrameworkContentElement fce ? fce.Parent : null; } //also try searching for parent in framework elements (such as DockPanel, etc) if (child is FrameworkElement frameworkElement) { var parent = frameworkElement.Parent; if (parent != null) return parent; } //if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper return VisualTreeHelper.GetParent(child); } /// /// Finds all Ancestors of a given item on the visual tree. /// /// A node in a visual tree /// All ancestors in visual tree of element public static IEnumerable GetAncestors(this DependencyObject child) { var parent = VisualTreeHelper.GetParent(child); while (parent != null) { yield return parent; parent = VisualTreeHelper.GetParent(parent); } } #endregion #region Find children /// /// Finds a Child of a given item in the visual tree. /// /// A direct parent of the queried item. /// The type of the queried item. /// x:Name or Name of child. /// The first parent item that matches the submitted type parameter. /// If not matching item can be found, /// a null parent is being returned. public static T FindChild(this DependencyObject parent, string childName) where T : DependencyObject { // Confirm parent and childName are valid. if (parent == null) return null; T foundChild = null; var childrenCount = VisualTreeHelper.GetChildrenCount(parent); for (var i = 0; i < childrenCount; i++) { var child = VisualTreeHelper.GetChild(parent, i); // If the child is not of the request child type child var childType = child as T; if (childType == null) { // recursively drill down the tree foundChild = child.FindChild(childName); // If the child is found, break so we do not overwrite the found child. if (foundChild != null) break; } else if (!string.IsNullOrEmpty(childName)) { var frameworkInputElement = child as IFrameworkInputElement; // If the child's name is set for search if (frameworkInputElement != null && frameworkInputElement.Name == childName) { // if the child's name is of the request name foundChild = (T)child; break; } else { // recursively drill down the tree foundChild = child.FindChild(childName); // If the child is found, break so we do not overwrite the found child. if (foundChild != null) break; } } else { // child element found. foundChild = (T)child; break; } } return foundChild; } /// /// Analyzes both visual and logical tree in order to find all elements of a given /// type that are descendants of the item. /// /// The type of the queried items. /// 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. /// All descendants of that match the requested type. public static IEnumerable FindChildren(this DependencyObject source) where T : DependencyObject { if (source != null) { var childs = source.GetChildObjects(); foreach (var child in childs) { //analyze if children match the requested type if (child != null && child is T) { yield return (T)child; } //recurse tree foreach (var descendant in child.FindChildren()) { yield return descendant; } } } } /// /// This method is an alternative to WPF's /// method, which also /// supports content elements. Keep in mind that for content elements, /// this method falls back to the logical tree of the element. /// /// The item to be processed. /// Sometimes it's better to search in the VisualTree (e.g. in tests) /// The submitted item's child elements, if available. public static IEnumerable GetChildObjects(this DependencyObject parent, bool forceUsingTheVisualTreeHelper = false) { if (parent == null) yield break; if (!forceUsingTheVisualTreeHelper && (parent is ContentElement || parent is FrameworkElement)) { //use the logical tree for content / framework elements foreach (var obj in LogicalTreeHelper.GetChildren(parent)) { var depObj = obj as DependencyObject; if (depObj != null) yield return (DependencyObject)obj; } } else if (parent is Visual || parent is Visual3D) { //use the visual tree per default var count = VisualTreeHelper.GetChildrenCount(parent); for (var i = 0; i < count; i++) { yield return VisualTreeHelper.GetChild(parent, i); } } } #endregion #region Find From Point /// /// Tries to locate a given item within the visual tree, /// starting with the dependency object at a given position. /// /// The type of the element to be found /// on the visual tree of the element at the given location. /// The main element which is used to perform /// hit testing. /// The position to be evaluated on the origin. public static T TryFindFromPoint(UIElement reference, Point point) where T : DependencyObject { if (!(reference.InputHitTest(point) is DependencyObject element)) return null; else if (element is T) return (T)element; else return element.TryFindParent(); } #endregion } }