How can I find WPF controls by name or type?

后端 未结 18 2939
庸人自扰
庸人自扰 2020-11-21 04:23

I need to search a WPF control hierarchy for controls that match a given name or type. How can I do this?

相关标签:
18条回答
  • 2020-11-21 05:00

    I have a sequence function like this (which is completely general):

        public static IEnumerable<T> SelectAllRecursively<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> func)
        {
            return (items ?? Enumerable.Empty<T>()).SelectMany(o => new[] { o }.Concat(SelectAllRecursively(func(o), func)));
        }
    

    Getting immediate children:

        public static IEnumerable<DependencyObject> FindChildren(this DependencyObject obj)
        {
            return Enumerable.Range(0, VisualTreeHelper.GetChildrenCount(obj))
                .Select(i => VisualTreeHelper.GetChild(obj, i));
        }
    

    Finding all children down the hiararchical tree:

        public static IEnumerable<DependencyObject> FindAllChildren(this DependencyObject obj)
        {
            return obj.FindChildren().SelectAllRecursively(o => o.FindChildren());
        }
    

    You can call this on the Window to get all controls.

    After you have the collection, you can use LINQ (i.e. OfType, Where).

    0 讨论(0)
  • 2020-11-21 05:05

    This will dismiss some elements - you should extend it like this in order to support a wider array of controls. For a brief discussion, have a look here

     /// <summary>
     /// Helper methods for UI-related tasks.
     /// </summary>
     public static class UIHelper
     {
       /// <summary>
       /// Finds a parent of a given item on the visual tree.
       /// </summary>
       /// <typeparam name="T">The type of the queried item.</typeparam>
       /// <param name="child">A direct or indirect child of the
       /// queried item.</param>
       /// <returns>The first parent item that matches the submitted
       /// type parameter. If not matching item can be found, a null
       /// reference is being returned.</returns>
       public static T TryFindParent<T>(DependencyObject child)
         where T : DependencyObject
       {
         //get parent item
         DependencyObject parentObject = GetParentObject(child);
    
         //we've reached the end of the tree
         if (parentObject == null) return null;
    
         //check if the parent matches the type we're looking for
         T parent = parentObject as T;
         if (parent != null)
         {
           return parent;
         }
         else
         {
           //use recursion to proceed with next level
           return TryFindParent<T>(parentObject);
         }
       }
    
       /// <summary>
       /// This method is an alternative to WPF's
       /// <see cref="VisualTreeHelper.GetParent"/> method, which also
       /// supports content elements. Do note, that for content element,
       /// this method falls back to the logical tree of the element!
       /// </summary>
       /// <param name="child">The item to be processed.</param>
       /// <returns>The submitted item's parent, if available. Otherwise
       /// null.</returns>
       public static DependencyObject GetParentObject(DependencyObject child)
       {
         if (child == null) return null;
         ContentElement contentElement = child as ContentElement;
    
         if (contentElement != null)
         {
           DependencyObject parent = ContentOperations.GetParent(contentElement);
           if (parent != null) return parent;
    
           FrameworkContentElement fce = contentElement as FrameworkContentElement;
           return fce != null ? fce.Parent : null;
         }
    
         //if it's not a ContentElement, rely on VisualTreeHelper
         return VisualTreeHelper.GetParent(child);
       }
    }
    
    0 讨论(0)
  • 2020-11-21 05:06

    I combined the template format used by John Myczek and Tri Q's algorithm above to create a findChild Algorithm that can be used on any parent. Keep in mind that recursively searching a tree downwards could be a lengthy process. I've only spot-checked this on a WPF application, please comment on any errors you might find and I'll correct my code.

    WPF Snoop is a useful tool in looking at the visual tree - I'd strongly recommend using it while testing or using this algorithm to check your work.

    There is a small error in Tri Q's Algorithm. After the child is found, if childrenCount is > 1 and we iterate again we can overwrite the properly found child. Therefore I added a if (foundChild != null) break; into my code to deal with this condition.

    /// <summary>
    /// Finds a Child of a given item in the visual tree. 
    /// </summary>
    /// <param name="parent">A direct parent of the queried item.</param>
    /// <typeparam name="T">The type of the queried item.</typeparam>
    /// <param name="childName">x:Name or Name of child. </param>
    /// <returns>The first parent item that matches the submitted type parameter. 
    /// If not matching item can be found, 
    /// a null parent is being returned.</returns>
    public static T FindChild<T>(DependencyObject parent, string childName)
       where T : DependencyObject
    {    
      // Confirm parent and childName are valid. 
      if (parent == null) return null;
    
      T foundChild = null;
    
      int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
      for (int i = 0; i < childrenCount; i++)
      {
        var child = VisualTreeHelper.GetChild(parent, i);
        // If the child is not of the request child type child
        T childType = child as T;
        if (childType == null)
        {
          // recursively drill down the tree
          foundChild = FindChild<T>(child, childName);
    
          // If the child is found, break so we do not overwrite the found child. 
          if (foundChild != null) break;
        }
        else if (!string.IsNullOrEmpty(childName))
        {
          var frameworkElement = child as FrameworkElement;
          // If the child's name is set for search
          if (frameworkElement != null && frameworkElement.Name == childName)
          {
            // if the child's name is of the request name
            foundChild = (T)child;
            break;
          }
        }
        else
        {
          // child element found.
          foundChild = (T)child;
          break;
        }
      }
    
      return foundChild;
    }
    

    Call it like this:

    TextBox foundTextBox = 
       UIHelper.FindChild<TextBox>(Application.Current.MainWindow, "myTextBoxName");
    

    Note Application.Current.MainWindow can be any parent window.

    0 讨论(0)
  • 2020-11-21 05:07

    You can use the VisualTreeHelper to find controls. Below is a method that uses the VisualTreeHelper to find a parent control of a specified type. You can use the VisualTreeHelper to find controls in other ways as well.

    public static class UIHelper
    {
       /// <summary>
       /// Finds a parent of a given item on the visual tree.
       /// </summary>
       /// <typeparam name="T">The type of the queried item.</typeparam>
       /// <param name="child">A direct or indirect child of the queried item.</param>
       /// <returns>The first parent item that matches the submitted type parameter. 
       /// If not matching item can be found, a null reference is being returned.</returns>
       public static T FindVisualParent<T>(DependencyObject child)
         where T : DependencyObject
       {
          // get parent item
          DependencyObject parentObject = VisualTreeHelper.GetParent(child);
    
          // we’ve reached the end of the tree
          if (parentObject == null) return null;
    
          // check if the parent matches the type we’re looking for
          T parent = parentObject as T;
          if (parent != null)
          {
             return parent;
          }
          else
          {
             // use recursion to proceed with next level
             return FindVisualParent<T>(parentObject);
          }
       }
    }
    

    Call it like this:

    Window owner = UIHelper.FindVisualParent<Window>(myControl);
    
    0 讨论(0)
  • 2020-11-21 05:07

    I may be just repeating everyone else but I do have a pretty piece of code that extends the DependencyObject class with a method FindChild() that will get you the child by type and name. Just include and use.

    public static class UIChildFinder
    {
        public static DependencyObject FindChild(this DependencyObject reference, string childName, Type childType)
        {
            DependencyObject foundChild = null;
            if (reference != null)
            {
                int childrenCount = VisualTreeHelper.GetChildrenCount(reference);
                for (int i = 0; i < childrenCount; i++)
                {
                    var child = VisualTreeHelper.GetChild(reference, i);
                    // If the child is not of the request child type child
                    if (child.GetType() != childType)
                    {
                        // recursively drill down the tree
                        foundChild = FindChild(child, childName, childType);
                    }
                    else if (!string.IsNullOrEmpty(childName))
                    {
                        var frameworkElement = child as FrameworkElement;
                        // If the child's name is set for search
                        if (frameworkElement != null && frameworkElement.Name == childName)
                        {
                            // if the child's name is of the request name
                            foundChild = child;
                            break;
                        }
                    }
                    else
                    {
                        // child element found.
                        foundChild = child;
                        break;
                    }
                }
            }
            return foundChild;
        }
    }
    

    Hope you find it useful.

    0 讨论(0)
  • 2020-11-21 05:08

    Here is a solution that uses a flexible predicate:

    public static DependencyObject FindChild(DependencyObject parent, Func<DependencyObject, bool> predicate)
    {
        if (parent == null) return null;
    
        int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < childrenCount; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
    
            if (predicate(child))
            {
                return child;
            }
            else
            {
                var foundChild = FindChild(child, predicate);
                if (foundChild != null)
                    return foundChild;
            }
        }
    
        return null;
    }
    

    You can for example call it like this:

    var child = FindChild(parent, child =>
    {
        var textBlock = child as TextBlock;
        if (textBlock != null && textBlock.Name == "MyTextBlock")
            return true;
        else
            return false;
    }) as TextBlock;
    
    0 讨论(0)
提交回复
热议问题