it-swarm.com.de

Wie finde ich WPF-Steuerelemente nach Name oder Typ?

Ich muss eine WPF-Steuerelementhierarchie nach Steuerelementen durchsuchen, die einem bestimmten Namen oder Typ entsprechen. Wie kann ich das machen?

255
alex2k8

Ich habe das von John Myczek und Tri Qs Algorithmus oben verwendete Vorlagenformat kombiniert, um einen findChild-Algorithmus zu erstellen, der für alle übergeordneten Elemente verwendet werden kann. Denken Sie daran, dass das rekursive Durchsuchen eines Baums nach unten ein langwieriger Prozess sein kann. Ich habe dies in einer WPF-Anwendung nur vor Ort überprüft. Bitte kommentieren Sie alle Fehler, die Sie möglicherweise finden, und ich werde meinen Code korrigieren.

WPF Snoop ist ein nützliches Werkzeug für die Anzeige des visuellen Baums. Ich würde dringend empfehlen, es beim Testen zu verwenden oder diesen Algorithmus zu verwenden, um Ihre Arbeit zu überprüfen.

Es gibt einen kleinen Fehler im Algorithmus von Tri Q. Wenn childrenCount> 1 ist und wir es noch einmal wiederholen, können wir das richtig gefundene Kind überschreiben . Aus diesem Grund habe ich meinem Code eine if (foundChild != null) break; hinzugefügt, um mit dieser Bedingung umzugehen.

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

Nenne es so:

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

Hinweis Application.Current.MainWindow kann ein beliebiges übergeordnetes Fenster sein.

299
CrimsonX

Sie können ein Element auch nach Namen suchen, indem Sie FrameworkElement.FindName (string) verwenden.

Gegeben:

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

In die Code-Behind-Datei können Sie schreiben:

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

Da das Feld mit x: Name definiert ist, können Sie natürlich auch auf das generierte Feld verweisen, aber Sie möchten es möglicherweise eher dynamisch als statisch nachschlagen.

Dieser Ansatz ist auch für Vorlagen verfügbar, in denen das benannte Element mehrmals vorkommt (einmal pro Verwendung der Vorlage).

125
Drew Noakes

Sie können das VisualTreeHelper verwenden, um Steuerelemente zu finden. Im Folgenden finden Sie eine Methode, mit der VisualTreeHelper ein übergeordnetes Steuerelement eines angegebenen Typs findet. Mit dem VisualTreeHelper können Sie Steuerelemente auch auf andere Weise finden.

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

Nenne es so:

Window owner = UIHelper.FindVisualParent<Window>(myControl);
66
John Myczek

Ich wiederhole vielleicht nur alle anderen, aber ich habe ein hübsches Stück Code, das die DependencyObject-Klasse mit einer Methode FindChild () erweitert, mit der Sie das Kind nach Typ und Name erhalten. Einfach einbinden und verwenden.

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

Ich hoffe, Sie finden es nützlich.

20
Tri Q Tran

Meine Erweiterungen zum Code.

  • Es wurden Überladungen hinzugefügt, um ein Kind nach Typ, Typ und Kriterien (Prädikat) zu finden. Suchen Sie alle Kinder des Typs, die die Kriterien erfüllen
  • die FindChildren-Methode ist ein Iterator und eine Erweiterungsmethode für DependencyObject
  • FindChildren geht auch logische Unterbäume durch. Siehe Josh Smiths Beitrag im Blogbeitrag.

Quelle: https://code.google.com/p/gishu-util/source/browse/#git%2FWPF%2FUtilities

Erläuternder Blog-Beitrag: http://madcoderspeak.blogspot.com/2010/04/wpf-find-child-control-of-specific-type.html

18
Gishu

Wenn Sie ALLE Steuerelemente eines bestimmten Typs suchen möchten, ist dieses Snippet möglicherweise auch für Sie von Interesse

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

Ich habe den Code von CrimsonX bearbeitet, da er nicht mit Oberklassentypen funktionierte:

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

Dadurch werden einige Elemente ausgeblendet. Sie sollten sie so erweitern, um ein breiteres Spektrum von Steuerelementen zu unterstützen. Schauen Sie sich für eine kurze Diskussion hier an

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

Obwohl ich die Rekursion im Allgemeinen liebe, ist sie beim Programmieren in C # nicht so effizient wie die Iteration. Vielleicht ist die folgende Lösung besser als die von John Myczek vorgeschlagene? Dadurch wird eine Hierarchie von einem bestimmten Steuerelement durchsucht, um ein Vorgängersteuerelement eines bestimmten Typs zu finden.

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

Nennen Sie es so, um das Window zu finden, das ein Steuerelement mit dem Namen ExampleTextBox enthält:

Window window = ExampleTextBox.FindVisualAncestorOfType<Window>();
13
Nathan Phillips

Hier ist mein Code, um Steuerelemente nach Typ zu finden und gleichzeitig zu steuern, wie tief wir in die Hierarchie vordringen (maxDepth == 0 bedeutet unendlich tief).

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

exciton80 ... Ich hatte ein Problem mit Ihrem Code, der nicht über Benutzersteuerelemente rekursiv ist. Es traf die Gitterwurzel und warf einen Fehler aus. Ich glaube, das behebt es für mich:

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

Ich habe eine Sequenzfunktion wie diese (die ganz allgemein ist):

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

Erste unmittelbare Kinder:

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

Suche nach allen Kindern im Hiararchischen Baum:

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

Sie können dies im Fenster aufrufen, um alle Steuerelemente abzurufen.

Nachdem Sie die Sammlung erstellt haben, können Sie LINQ verwenden (OfType, Where).

8
VB Guy

Da die Frage allgemein genug ist, um Menschen anzulocken, die nach Antworten auf sehr unbedeutende Fälle suchen: Wenn Sie nur ein Kind anstelle eines Nachkommen möchten, können Sie Linq verwenden:

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

oder natürlich die offensichtliche Schleife, die über Kinder iteriert.

6
El Zorko

Diese Optionen beziehen sich bereits auf das Durchlaufen des Visual Tree in C #. Es ist auch möglich, den visuellen Baum in XAML mit der RelativeSource-Markup-Erweiterung zu durchlaufen. msdn

suche nach Typ

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

Hier ist eine Lösung, die ein flexibles Prädikat verwendet:

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

Sie können es zum Beispiel so nennen:

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

Dieser Code behebt nur den Fehler von @CrimsonX answer:

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

Sie müssen die Methode nur rekursiv aufrufen, wenn die Typen übereinstimmen, die Namen jedoch nicht (dies geschieht, wenn Sie FrameworkElement als T übergeben). Andernfalls wird null zurückgegeben, und das ist falsch.

1
Amir Oveisi

Um einen Vorfahren eines bestimmten Typs im Code zu finden, können Sie Folgendes verwenden:

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

Diese Implementierung verwendet eine Iteration anstelle einer Rekursion, die etwas schneller sein kann.

Wenn Sie C # 7 verwenden, kann dies etwas kürzer gemacht werden:

[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;
    }
}
0
Drew Noakes