[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / Internal / PropertyEditing / VisualTreeUtils.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.PropertyEditing 
5 {
6     using System;
7     using System.Collections;
8     using System.Diagnostics.CodeAnalysis;
9     using System.Windows;
10     using System.Windows.Media;
11     using System.Windows.Media.Media3D;
12     using System.Collections.Generic;
13
14     // <summary>
15     // Collection of simple utilities that deal with the VisualTree
16     // </summary>
17     internal static class VisualTreeUtils 
18     {
19
20         // The depth of the visual tree we explore in looking for templated children
21         // (see GetTemplateChild<>())
22         private const int MaxSearchDepth = 5;
23
24         // The maxium wpf visual tree depth
25         // this value should be kept in [....] with WPF's limit
26         private const int MaxAllowedTreeDepth = 250;
27
28         // <summary>
29         // Examines the visual children of the given element and returns the one
30         // with the specified name and type, if one exists
31         // </summary>
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 
38         {
39             return GetNamedChild<T>(container, name, 0);
40         }
41
42         // <summary>
43         // Examines the visual children of the given element and returns the one
44         // with the specified name and type, if one exists
45         // </summary>
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 
53         {
54             if (container == null || string.IsNullOrEmpty(name) || searchDepth < 0)
55             {
56                 return null;
57             }
58
59             if (container is T && string.Equals( name, ((T)container).Name))
60             {
61                 return (T)container;
62             }
63
64             int childCount = VisualTreeHelper.GetChildrenCount(container);
65             if (childCount == 0)
66             {
67                 return null;
68             }
69
70             // Look for the first child that matches
71             for (int index = 0; index < childCount; index++) 
72             {
73                 FrameworkElement child = VisualTreeHelper.GetChild(container, index) as FrameworkElement;
74                 if (child == null)
75                 {
76                     continue;
77                 }
78
79                 // Search recursively until we reach the requested search depth
80                 T namedChild =
81                     (child.FindName(name) as T) ??
82                     (searchDepth > 0 ? GetNamedChild<T>(child, name, searchDepth - 1) : null);
83
84                 if (namedChild != null)
85                 {
86                     return namedChild;
87                 }
88             }
89
90             return null;
91         }
92
93         // <summary>
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.
97         // </summary>
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 
102         {
103             int availableSearchDepth = MaxSearchDepth;
104             while (availableSearchDepth > 0 && element != null) 
105             {
106                 int childrenCount = VisualTreeHelper.GetChildrenCount(element);
107                 if (childrenCount < 1)
108                 {
109                     return null;
110                 }
111
112                 // Just worry about the first child, since we are looking for a template control,
113                 // not a complicated visual tree
114                 //
115                 element = VisualTreeHelper.GetChild(element, 0);
116                 T childAsT = element as T;
117                 if (childAsT != null)
118                 {
119                     return childAsT;
120                 }
121
122                 availableSearchDepth--;
123             }
124
125             return null;
126         }
127
128         // <summary>
129         // Goes up the visual tree, looking for an ancestor of the specified Type
130         // </summary>
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 
136         {
137             if (child == null)
138             {
139                 return null;
140             }
141
142             do 
143             {
144                 child = VisualTreeHelper.GetParent(child);
145             }
146             while (child != null && !typeof(T).IsAssignableFrom(child.GetType()));
147
148             return child as T;
149         }
150
151         // <summary>
152         // Recursively looks through all the children and looks for and returns the first
153         // element with IsFocusable set to true.
154         // </summary>
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 
159         {
160             if (reference == null || (reference.Focusable && reference.Visibility == Visibility.Visible))
161             {
162                 return reference;
163             }
164
165             int childCount = VisualTreeHelper.GetChildrenCount(reference);
166             for (int i = 0; i < childCount; i++) 
167             {
168                 T child = VisualTreeHelper.GetChild(reference, i) as T;
169                 if (child == null)
170                 {
171                     continue;
172                 }
173
174                 if (child.Visibility != Visibility.Visible)
175                 {
176                     continue;
177                 }
178
179                 if (child.Focusable)
180                 {
181                     return child;
182                 }
183
184                 child = FindFocusableElement<T>(child);
185                 if (child != null)
186                 {
187                     return child;
188                 }
189             }
190
191             return null;
192         }
193
194         // <summary>
195         // Walks up the parent tree and returns the first
196         // element with IsFocusable set to true.
197         // </summary>
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
202         {
203             if (null != reference)
204             {
205                 UIElement parent = VisualTreeHelper.GetParent(reference) as UIElement;
206                 while (null != parent)
207                 {
208                     if (parent.Visibility == Visibility.Visible && parent is T && parent.Focusable)
209                     {
210                         return parent as T;
211                     }
212
213                     parent = VisualTreeHelper.GetParent(parent) as UIElement;
214                 }
215             }
216             return null;
217         }
218
219         // <summary>
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
222         // </summary>
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) 
228         {
229             childrenCount = 0;
230             DependencyObject parent = VisualTreeHelper.GetParent(child);
231
232             if (parent != null) 
233             {
234                 childrenCount = VisualTreeHelper.GetChildrenCount(parent);
235                 for (childIndex = 0; childIndex < childrenCount; childIndex++) 
236                 {
237                     if (child.Equals(VisualTreeHelper.GetChild(parent, childIndex)))
238                     {
239                         return parent;
240                     }
241                 }
242             }
243
244             childIndex = -1;
245             return null;
246         }
247
248         // <summary>
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
251         // true.
252         // </summary>
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) 
257         {
258             UIElement parent = element;
259             while (parent != null) 
260             {
261                 if (parent.Visibility != Visibility.Visible)
262                 {
263                     return false;
264                 }
265
266                 parent = VisualTreeHelper.GetParent(parent) as UIElement;
267             }
268
269             return true;
270         }
271
272         /// <summary>
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
275         /// </summary>
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
280         {
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;
286             Visual child;
287             T violatingParent;
288             //doing a depth first walk of the visual tree.
289             while (stack.Count > 0)
290             {
291                 currentObject = stack.Pop();
292
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 
295                 // this node further
296                 if (currentObject.Depth + 1 >= MaxAllowedTreeDepth)
297                 {
298                     violatingParent = currentObject.Visual as T;
299                     if (violatingParent != null)
300                     {
301                         deepElements.Add(violatingParent);
302                     }
303                     else
304                     {
305                         violatingParent = FindVisualAncestor<T>(currentObject.Visual);
306                         if (violatingParent != null)
307                         {
308                             deepElements.Add(violatingParent);
309                         }
310                     }
311                 }
312                 else // continue the depth first traversal
313                 {
314                     totalChildCount = VisualTreeHelper.GetChildrenCount(currentObject.Visual);
315                     for (int i = 0; i < totalChildCount; i++)
316                     {
317                         child = VisualTreeHelper.GetChild(currentObject.Visual, i) as Visual;
318                         if (child != null)
319                         {
320                             stack.Push(new VisualState(child, currentObject.Depth + 1));
321                         }
322                     }
323                 }
324             }
325            return deepElements;
326         }
327
328         //find the depth of an DependencyObject
329         public static uint GetTreeDepth(DependencyObject element)
330         {
331             uint depth = 0;
332             while (element != null)
333             {
334                 depth++;
335                 if (element is Visual || element is Visual3D)
336                 {
337                     element = VisualTreeHelper.GetParent(element);
338                 }
339                 else
340                 {
341                     element = LogicalTreeHelper.GetParent(element);
342                 }
343             }
344             return depth;
345         }
346
347         private class VisualState
348         {
349             internal Visual Visual { get; set; }
350             internal uint Depth { get; set; }
351
352             internal VisualState(Visual visual, uint depth)
353             {
354                 this.Visual = visual;
355                 this.Depth = depth;
356             }
357         }
358     }
359 }