Frage

Ich brauche eine WPF-Steuerelement Hierarchie für Steuerelemente suchen, die einen bestimmten Namen oder Typen entsprechen. Wie kann ich das tun?

War es hilfreich?

Lösung

I kombiniert das Template-Format verwendet von John Myczek und Tri Q-Algorithmus über einen findChild Algorithmus zu erstellen, die auf jedem Elternteil verwendet werden kann. Beachten Sie, dass rekursiv einen Baum nach unten Suche könnte ein langwieriger Prozess sein. Ich habe nur diese auf einer WPF-Anwendung vor Ort überprüft, bitte Fehler kommentieren Sie finden könnten, und ich werde meinen Code korrigieren.

WPF Snoop ist ein nützliches Werkzeug bei der visuellen Struktur in der Suche - ich stark würde empfehlen, es zu benutzen beim testen oder Verwendung dieses Algorithmus Ihrer Arbeit zu überprüfen.

Es gibt einen kleinen Fehler in Tri Q-Algorithmus. Nachdem das Kind gefunden wird, wenn childrenCount> 1 ist und wir wiederholen können wir wieder Kind fanden die richtig überschreiben. Deshalb habe ich eine if (foundChild != null) break; in meinen Code mit dieser Krankheit fertig zu werden.

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

Nennen Sie es wie folgt aus:

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

Hinweis Application.Current.MainWindow kann jedes übergeordnetes Fenster sein.

Andere Tipps

Sie können auch ein Element von Namen finden Framework .FindName (string) .

Gegeben:

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

In der Code-Behind-Datei, könnte man schreiben:

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

Natürlich, denn es definiert ist mit x:. Namen, können Sie nur das erzeugte Feld verweisen, aber vielleicht wollen Sie es dynamisch eher nachschlagen als statisch

Dieser Ansatz ist auch für Vorlagen zur Verfügung, in dem die genannten Artikel (pro Nutzung der Vorlage einmal) mehrmals angezeigt wird.

Sie können mit der VisualTreeHelper finden Kontrollen. Unten ist ein Verfahren, das den VisualTreeHelper verwendet eine übergeordnete Steuerung eines bestimmten Typs zu finden. Sie können die VisualTreeHelper verwenden Kontrollen auch auf andere Weise als auch zu 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);
      }
   }
}

Nennen Sie es wie folgt aus:

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

kann ich sein, nur alle anderen zu wiederholen, aber ich habe ein ziemlich Stück Code haben, der die DependencyObject-Klasse mit einer Methode FindChild () erstreckt, dass Sie das Kind nach Typ und Namen erhalten. Nur sind und verwendet werden.

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

Hoffe, Sie finden es nützlich.

Meine Erweiterungen an den Code.

  • Überlastungen Added ein Kind von Typ zu finden, die nach Art und Kriterien (Prädikat), finden alle Kinder des Typs, die die Kriterien
  • erfüllen
  • die FindChildren Verfahren ist ein Iterator zusätzlich eine Erweiterungsmethode für DependencyObject wobei
  • FindChildren geht logische Teilbäume auch. Siehe Josh Smith Post im Blog-Eintrag verknüpft.

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

Erläuternde Blog-Eintrag: http: //madcoderspeak.blogspot. com / 2010/04 / wpf-find-Kind-control-of-specific-type.html

Wenn Sie alle Steuerelemente eines bestimmten Typs finden möchten, können Sie in diesem Snippet interessiert sein zu

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

I edited CrimsonX den Code, da es nicht mit Supertypen arbeitet:

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

Dies wird einige Elemente entlassen - Sie es so erstrecken, um sollte eine breitere Palette von Kontrollen zu unterstützen. Für eine kurze Diskussion, schauen Sie rel="noreferrer">

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

Während ich Rekursion im allgemeinen lieben, dann ist es nicht so effizient wie Iteration, wenn in C # programmieren, so vielleicht die folgende Lösung ist ordentlicheres als die von John Myczek vorgeschlagen? Dies sucht eine Hierarchie von einer gegebenen Steuerung bis zu einem Vorfahren Kontrolle 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, wie dies die Window zu finden, ein Steuerelement namens ExampleTextBox enthalten:

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

Hier ist mein Code Kontrollen nach Typ zu finden, während die Steuerung, wie tief wir in der Hierarchie gehen (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();
    }
}

exciton80 ... Ich habe ein Problem mit dem Code nicht Rekursion durch usercontrols. Es wurde Schlagen der Grid Wurzel und einen Fehler zu werfen. Ich glaube, dies 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();
}

Ich habe eine Sequenz Funktion 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)));
    }

Erst unmittelbare Kinder:

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

Die Suche nach allen Kindern auf dem hiararchical Baum:

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

Sie können dies auf dem Fenster rufen, um alle Kontrollen zu bekommen.

Wenn Sie die Sammlung haben, können Sie LINQ verwenden (das heißt OfType, Wo).

Da die Frage ist allgemein genug, dass es die Menschen nach Antworten auf sehr triviale Fälle suchen könnte anziehen: wenn Sie nur ein Kind eher als ein Nachkomme mögen, 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 offensichtlichen for-Schleife Iterieren über Kinder.

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 zum Beispiel nennen es wie folgt aus:

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

Dieser Code behebt nur @CrimsonX Antwort Bug:

 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 nur weiterhin die Methode aufgerufen wird rekursiv, wenn Typen passen aber Namen nicht (dies geschieht, wenn Sie FrameworkElement als T passieren). ansonsten ist es Gonna Rückkehr null und das ist falsch.

Um ein Vorfahre eines bestimmten Typs von Code zu finden, können Sie 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 Iteration statt 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;
    }
}

Versuchen Sie, diese

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

Code hinter

var txtblock = sender as Textblock;
txtblock.Foreground = "Red"
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top