首页 文章

如何按名称或类型查找WPF控件?

提问于
浏览
236

我需要在WPF控件层次结构中搜索与给定名称或类型匹配的控件 . 我怎样才能做到这一点?

18 回答

  • 7

    我结合了John Myczek使用的模板格式和上面的Tri Q算法来创建一个可以在任何父级上使用的findChild算法 . 请记住,向下递归搜索树可能是一个漫长的过程 . 我只是在WPF应用程序上对此进行了检查,请对您可能发现的任何错误发表评论,我将更正我的代码 .

    WPF Snoop是查看可视化树的有用工具 - 我强烈建议在测试或使用此算法检查您的工作时使用它 .

    There is a small error in Tri Q's Algorithm. 找到孩子后,如果childrenCount> 1并且我们再次迭代,我们可以覆盖正确找到的孩子 . 因此我在我的代码中添加了 if (foundChild != null) break; 来处理这种情况 .

    /// <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;
    }
    

    这样叫:

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

    注意 Application.Current.MainWindow 可以是任何父窗口 .

  • 62

    您还可以使用FrameworkElement.FindName(string)按名称查找元素 .

    鉴于:

    <UserControl ...>
        <TextBlock x:Name="myTextBlock" />
    </UserControl>
    

    在代码隐藏文件中,您可以编写:

    var myTextBlock = (TextBlock)this.FindName("myTextBlock");
    

    当然,因为它是使用x:Name定义的,所以你可以只引用生成的字段,但也许你想要动态地而不是静态地查找它 .

    此方法也适用于模板,其中命名项目多次出现(每次使用模板一次) .

  • 19

    您可以使用VisualTreeHelper查找控件 . 下面是一个使用VisualTreeHelper查找指定类型的父控件的方法 . 您也可以使用VisualTreeHelper以其他方式查找控件 .

    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);
          }
       }
    }
    

    这样叫:

    Window owner = UIHelper.FindVisualParent<Window>(myControl);
    
  • 1

    我可能只是重复其他所有人,但我确实有一段很好的代码,它使用FindChild()方法扩展DependencyObject类,它将按类型和名称为您提供子类 . 只是包括和使用 .

    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;
        }
    }
    

    希望你觉得它有用 .

  • 113

    我对代码的扩展 .

    • 添加重载以按类型,按类型和条件(谓词)查找一个子项,查找符合条件的所有类型的子项

    • FindChildren方法除了是DependencyObject的扩展方法外,还是一个迭代器

    • FindChildren也走逻辑子树 . 请参阅博客文章中链接的Josh Smith的帖子 .

    资料来源:https://code.google.com/p/gishu-util/source/browse/#git%2FWPF%2FUtilities

    解释性博文:http://madcoderspeak.blogspot.com/2010/04/wpf-find-child-control-of-specific-type.html

  • 0

    如果要查找特定类型的所有控件,您可能也对此代码段感兴趣

    public static IEnumerable<T> FindVisualChildren<T>(DependencyObject parent) 
            where T : DependencyObject
        {
            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
    
                var childType = child as T;
                if (childType != null)
                {
                    yield return (T)child;
                }
    
                foreach (var other in FindVisualChildren<T>(child))
                {
                    yield return other;
                }
            }
        }
    
  • -4

    我编辑了CrimsonX的代码,因为它不适用于超类类型:

    public static T FindChild<T>(DependencyObject depObj, string childName)
       where T : DependencyObject
    {
        // Confirm obj is valid. 
        if (depObj == null) return null;
    
        // success case
        if (depObj is T && ((FrameworkElement)depObj).Name == childName)
            return depObj as T;
    
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
    
            //DFS
            T obj = FindChild<T>(child, childName);
    
            if (obj != null)
                return obj;
        }
    
        return null;
    }
    
  • 279

    这将忽略一些元素 - 您应该像这样扩展它以支持更广泛的控件 . 如需简短的讨论,请看看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);
       }
    }
    
  • 2

    虽然我喜欢一般的递归,但它在用C#编程时不如迭代效率高,所以下面的解决方案可能比John Myczek建议的更简洁?这将从给定控件中搜索层次结构以查找特定类型的祖先控件 .

    public static T FindVisualAncestorOfType<T>(this DependencyObject Elt)
        where T : DependencyObject
    {
        for (DependencyObject parent = VisualTreeHelper.GetParent(Elt);
            parent != null; parent = VisualTreeHelper.GetParent(parent))
        {
            T result = parent as T;
            if (result != null)
                return result;
        }
        return null;
    }
    

    像这样调用它来查找包含名为 ExampleTextBox 的控件的 Window

    Window window = ExampleTextBox.FindVisualAncestorOfType<Window>();
    
  • 14

    这是我的代码,通过类型查找控件,同时控制我们进入层次结构的深度(maxDepth == 0表示无限深) .

    public static class FrameworkElementExtension
    {
        public static object[] FindControls(
            this FrameworkElement f, Type childType, int maxDepth)
        {
            return RecursiveFindControls(f, childType, 1, maxDepth);
        }
    
        private static object[] RecursiveFindControls(
            object o, Type childType, int depth, int maxDepth = 0)
        {
            List<object> list = new List<object>();
            var attrs = o.GetType()
                .GetCustomAttributes(typeof(ContentPropertyAttribute), true);
            if (attrs != null && attrs.Length > 0)
            {
                string childrenProperty = (attrs[0] as ContentPropertyAttribute).Name;
                foreach (var c in (IEnumerable)o.GetType()
                    .GetProperty(childrenProperty).GetValue(o, null))
                {
                    if (c.GetType().FullName == childType.FullName)
                        list.Add(c);
                    if (maxDepth == 0 || depth < maxDepth)
                        list.AddRange(RecursiveFindControls(
                            c, childType, depth + 1, maxDepth));
                }
            }
            return list.ToArray();
        }
    }
    
  • 15

    exciton80 ...我的代码没有通过usercontrols递归出现问题 . 它击中了Grid根并抛出错误 . 我相信这可以解决它:

    public static object[] FindControls(this FrameworkElement f, Type childType, int maxDepth)
    {
        return RecursiveFindControls(f, childType, 1, maxDepth);
    }
    
    private static object[] RecursiveFindControls(object o, Type childType, int depth, int maxDepth = 0)
    {
        List<object> list = new List<object>();
        var attrs = o.GetType().GetCustomAttributes(typeof(ContentPropertyAttribute), true);
        if (attrs != null && attrs.Length > 0)
        {
            string childrenProperty = (attrs[0] as ContentPropertyAttribute).Name;
            if (String.Equals(childrenProperty, "Content") || String.Equals(childrenProperty, "Children"))
            {
                var collection = o.GetType().GetProperty(childrenProperty).GetValue(o, null);
                if (collection is System.Windows.Controls.UIElementCollection) // snelson 6/6/11
                {
                    foreach (var c in (IEnumerable)collection)
                    {
                        if (c.GetType().FullName == childType.FullName)
                            list.Add(c);
                        if (maxDepth == 0 || depth < maxDepth)
                            list.AddRange(RecursiveFindControls(
                                c, childType, depth + 1, maxDepth));
                    }
                }
                else if (collection != null && collection.GetType().BaseType.Name == "Panel") // snelson 6/6/11; added because was skipping control (e.g., System.Windows.Controls.Grid)
                {
                    if (maxDepth == 0 || depth < maxDepth)
                        list.AddRange(RecursiveFindControls(
                            collection, childType, depth + 1, maxDepth));
                }
            }
        }
        return list.ToArray();
    }
    
  • 8

    我有这样的序列函数(这是完全一般的):

    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)));
        }
    

    立即生孩子:

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

    在分层树上寻找所有孩子:

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

    您可以在Window上调用它来获取所有控件 .

    收集完成后,可以使用LINQ(即OfType,Where) .

  • 8

    由于问题足够普遍,它可能会吸引人们寻找非常微不足道的案例的答案:如果你只想要一个孩子而不是一个后代,你可以使用Linq:

    private void ItemsControlItem_Loaded(object sender, RoutedEventArgs e)
    {
        if (SomeCondition())
        {
            var children = (sender as Panel).Children;
            var child = (from Control child in children
                     where child.Name == "NameTextBox"
                     select child).First();
            child.Focus();
        }
    }
    

    或者当然是明显的循环迭代儿童 .

  • 17

    这是一个使用灵活谓词的解决方案:

    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;
    }
    

    你可以这样称呼它:

    var child = FindChild(parent, child =>
    {
        var textBlock = child as TextBlock;
        if (textBlock != null && textBlock.Name == "MyTextBlock")
            return true;
        else
            return false;
    }) as TextBlock;
    
  • 2

    这些选项已经讨论过在C#中遍历Visual Tree . 它也可以使用RelativeSource标记扩展来遍历xaml中的可视化树 . msdn

    按类型查找

    Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type <TypeToFind>}}}"
    
  • 5

    这段代码只修复了@CrimsonX回答的错误:

    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;
              }
    
     // 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
            {
              // child element found.
              foundChild = (T)child;
              break;
            }
          }
    
          return foundChild;
        }
    

    如果类型匹配但名称不匹配,则需要继续以递归方式调用该方法(当您将 FrameworkElement 作为 T 传递时会发生这种情况) . 否则它会返回 null 而这是错误的 .

  • 16

    要从代码中查找给定类型的祖先,您可以使用:

    [CanBeNull]
    public static T FindAncestor<T>(DependencyObject d) where T : DependencyObject
    {
        while (true)
        {
            d = VisualTreeHelper.GetParent(d);
    
            if (d == null)
                return null;
    
            var t = d as T;
    
            if (t != null)
                return t;
        }
    }
    

    此实现使用迭代而不是递归,这可能稍微快一些 .

    如果您使用的是C#7,可以稍微缩短一下:

    [CanBeNull]
    public static T FindAncestor<T>(DependencyObject d) where T : DependencyObject
    {
        while (true)
        {
            d = VisualTreeHelper.GetParent(d);
    
            if (d == null)
                return null;
    
            if (d is T t)
                return t;
        }
    }
    
  • 11

    试试这个

    <TextBlock x:Name="txtblock" FontSize="24" >Hai Welcom to this page
    </TextBlock>
    

    代码背后

    var txtblock = sender as Textblock;
    txtblock.Foreground = "Red"
    

相关问题