كيف يمكنني العثور على عناصر تحكم WPF بالاسم أو النوع؟

StackOverflow https://stackoverflow.com/questions/636383

  •  10-07-2019
  •  | 
  •  

سؤال

أحتاج إلى البحث في التسلسل الهرمي للتحكم في WPF عن عناصر التحكم التي تتطابق مع اسم أو نوع معين.كيف يمكنني أن أفعل هذا؟

هل كانت مفيدة؟

المحلول

لقد قمت بدمج تنسيق القالب الذي تستخدمه خوارزمية John Myczek وTri Q أعلاه لإنشاء خوارزمية findChild التي يمكن استخدامها على أي والد.ضع في اعتبارك أن البحث المتكرر عن شجرة للأسفل قد يكون عملية طويلة.لقد قمت فقط بالتحقق من ذلك في تطبيق WPF، يرجى التعليق على أي أخطاء قد تجدها وسوف أقوم بتصحيح الكود الخاص بي.

دبليو بي إف سنوب هي أداة مفيدة في النظر إلى الشجرة المرئية - أوصي بشدة باستخدامها أثناء الاختبار أو استخدام هذه الخوارزمية للتحقق من عملك.

يوجد خطأ بسيط في خوارزمية Tri Q. بعد العثور على الطفل، إذا كان 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 يمكن أن يكون أي نافذة الأصل.

نصائح أخرى

ويمكنك أيضا العثور على عنصر بالاسم باستخدام FrameworkElement .FindName (سلسلة) .

ونظرا:

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

في ملف التعليمات البرمجية الخلفية، يمكن أن تكتب:

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

وبطبيعة الحال، لأنه يعرف باستخدام س: الاسم، هل يمكن أن مجرد مرجع الحقل ولدت، ولكن ربما تريد البحث عنه حيوي بدلا من ثابت

وهذا النهج هو متوفر لقوالب، في البند الذي يدعى تظهر عدة مرات (مرة واحدة في استخدام القالب).

أيضا

ويمكنك استخدام 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);

وأنا قد يكون مجرد تكرار الجميع ولكن لدي قطعة جميلة من التعليمات البرمجية التي تمتد الطبقة DependencyObject مع طريقة FindChild () التي سوف تحصل على الطفل حسب نوع واسم. وتشمل فقط واستخدامها.

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

وآمل أن تجد أنه من المفيد.

ملحقاتي للكود.

  • تمت إضافة الأحمال الزائدة للعثور على طفل واحد حسب النوع، حسب النوع والمعايير (المسند)، والعثور على جميع الأطفال من النوع الذي يستوفي المعايير
  • تعد طريقة FindChildren بمثابة مكرر بالإضافة إلى كونها طريقة تمديد لـ DependencyObject
  • يمشي FindChildren أيضًا على الأشجار الفرعية المنطقية.راجع منشور جوش سميث المرتبط في منشور المدونة.

مصدر:https://code.google.com/p/gishu-util/source/browse/#git%2FWPF%2FUtilities

مشاركة مدونة توضيحية:http://madcoderspeak.blogspot.com/2010/04/wpf-find-child-control-of-speci-type.html

إذا كنت تريد أن تجد ALL ضوابط نوع معين، قد تكون مهتمة في هذا المقتطف جدا

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

وأنا تحريرها كود 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;
}

وهذا من شأنه استبعاد بعض العناصر - يجب تقديم مثل هذا من أجل دعم مجموعة أوسع من الضوابط. لمناقشة وجيزة، لديها نظرة هنا

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

وبينما أنا أحب العودية بشكل عام، انها ليست فعالة مثل التكرار عند برمجة في C #، لذلك ربما يكون الحل التالي هو أكثر إتقانا من تلك التي اقترحها جون 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;
}

ويطلق عليه مثل هذا للعثور على Window التي تحتوي على عنصر تحكم يسمى ExampleTextBox:

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

إليك قانون بلدي لإيجاد الضوابط حسب نوع حين السيطرة على مدى عمق نذهب إلى التسلسل الهرمي (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();
    }
}

وexciton80 ... كنت أعاني من مشكلة مع التعليمات البرمجية لا recursing من خلال usercontrols. انها ضرب الجذر الشبكة ورمي خطأ. وأعتقد أن هذا يصلح له بالنسبة لي:

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

ولدي وظيفة تسلسل مثل هذا (والذي هو عام كامل):

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

وإيجاد جميع الأطفال أسفل شجرة hiararchical:

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

ويمكنك الاتصال هذا على النافذة للحصول على كل الضوابط.

وبعد لديك مجموعة، يمكنك استخدام LINQ (أي OfType، أين).

ومنذ والسؤال هو عام بما فيه الكفاية أنه قد جذب الناس تبحث عن أجوبة لقضايا تافهة جدا: إذا كنت ترغب فقط في الأطفال بدلا من سليل، يمكنك استخدام ينق:

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

وأو بالطبع واضح لبالتكرار حلقة على الأطفال.

وهذه الخيارات يتحدثون بالفعل عن عبور شجرة البصرية في C #. من الممكن أن تجتاز شجرة البصرية في XAML كذلك باستخدام تمديد العلامات RelativeSource. MSDN

والاكتشاف عن طريق نوع

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

وهنا هو الحل الذي يستخدم المسند مرونة:

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;

وهذا الرمز فقط بإصلاح الخلل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 العودة وهذا غير صحيح.

لايجاد سلف من نوع معين من التعليمات البرمجية، يمكنك استخدام:

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

وهذه محاولة

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

ورمز خلف

var txtblock = sender as Textblock;
txtblock.Foreground = "Red"
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top