1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.PropertyEditing
7 using System.Collections;
8 using System.Diagnostics.CodeAnalysis;
10 using System.Windows.Media;
11 using System.Windows.Media.Media3D;
12 using System.Collections.Generic;
15 // Collection of simple utilities that deal with the VisualTree
17 internal static class VisualTreeUtils
20 // The depth of the visual tree we explore in looking for templated children
21 // (see GetTemplateChild<>())
22 private const int MaxSearchDepth = 5;
24 // The maxium wpf visual tree depth
25 // this value should be kept in [....] with WPF's limit
26 private const int MaxAllowedTreeDepth = 250;
29 // Examines the visual children of the given element and returns the one
30 // with the specified name and type, if one exists
32 // <typeparam name="T">Type of child to look for</typeparam>
33 // <param name="container">Container to look in</param>
34 // <param name="name">Name to look for</param>
35 // <returns>The specified named child if found, null otherwise</returns>
36 public static T GetNamedChild<T>(DependencyObject container, string name)
37 where T : FrameworkElement
39 return GetNamedChild<T>(container, name, 0);
43 // Examines the visual children of the given element and returns the one
44 // with the specified name and type, if one exists
46 // <typeparam name="T">Type of child to look for</typeparam>
47 // <param name="container">Container to look in</param>
48 // <param name="name">Name to look for</param>
49 // <param name="searchDepth">Visual depth to search in. Default is 0.</param>
50 // <returns>The specified named child if found, null otherwise</returns>
51 public static T GetNamedChild<T>(DependencyObject container, string name, int searchDepth)
52 where T : FrameworkElement
54 if (container == null || string.IsNullOrEmpty(name) || searchDepth < 0)
59 if (container is T && string.Equals( name, ((T)container).Name))
64 int childCount = VisualTreeHelper.GetChildrenCount(container);
70 // Look for the first child that matches
71 for (int index = 0; index < childCount; index++)
73 FrameworkElement child = VisualTreeHelper.GetChild(container, index) as FrameworkElement;
79 // Search recursively until we reach the requested search depth
81 (child.FindName(name) as T) ??
82 (searchDepth > 0 ? GetNamedChild<T>(child, name, searchDepth - 1) : null);
84 if (namedChild != null)
94 // Helper method that goes down the first-child visual tree and looks for the visual element of the
95 // specified type. Useful for when you have one control that is templated to look like another
96 // and you need to get to the template instance itself.
98 // <typeparam name="T">Type of the visual template to look for</typeparam>
99 // <param name="element">Element to start from</param>
100 // <returns>The first matching instance in the visual tree of first children, null otherwise</returns>
101 public static T GetTemplateChild<T>(DependencyObject element) where T : DependencyObject
103 int availableSearchDepth = MaxSearchDepth;
104 while (availableSearchDepth > 0 && element != null)
106 int childrenCount = VisualTreeHelper.GetChildrenCount(element);
107 if (childrenCount < 1)
112 // Just worry about the first child, since we are looking for a template control,
113 // not a complicated visual tree
115 element = VisualTreeHelper.GetChild(element, 0);
116 T childAsT = element as T;
117 if (childAsT != null)
122 availableSearchDepth--;
129 // Goes up the visual tree, looking for an ancestor of the specified Type
131 // <typeparam name="T">Type to look for</typeparam>
132 // <param name="child">Starting point</param>
133 // <returns>Visual ancestor, if any, of the specified starting point and Type</returns>
134 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
135 public static T FindVisualAncestor<T>(DependencyObject child) where T : DependencyObject
144 child = VisualTreeHelper.GetParent(child);
146 while (child != null && !typeof(T).IsAssignableFrom(child.GetType()));
152 // Recursively looks through all the children and looks for and returns the first
153 // element with IsFocusable set to true.
155 // <param name="reference">Starting point for the search</param>
156 // <typeparam name="T">Type of child to look for</typeparam>
157 // <returns>The first focusable child of the specified Type, null otherwise.</returns>
158 public static T FindFocusableElement<T>(T reference) where T : UIElement
160 if (reference == null || (reference.Focusable && reference.Visibility == Visibility.Visible))
165 int childCount = VisualTreeHelper.GetChildrenCount(reference);
166 for (int i = 0; i < childCount; i++)
168 T child = VisualTreeHelper.GetChild(reference, i) as T;
174 if (child.Visibility != Visibility.Visible)
184 child = FindFocusableElement<T>(child);
195 // Walks up the parent tree and returns the first
196 // element with IsFocusable set to true.
198 // <param name="reference">Starting point for the search</param>
199 // <typeparam name="T">Type of parent to look for</typeparam>
200 // <returns>The first focusable parent of the specified Type, null otherwise.</returns>
201 public static T FindFocusableParent<T>(UIElement reference) where T : UIElement
203 if (null != reference)
205 UIElement parent = VisualTreeHelper.GetParent(reference) as UIElement;
206 while (null != parent)
208 if (parent.Visibility == Visibility.Visible && parent is T && parent.Focusable)
213 parent = VisualTreeHelper.GetParent(parent) as UIElement;
220 // Helper method identical to VisualTreeHelper.GetParent() but that also returns the index of the given child
221 // with respect to any of its visual siblings
223 // <param name="child">Child to examine</param>
224 // <param name="childrenCount">Total number of children that the specified child's parent has</param>
225 // <param name="childIndex">Index of the specified child in the parent's visual children array</param>
226 // <returns>Visual parent of the specified child, if any</returns>
227 public static DependencyObject GetIndexedVisualParent(DependencyObject child, out int childrenCount, out int childIndex)
230 DependencyObject parent = VisualTreeHelper.GetParent(child);
234 childrenCount = VisualTreeHelper.GetChildrenCount(parent);
235 for (childIndex = 0; childIndex < childrenCount; childIndex++)
237 if (child.Equals(VisualTreeHelper.GetChild(parent, childIndex)))
249 // Helper method that goes up the visual tree checking the visibility flag of the specified element
250 // and all its parents. If an invisible parent is found, the method return false. Otherwise it returns
253 // <param name="element">Element to check</param>
254 // <returns>True if the specified element and all of its visual parents are visible,
255 // false otherwise.</returns>
256 public static bool IsVisible(UIElement element)
258 UIElement parent = element;
259 while (parent != null)
261 if (parent.Visibility != Visibility.Visible)
266 parent = VisualTreeHelper.GetParent(parent) as UIElement;
273 /// Helper method that trasverse the visual tree checking for immediate parent of specified type that has children
274 /// that is too deep for WPF visual tree
276 /// <typeparam name="T">The type of parent to look for</typeparam>
277 /// <param name="root">The root element to start checking</param>
278 /// <returns></returns>
279 public static ICollection<T> PrunVisualTree<T>(Visual root) where T : DependencyObject
281 HashSet<T> deepElements = new HashSet<T>();
282 Stack<VisualState> stack = new Stack<VisualState>();
283 VisualState currentObject = new VisualState(root, GetTreeDepth(root));
284 stack.Push(currentObject);
285 int totalChildCount = 0;
288 //doing a depth first walk of the visual tree.
289 while (stack.Count > 0)
291 currentObject = stack.Pop();
293 // currentObject.Depth + 1 is the children's depth
294 // if it's too deep, we would try to find the parent and stop traversing
296 if (currentObject.Depth + 1 >= MaxAllowedTreeDepth)
298 violatingParent = currentObject.Visual as T;
299 if (violatingParent != null)
301 deepElements.Add(violatingParent);
305 violatingParent = FindVisualAncestor<T>(currentObject.Visual);
306 if (violatingParent != null)
308 deepElements.Add(violatingParent);
312 else // continue the depth first traversal
314 totalChildCount = VisualTreeHelper.GetChildrenCount(currentObject.Visual);
315 for (int i = 0; i < totalChildCount; i++)
317 child = VisualTreeHelper.GetChild(currentObject.Visual, i) as Visual;
320 stack.Push(new VisualState(child, currentObject.Depth + 1));
328 //find the depth of an DependencyObject
329 public static uint GetTreeDepth(DependencyObject element)
332 while (element != null)
335 if (element is Visual || element is Visual3D)
337 element = VisualTreeHelper.GetParent(element);
341 element = LogicalTreeHelper.GetParent(element);
347 private class VisualState
349 internal Visual Visual { get; set; }
350 internal uint Depth { get; set; }
352 internal VisualState(Visual visual, uint depth)
354 this.Visual = visual;