//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing { using System; using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Windows; using System.Windows.Media; using System.Windows.Media.Media3D; using System.Collections.Generic; // // Collection of simple utilities that deal with the VisualTree // internal static class VisualTreeUtils { // The depth of the visual tree we explore in looking for templated children // (see GetTemplateChild<>()) private const int MaxSearchDepth = 5; // The maxium wpf visual tree depth // this value should be kept in [....] with WPF's limit private const int MaxAllowedTreeDepth = 250; // // Examines the visual children of the given element and returns the one // with the specified name and type, if one exists // // Type of child to look for // Container to look in // Name to look for // The specified named child if found, null otherwise public static T GetNamedChild(DependencyObject container, string name) where T : FrameworkElement { return GetNamedChild(container, name, 0); } // // Examines the visual children of the given element and returns the one // with the specified name and type, if one exists // // Type of child to look for // Container to look in // Name to look for // Visual depth to search in. Default is 0. // The specified named child if found, null otherwise public static T GetNamedChild(DependencyObject container, string name, int searchDepth) where T : FrameworkElement { if (container == null || string.IsNullOrEmpty(name) || searchDepth < 0) { return null; } if (container is T && string.Equals( name, ((T)container).Name)) { return (T)container; } int childCount = VisualTreeHelper.GetChildrenCount(container); if (childCount == 0) { return null; } // Look for the first child that matches for (int index = 0; index < childCount; index++) { FrameworkElement child = VisualTreeHelper.GetChild(container, index) as FrameworkElement; if (child == null) { continue; } // Search recursively until we reach the requested search depth T namedChild = (child.FindName(name) as T) ?? (searchDepth > 0 ? GetNamedChild(child, name, searchDepth - 1) : null); if (namedChild != null) { return namedChild; } } return null; } // // Helper method that goes down the first-child visual tree and looks for the visual element of the // specified type. Useful for when you have one control that is templated to look like another // and you need to get to the template instance itself. // // Type of the visual template to look for // Element to start from // The first matching instance in the visual tree of first children, null otherwise public static T GetTemplateChild(DependencyObject element) where T : DependencyObject { int availableSearchDepth = MaxSearchDepth; while (availableSearchDepth > 0 && element != null) { int childrenCount = VisualTreeHelper.GetChildrenCount(element); if (childrenCount < 1) { return null; } // Just worry about the first child, since we are looking for a template control, // not a complicated visual tree // element = VisualTreeHelper.GetChild(element, 0); T childAsT = element as T; if (childAsT != null) { return childAsT; } availableSearchDepth--; } return null; } // // Goes up the visual tree, looking for an ancestor of the specified Type // // Type to look for // Starting point // Visual ancestor, if any, of the specified starting point and Type [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static T FindVisualAncestor(DependencyObject child) where T : DependencyObject { if (child == null) { return null; } do { child = VisualTreeHelper.GetParent(child); } while (child != null && !typeof(T).IsAssignableFrom(child.GetType())); return child as T; } // // Recursively looks through all the children and looks for and returns the first // element with IsFocusable set to true. // // Starting point for the search // Type of child to look for // The first focusable child of the specified Type, null otherwise. public static T FindFocusableElement(T reference) where T : UIElement { if (reference == null || (reference.Focusable && reference.Visibility == Visibility.Visible)) { return reference; } int childCount = VisualTreeHelper.GetChildrenCount(reference); for (int i = 0; i < childCount; i++) { T child = VisualTreeHelper.GetChild(reference, i) as T; if (child == null) { continue; } if (child.Visibility != Visibility.Visible) { continue; } if (child.Focusable) { return child; } child = FindFocusableElement(child); if (child != null) { return child; } } return null; } // // Walks up the parent tree and returns the first // element with IsFocusable set to true. // // Starting point for the search // Type of parent to look for // The first focusable parent of the specified Type, null otherwise. public static T FindFocusableParent(UIElement reference) where T : UIElement { if (null != reference) { UIElement parent = VisualTreeHelper.GetParent(reference) as UIElement; while (null != parent) { if (parent.Visibility == Visibility.Visible && parent is T && parent.Focusable) { return parent as T; } parent = VisualTreeHelper.GetParent(parent) as UIElement; } } return null; } // // Helper method identical to VisualTreeHelper.GetParent() but that also returns the index of the given child // with respect to any of its visual siblings // // Child to examine // Total number of children that the specified child's parent has // Index of the specified child in the parent's visual children array // Visual parent of the specified child, if any public static DependencyObject GetIndexedVisualParent(DependencyObject child, out int childrenCount, out int childIndex) { childrenCount = 0; DependencyObject parent = VisualTreeHelper.GetParent(child); if (parent != null) { childrenCount = VisualTreeHelper.GetChildrenCount(parent); for (childIndex = 0; childIndex < childrenCount; childIndex++) { if (child.Equals(VisualTreeHelper.GetChild(parent, childIndex))) { return parent; } } } childIndex = -1; return null; } // // Helper method that goes up the visual tree checking the visibility flag of the specified element // and all its parents. If an invisible parent is found, the method return false. Otherwise it returns // true. // // Element to check // True if the specified element and all of its visual parents are visible, // false otherwise. public static bool IsVisible(UIElement element) { UIElement parent = element; while (parent != null) { if (parent.Visibility != Visibility.Visible) { return false; } parent = VisualTreeHelper.GetParent(parent) as UIElement; } return true; } /// /// Helper method that trasverse the visual tree checking for immediate parent of specified type that has children /// that is too deep for WPF visual tree /// /// The type of parent to look for /// The root element to start checking /// public static ICollection PrunVisualTree(Visual root) where T : DependencyObject { HashSet deepElements = new HashSet(); Stack stack = new Stack(); VisualState currentObject = new VisualState(root, GetTreeDepth(root)); stack.Push(currentObject); int totalChildCount = 0; Visual child; T violatingParent; //doing a depth first walk of the visual tree. while (stack.Count > 0) { currentObject = stack.Pop(); // currentObject.Depth + 1 is the children's depth // if it's too deep, we would try to find the parent and stop traversing // this node further if (currentObject.Depth + 1 >= MaxAllowedTreeDepth) { violatingParent = currentObject.Visual as T; if (violatingParent != null) { deepElements.Add(violatingParent); } else { violatingParent = FindVisualAncestor(currentObject.Visual); if (violatingParent != null) { deepElements.Add(violatingParent); } } } else // continue the depth first traversal { totalChildCount = VisualTreeHelper.GetChildrenCount(currentObject.Visual); for (int i = 0; i < totalChildCount; i++) { child = VisualTreeHelper.GetChild(currentObject.Visual, i) as Visual; if (child != null) { stack.Push(new VisualState(child, currentObject.Depth + 1)); } } } } return deepElements; } //find the depth of an DependencyObject public static uint GetTreeDepth(DependencyObject element) { uint depth = 0; while (element != null) { depth++; if (element is Visual || element is Visual3D) { element = VisualTreeHelper.GetParent(element); } else { element = LogicalTreeHelper.GetParent(element); } } return depth; } private class VisualState { internal Visual Visual { get; set; } internal uint Depth { get; set; } internal VisualState(Visual visual, uint depth) { this.Visual = visual; this.Depth = depth; } } } }