TreeHelper.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. using System.Collections.Generic;
  2. using System.Windows;
  3. using System.Windows.Media;
  4. namespace MediaBrowser.UI.Controls
  5. {
  6. /// <summary>
  7. /// Helper methods for UI-related tasks.
  8. /// </summary>
  9. public static class TreeHelper
  10. {
  11. /// <summary>
  12. /// Finds a Child of a given item in the visual tree.
  13. /// </summary>
  14. /// <param name="parent">A direct parent of the queried item.</param>
  15. /// <typeparam name="T">The type of the queried item.</typeparam>
  16. /// <param name="childName">x:Name or Name of child. </param>
  17. /// <returns>The first parent item that matches the submitted type parameter.
  18. /// If not matching item can be found,
  19. /// a null parent is being returned.</returns>
  20. public static T FindChild<T>(DependencyObject parent, string childName)
  21. where T : DependencyObject
  22. {
  23. // Confirm parent and childName are valid.
  24. if (parent == null) return null;
  25. T foundChild = null;
  26. int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
  27. for (int i = 0; i < childrenCount; i++)
  28. {
  29. var child = VisualTreeHelper.GetChild(parent, i);
  30. // If the child is not of the request child type child
  31. T childType = child as T;
  32. if (childType == null)
  33. {
  34. // recursively drill down the tree
  35. foundChild = FindChild<T>(child, childName);
  36. // If the child is found, break so we do not overwrite the found child.
  37. if (foundChild != null) break;
  38. }
  39. else if (!string.IsNullOrEmpty(childName))
  40. {
  41. var frameworkElement = child as FrameworkElement;
  42. // If the child's name is set for search
  43. if (frameworkElement != null && frameworkElement.Name == childName)
  44. {
  45. // if the child's name is of the request name
  46. foundChild = (T)child;
  47. break;
  48. }
  49. }
  50. else
  51. {
  52. // child element found.
  53. foundChild = (T)child;
  54. break;
  55. }
  56. }
  57. return foundChild;
  58. }
  59. #region find parent
  60. /// <summary>
  61. /// Finds a parent of a given item on the visual tree.
  62. /// </summary>
  63. /// <typeparam name="T">The type of the queried item.</typeparam>
  64. /// <param name="child">A direct or indirect child of the
  65. /// queried item.</param>
  66. /// <returns>The first parent item that matches the submitted
  67. /// type parameter. If not matching item can be found, a null
  68. /// reference is being returned.</returns>
  69. public static T TryFindParent<T>(this DependencyObject child)
  70. where T : DependencyObject
  71. {
  72. //get parent item
  73. DependencyObject parentObject = GetParentObject(child);
  74. //we've reached the end of the tree
  75. if (parentObject == null) return null;
  76. //check if the parent matches the type we're looking for
  77. T parent = parentObject as T;
  78. if (parent != null)
  79. {
  80. return parent;
  81. }
  82. //use recursion to proceed with next level
  83. return TryFindParent<T>(parentObject);
  84. }
  85. /// <summary>
  86. /// This method is an alternative to WPF's
  87. /// <see cref="VisualTreeHelper.GetParent"/> method, which also
  88. /// supports content elements. Keep in mind that for content element,
  89. /// this method falls back to the logical tree of the element!
  90. /// </summary>
  91. /// <param name="child">The item to be processed.</param>
  92. /// <returns>The submitted item's parent, if available. Otherwise
  93. /// null.</returns>
  94. public static DependencyObject GetParentObject(this DependencyObject child)
  95. {
  96. if (child == null) return null;
  97. //handle content elements separately
  98. ContentElement contentElement = child as ContentElement;
  99. if (contentElement != null)
  100. {
  101. DependencyObject parent = ContentOperations.GetParent(contentElement);
  102. if (parent != null) return parent;
  103. FrameworkContentElement fce = contentElement as FrameworkContentElement;
  104. return fce != null ? fce.Parent : null;
  105. }
  106. //also try searching for parent in framework elements (such as DockPanel, etc)
  107. FrameworkElement frameworkElement = child as FrameworkElement;
  108. if (frameworkElement != null)
  109. {
  110. DependencyObject parent = frameworkElement.Parent;
  111. if (parent != null) return parent;
  112. }
  113. //if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
  114. return VisualTreeHelper.GetParent(child);
  115. }
  116. #endregion
  117. #region find children
  118. /// <summary>
  119. /// Analyzes both visual and logical tree in order to find all elements of a given
  120. /// type that are descendants of the <paramref name="source"/> item.
  121. /// </summary>
  122. /// <typeparam name="T">The type of the queried items.</typeparam>
  123. /// <param name="source">The root element that marks the source of the search. If the
  124. /// source is already of the requested type, it will not be included in the result.</param>
  125. /// <returns>All descendants of <paramref name="source"/> that match the requested type.</returns>
  126. public static IEnumerable<T> FindChildren<T>(this DependencyObject source) where T : DependencyObject
  127. {
  128. if (source != null)
  129. {
  130. var childs = GetChildObjects(source);
  131. foreach (DependencyObject child in childs)
  132. {
  133. //analyze if children match the requested type
  134. if (child is T)
  135. {
  136. yield return (T)child;
  137. }
  138. //recurse tree
  139. foreach (T descendant in FindChildren<T>(child))
  140. {
  141. yield return descendant;
  142. }
  143. }
  144. }
  145. }
  146. /// <summary>
  147. /// This method is an alternative to WPF's
  148. /// <see cref="VisualTreeHelper.GetChild"/> method, which also
  149. /// supports content elements. Keep in mind that for content elements,
  150. /// this method falls back to the logical tree of the element.
  151. /// </summary>
  152. /// <param name="parent">The item to be processed.</param>
  153. /// <returns>The submitted item's child elements, if available.</returns>
  154. public static IEnumerable<DependencyObject> GetChildObjects(this DependencyObject parent)
  155. {
  156. if (parent == null) yield break;
  157. if (parent is ContentElement || parent is FrameworkElement)
  158. {
  159. //use the logical tree for content / framework elements
  160. foreach (object obj in LogicalTreeHelper.GetChildren(parent))
  161. {
  162. var depObj = obj as DependencyObject;
  163. if (depObj != null) yield return (DependencyObject)obj;
  164. }
  165. }
  166. else
  167. {
  168. //use the visual tree per default
  169. int count = VisualTreeHelper.GetChildrenCount(parent);
  170. for (int i = 0; i < count; i++)
  171. {
  172. yield return VisualTreeHelper.GetChild(parent, i);
  173. }
  174. }
  175. }
  176. #endregion
  177. #region find from point
  178. /// <summary>
  179. /// Tries to locate a given item within the visual tree,
  180. /// starting with the dependency object at a given position.
  181. /// </summary>
  182. /// <typeparam name="T">The type of the element to be found
  183. /// on the visual tree of the element at the given location.</typeparam>
  184. /// <param name="reference">The main element which is used to perform
  185. /// hit testing.</param>
  186. /// <param name="point">The position to be evaluated on the origin.</param>
  187. public static T TryFindFromPoint<T>(UIElement reference, Point point)
  188. where T : DependencyObject
  189. {
  190. DependencyObject element = reference.InputHitTest(point) as DependencyObject;
  191. if (element == null) return null;
  192. if (element is T) return (T)element;
  193. return TryFindParent<T>(element);
  194. }
  195. #endregion
  196. }
  197. }