TreeHelper.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Windows;
  5. using System.Windows.Media;
  6. namespace MediaBrowser.UI.Controls
  7. {
  8. /// <summary>
  9. /// Helper methods for UI-related tasks.
  10. /// </summary>
  11. public static class TreeHelper
  12. {
  13. /// <summary>
  14. /// Gets the window.
  15. /// </summary>
  16. /// <param name="element">The element.</param>
  17. /// <returns>Window.</returns>
  18. /// <value>The window.</value>
  19. public static Window GetWindow(this FrameworkElement element)
  20. {
  21. return element.ParentOfType<Window>();
  22. }
  23. /// <summary>
  24. /// Gets the parent.
  25. /// </summary>
  26. /// <param name="element">The element.</param>
  27. /// <returns>DependencyObject.</returns>
  28. private static DependencyObject GetParent(this DependencyObject element)
  29. {
  30. DependencyObject parent = VisualTreeHelper.GetParent(element);
  31. if (parent == null)
  32. {
  33. FrameworkElement frameworkElement = element as FrameworkElement;
  34. if (frameworkElement != null)
  35. {
  36. parent = frameworkElement.Parent;
  37. }
  38. }
  39. return parent;
  40. }
  41. /// <summary>
  42. /// Gets the parents.
  43. /// </summary>
  44. /// <param name="element">The element.</param>
  45. /// <returns>IEnumerable{DependencyObject}.</returns>
  46. /// <exception cref="System.ArgumentNullException">element</exception>
  47. public static IEnumerable<DependencyObject> GetParents(this DependencyObject element)
  48. {
  49. if (element == null)
  50. {
  51. throw new ArgumentNullException("element");
  52. }
  53. while ((element = element.GetParent()) != null)
  54. {
  55. yield return element;
  56. }
  57. yield break;
  58. }
  59. /// <summary>
  60. /// Parents the type of the of.
  61. /// </summary>
  62. /// <typeparam name="T"></typeparam>
  63. /// <param name="element">The element.</param>
  64. /// <returns>``0.</returns>
  65. public static T ParentOfType<T>(this DependencyObject element) where T : DependencyObject
  66. {
  67. if (element == null)
  68. {
  69. return default(T);
  70. }
  71. return element.GetParents().OfType<T>().FirstOrDefault<T>();
  72. }
  73. /// <summary>
  74. /// Finds a Child of a given item in the visual tree.
  75. /// </summary>
  76. /// <typeparam name="T">The type of the queried item.</typeparam>
  77. /// <param name="parent">A direct parent of the queried item.</param>
  78. /// <param name="childName">x:Name or Name of child.</param>
  79. /// <returns>The first parent item that matches the submitted type parameter.
  80. /// If not matching item can be found,
  81. /// a null parent is being returned.</returns>
  82. public static T FindChild<T>(DependencyObject parent, string childName)
  83. where T : DependencyObject
  84. {
  85. // Confirm parent and childName are valid.
  86. if (parent == null) return null;
  87. T foundChild = null;
  88. int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
  89. for (int i = 0; i < childrenCount; i++)
  90. {
  91. var child = VisualTreeHelper.GetChild(parent, i);
  92. // If the child is not of the request child type child
  93. T childType = child as T;
  94. if (childType == null)
  95. {
  96. // recursively drill down the tree
  97. foundChild = FindChild<T>(child, childName);
  98. // If the child is found, break so we do not overwrite the found child.
  99. if (foundChild != null) break;
  100. }
  101. else if (!string.IsNullOrEmpty(childName))
  102. {
  103. var frameworkElement = child as FrameworkElement;
  104. // If the child's name is set for search
  105. if (frameworkElement != null && frameworkElement.Name == childName)
  106. {
  107. // if the child's name is of the request name
  108. foundChild = (T)child;
  109. break;
  110. }
  111. }
  112. else
  113. {
  114. // child element found.
  115. foundChild = (T)child;
  116. break;
  117. }
  118. }
  119. return foundChild;
  120. }
  121. /// <summary>
  122. /// Gets the visual child.
  123. /// </summary>
  124. /// <typeparam name="T"></typeparam>
  125. /// <param name="referenceVisual">The reference visual.</param>
  126. /// <returns>``0.</returns>
  127. public static T GetVisualChild<T>(this Visual referenceVisual) where T : Visual
  128. {
  129. Visual child = null;
  130. for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceVisual); i++)
  131. {
  132. child = VisualTreeHelper.GetChild(referenceVisual, i) as Visual;
  133. if (child != null && (child.GetType() == typeof(T)))
  134. {
  135. break;
  136. }
  137. else if (child != null)
  138. {
  139. child = GetVisualChild<T>(child);
  140. if (child != null && (child.GetType() == typeof(T)))
  141. {
  142. break;
  143. }
  144. }
  145. }
  146. return child as T;
  147. }
  148. #region find parent
  149. /// <summary>
  150. /// Finds a parent of a given item on the visual tree.
  151. /// </summary>
  152. /// <typeparam name="T">The type of the queried item.</typeparam>
  153. /// <param name="child">A direct or indirect child of the
  154. /// queried item.</param>
  155. /// <returns>The first parent item that matches the submitted
  156. /// type parameter. If not matching item can be found, a null
  157. /// reference is being returned.</returns>
  158. public static T TryFindParent<T>(this DependencyObject child)
  159. where T : DependencyObject
  160. {
  161. //get parent item
  162. DependencyObject parentObject = GetParentObject(child);
  163. //we've reached the end of the tree
  164. if (parentObject == null) return null;
  165. //check if the parent matches the type we're looking for
  166. T parent = parentObject as T;
  167. if (parent != null)
  168. {
  169. return parent;
  170. }
  171. //use recursion to proceed with next level
  172. return TryFindParent<T>(parentObject);
  173. }
  174. /// <summary>
  175. /// This method is an alternative to WPF's
  176. /// <see cref="VisualTreeHelper.GetParent" /> method, which also
  177. /// supports content elements. Keep in mind that for content element,
  178. /// this method falls back to the logical tree of the element!
  179. /// </summary>
  180. /// <param name="child">The item to be processed.</param>
  181. /// <returns>The submitted item's parent, if available. Otherwise
  182. /// null.</returns>
  183. public static DependencyObject GetParentObject(this DependencyObject child)
  184. {
  185. if (child == null) return null;
  186. //handle content elements separately
  187. ContentElement contentElement = child as ContentElement;
  188. if (contentElement != null)
  189. {
  190. DependencyObject parent = ContentOperations.GetParent(contentElement);
  191. if (parent != null) return parent;
  192. FrameworkContentElement fce = contentElement as FrameworkContentElement;
  193. return fce != null ? fce.Parent : null;
  194. }
  195. //also try searching for parent in framework elements (such as DockPanel, etc)
  196. FrameworkElement frameworkElement = child as FrameworkElement;
  197. if (frameworkElement != null)
  198. {
  199. DependencyObject parent = frameworkElement.Parent;
  200. if (parent != null) return parent;
  201. }
  202. //if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
  203. return VisualTreeHelper.GetParent(child);
  204. }
  205. #endregion
  206. #region find children
  207. /// <summary>
  208. /// Analyzes both visual and logical tree in order to find all elements of a given
  209. /// type that are descendants of the <paramref name="source" /> item.
  210. /// </summary>
  211. /// <typeparam name="T">The type of the queried items.</typeparam>
  212. /// <param name="source">The root element that marks the source of the search. If the
  213. /// source is already of the requested type, it will not be included in the result.</param>
  214. /// <returns>All descendants of <paramref name="source" /> that match the requested type.</returns>
  215. public static IEnumerable<T> FindChildren<T>(this DependencyObject source) where T : DependencyObject
  216. {
  217. if (source != null)
  218. {
  219. var childs = GetChildObjects(source);
  220. foreach (DependencyObject child in childs)
  221. {
  222. //analyze if children match the requested type
  223. if (child is T)
  224. {
  225. yield return (T)child;
  226. }
  227. //recurse tree
  228. foreach (T descendant in FindChildren<T>(child))
  229. {
  230. yield return descendant;
  231. }
  232. }
  233. }
  234. }
  235. /// <summary>
  236. /// This method is an alternative to WPF's
  237. /// <see cref="VisualTreeHelper.GetChild" /> method, which also
  238. /// supports content elements. Keep in mind that for content elements,
  239. /// this method falls back to the logical tree of the element.
  240. /// </summary>
  241. /// <param name="parent">The item to be processed.</param>
  242. /// <returns>The submitted item's child elements, if available.</returns>
  243. public static IEnumerable<DependencyObject> GetChildObjects(this DependencyObject parent)
  244. {
  245. if (parent == null) yield break;
  246. if (parent is ContentElement || parent is FrameworkElement)
  247. {
  248. //use the logical tree for content / framework elements
  249. foreach (object obj in LogicalTreeHelper.GetChildren(parent))
  250. {
  251. var depObj = obj as DependencyObject;
  252. if (depObj != null) yield return (DependencyObject)obj;
  253. }
  254. }
  255. else
  256. {
  257. //use the visual tree per default
  258. int count = VisualTreeHelper.GetChildrenCount(parent);
  259. for (int i = 0; i < count; i++)
  260. {
  261. yield return VisualTreeHelper.GetChild(parent, i);
  262. }
  263. }
  264. }
  265. #endregion
  266. #region find from point
  267. /// <summary>
  268. /// Tries to locate a given item within the visual tree,
  269. /// starting with the dependency object at a given position.
  270. /// </summary>
  271. /// <typeparam name="T">The type of the element to be found
  272. /// on the visual tree of the element at the given location.</typeparam>
  273. /// <param name="reference">The main element which is used to perform
  274. /// hit testing.</param>
  275. /// <param name="point">The position to be evaluated on the origin.</param>
  276. /// <returns>``0.</returns>
  277. public static T TryFindFromPoint<T>(UIElement reference, Point point)
  278. where T : DependencyObject
  279. {
  280. DependencyObject element = reference.InputHitTest(point) as DependencyObject;
  281. if (element == null) return null;
  282. if (element is T) return (T)element;
  283. return TryFindParent<T>(element);
  284. }
  285. #endregion
  286. }
  287. }