using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Media; namespace MediaBrowser.UI.Controls { /// /// Helper methods for UI-related tasks. /// public static class TreeHelper { /// /// Gets the window. /// /// The element. /// Window. /// The window. public static Window GetWindow(this FrameworkElement element) { return element.ParentOfType(); } /// /// Gets the parent. /// /// The element. /// DependencyObject. private static DependencyObject GetParent(this DependencyObject element) { DependencyObject parent = VisualTreeHelper.GetParent(element); if (parent == null) { FrameworkElement frameworkElement = element as FrameworkElement; if (frameworkElement != null) { parent = frameworkElement.Parent; } } return parent; } /// /// Gets the parents. /// /// The element. /// IEnumerable{DependencyObject}. /// element public static IEnumerable GetParents(this DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } while ((element = element.GetParent()) != null) { yield return element; } yield break; } /// /// Parents the type of the of. /// /// /// The element. /// ``0. public static T ParentOfType(this DependencyObject element) where T : DependencyObject { if (element == null) { return default(T); } return element.GetParents().OfType().FirstOrDefault(); } /// /// Finds a Child of a given item in the visual tree. /// /// The type of the queried item. /// A direct parent 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(DependencyObject parent, string childName) where T : DependencyObject { // Confirm parent and childName are valid. if (parent == null) return null; T foundChild = null; int childrenCount = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < childrenCount; i++) { var child = VisualTreeHelper.GetChild(parent, i); // If the child is not of the request child type child T childType = child as T; if (childType == null) { // recursively drill down the tree foundChild = FindChild(child, 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 frameworkElement = child as FrameworkElement; // If the child's name is set for search if (frameworkElement != null && frameworkElement.Name == childName) { // if the child's name is of the request name foundChild = (T)child; break; } } else { // child element found. foundChild = (T)child; break; } } return foundChild; } /// /// Gets the visual child. /// /// /// The reference visual. /// ``0. public static T GetVisualChild(this Visual referenceVisual) where T : Visual { Visual child = null; for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceVisual); i++) { child = VisualTreeHelper.GetChild(referenceVisual, i) as Visual; if (child != null && (child.GetType() == typeof(T))) { break; } else if (child != null) { child = GetVisualChild(child); if (child != null && (child.GetType() == typeof(T))) { break; } } } return child as T; } #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 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; } //use recursion to proceed with next level return TryFindParent(parentObject); } /// /// 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 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); } #endregion #region find children /// /// 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 = GetChildObjects(source); foreach (DependencyObject child in childs) { //analyze if children match the requested type if (child is T) { yield return (T)child; } //recurse tree foreach (T descendant in FindChildren(child)) { 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. /// The submitted item's child elements, if available. public static IEnumerable 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); } } } #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. /// ``0. public static T TryFindFromPoint(UIElement reference, Point point) where T : DependencyObject { DependencyObject element = reference.InputHitTest(point) as DependencyObject; if (element == null) return null; if (element is T) return (T)element; return TryFindParent(element); } #endregion } }