283 votes

Comment puis-je trouver des contrôles WPF par nom ou par type ?

Je dois rechercher dans une hiérarchie de contrôles WPF les contrôles qui correspondent à un nom ou un type donné. Comment faire ?

330voto

CrimsonX Points 3239

J'ai combiné le format de modèle utilisé par John Myczek et l'algorithme de Tri Q ci-dessus pour créer un algorithme findChild qui peut être utilisé sur n'importe quel parent. Gardez à l'esprit que la recherche récursive d'un arbre vers le bas peut être un processus long. J'ai seulement vérifié cela sur une application WPF, merci de commenter toute erreur que vous pourriez trouver et je corrigerai mon code.

WPF Snoop est un outil utile pour examiner l'arbre visuel - je vous recommande vivement de l'utiliser lors des tests ou de l'utilisation de cet algorithme pour vérifier votre travail.

Il y a une petite erreur dans l'algorithme de Tri Q. Après que l'enfant soit trouvé, si childrenCount est > 1 et que nous itérons à nouveau, nous pouvons écraser l'enfant correctement trouvé. C'est pourquoi j'ai ajouté un if (foundChild != null) break; dans mon code pour gérer cette 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;
}

Appelez ça comme ça :

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

Note Application.Current.MainWindow peut être n'importe quelle fenêtre parent.

0 votes

@CrimsonX : Je m'y prends peut-être mal... J'avais un besoin similaire où j'avais besoin d'accéder à un contrôle (ListBox) à l'intérieur d'un ContentControl (Expander). Le code ci-dessus n'a pas fonctionné pour moi tel quel... J'ai dû mettre à jour le code ci-dessus pour voir si un nœud de feuille (GetChildrenCount => 0) est un ContentControl. Si oui, vérifier si le contenu correspond aux critères nom+type.

0 votes

@Gishu - Je pense que cela devrait fonctionner dans ce but. Pouvez-vous copier et coller votre code pour montrer comment vous utilisez l'appel ? Je pense que c'est FindChild<ListBox>(Expander myExpanderName, "myListBoxName").

0 votes

@CrimsonX : FindChild<ListBox>( topLevelUserControl, "myListBoxName") & topLevelUserControl > DockPanel > StackPanel > Expander > ListBox La récursion s'arrêterait à l'Expander ; puisque GetChildrenCount() retournerait 0.

156voto

Drew Noakes Points 69288

Vous pouvez également trouver un élément par son nom en utilisant FrameworkElement.FindName(chaîne de caractères) .

Étant donné :

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

Dans le fichier code-behind, vous pourriez écrire :

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

Bien sûr, comme il est défini à l'aide de x:Name, vous pourriez simplement faire référence au champ généré, mais peut-être souhaitez-vous le consulter de manière dynamique plutôt que statique.

Cette approche est également disponible pour les modèles, dans lesquels l'élément nommé apparaît plusieurs fois (une fois par utilisation du modèle).

6 votes

Pour que cela fonctionne, vous ne devez pas nécessairement ajouter le "x :" à l'attribut de nom.

3 votes

Cela ne semble pas toujours fonctionner. J'ai des UserControls qui sont combinés ensemble de manière programmatique dans des grilles imbriquées comme le contenu d'une fenêtre de propriétés. La réponse de CrimsonX fonctionne cependant très bien.

8 votes

Cela ne fonctionnera pas pour les éléments des ItemControls, ListBoxes, etc.

70voto

John Myczek Points 6205

Vous pouvez utiliser le VisualTreeHelper pour trouver des contrôles. Vous trouverez ci-dessous une méthode qui utilise VisualTreeHelper pour trouver un contrôle parent d'un type spécifié. Vous pouvez également utiliser VisualTreeHelper pour trouver des contrôles d'autres manières.

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

Appelez ça comme ça :

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

2 votes

Comment obtient-on ou qu'est-ce que myControl ?

22voto

Tri Q Points 2661

Je suis peut-être en train de répéter tout le monde mais j'ai un joli morceau de code qui étend la classe DependencyObject avec une méthode FindChild() qui vous donnera l'enfant par type et par nom. Il suffit de l'inclure et de l'utiliser.

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

J'espère que vous le trouverez utile.

2 votes

Selon mon message ci-dessus, il y a une petite erreur d'implémentation dans votre code : stackoverflow.com/questions/636383/wpf-ways-to-find-controls/

18voto

Gishu Points 59012

Mes extensions au code.

  • Ajout de surcharges pour trouver un enfant par type, par type et critère (prédicat), trouver tous les enfants de type qui répondent aux critères.
  • la méthode FindChildren est un itérateur en plus d'être une méthode d'extension pour DependencyObject
  • FindChildren parcourt également les sous-arbres logiques. Voir l'article de Josh Smith dont le lien figure dans le billet de blog.

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

Article de blog explicatif : http://madcoderspeak.blogspot.com/2010/04/wpf-find-child-control-of-specific-type.html

0 votes

-1 Exactement ce que j'étais sur le point d'implémenter (prédicat, itérateur, et méthode d'extension), mais il y a un 404 sur le lien source. Je changerai en +1 si le code est inclus ici, ou si le lien source est corrigé !

0 votes

@cod3monk3y - La migration de Git a tué le lien il semble :) Voilà code.google.com/p/gishu-util/source/browse/

0 votes

Je suppose que c'est le code, car le lien original est vide. github.com/dmjensen/gishu-util

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X