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

Je dois rechercher une hiérarchie de contrôle WPF pour les contrôles correspondant à un nom ou à un type donné. Comment puis-je faire ceci?

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 pourrait être un processus long. Je l’ai seulement vérifié sur une application WPF, veuillez commenter les erreurs que vous pourriez trouver et je corrigerai mon code.

WPF Snoop est un outil utile pour examiner l’arborescence visuelle. Je vous recommande fortement de l’utiliser lors du test ou de l’utilisation de cet algorithme pour vérifier votre travail.

Il y a une petite erreur dans l’algorithme de Tri Q. Une fois que l’enfant est trouvé, si childrenCount est> 1 et que nous recommençons, nous pouvons écraser l’enfant correctement trouvé. J’ai donc ajouté une if (foundChild != null) break; dans mon code pour faire face à cette condition.

 ///  /// Finds a Child of a given item in the visual tree. ///  /// A direct parent of the queried item. /// The type of the queried item. /// x:Name or Name of child.  /// The first parent item that matches the submitted type parameter. /// If not matching item can be found, /// a null parent is being returned. public static T FindChild(DependencyObject parent, ssortingng 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(child, childName); // If the child is found, break so we do not overwrite the found child. if (foundChild != null) break; } else if (!ssortingng.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-le comme ceci:

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

Remarque Application.Current.MainWindow peut être n’importe quelle fenêtre parent.

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

Donné:

    

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

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

Bien sûr, comme il est défini avec x: Name, vous pouvez simplement référencer le champ généré, mais peut-être voulez-vous le rechercher de manière dynamic 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).

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

 public static class UIHelper { ///  /// Finds a parent of a given item on the visual tree. ///  /// The type of the queried item. /// A direct or indirect child of the queried item. /// The first parent item that matches the submitted type parameter. /// If not matching item can be found, a null reference is being returned. public static T FindVisualParent(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(parentObject); } } } 

Appelez-le comme ceci:

 Window owner = UIHelper.FindVisualParent(myControl); 

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. Juste inclure et utiliser.

 public static class UIChildFinder { public static DependencyObject FindChild(this DependencyObject reference, ssortingng 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; } } 

J'espère que vous le trouverez utile.

Mes extensions au code.

  • Ajout de surcharges pour trouver un enfant par type, par type et critère (prédicat), rechercher tous les enfants de type répondant aux critères
  • la méthode FindChildren est un iterator en plus d’être une méthode d’extension pour DependencyObject
  • FindChildren parcourt également les sous-arbres logiques. Voir le post de Josh Smith lié dans l’article du 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

Si vous souhaitez trouver TOUS les contrôles d’un type spécifique, cet extrait peut également vous intéresser

  public static IEnumerable FindVisualChildren(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(child)) { yield return other; } } } 

J’ai édité le code de CrimsonX car il ne fonctionnait pas avec les types de superclasses:

 public static T FindChild(DependencyObject depObj, ssortingng 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(child, childName); if (obj != null) return obj; } return null; } 

Cela écartera certains éléments – vous devriez l’étendre comme cela afin de prendre en charge un plus large éventail de contrôles. Pour une brève discussion, regardez ici

  ///  /// Helper methods for UI-related tasks. ///  public static class UIHelper { ///  /// Finds a parent of a given item on the visual tree. ///  /// The type of the queried item. /// A direct or indirect child of the /// queried item. /// The first parent item that matches the submitted /// type parameter. If not matching item can be found, a null /// reference is being returned. public static T TryFindParent(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(parentObject); } } ///  /// This method is an alternative to WPF's ///  method, which also /// supports content elements. Do note, that for content element, /// this method falls back to the logical tree of the element! ///  /// The item to be processed. /// The submitted item's parent, if available. Otherwise /// null. 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); } } 

Alors que j’adore la récursivité en général, ce n’est pas aussi efficace que l’itération lors de la programmation en C #, alors la solution suivante est peut-être plus simple que celle suggérée par John Myczek ci-dessus?

 public static T FindVisualAncestorOfType(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; } 

Voici mon code pour trouver les contrôles par type tout en contrôlant la profondeur de la hiérarchie (maxDepth == 0 signifie infiniment profond).

 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 list = new List(); var attrs = o.GetType() .GetCustomAtsortingbutes(typeof(ContentPropertyAtsortingbute), true); if (attrs != null && attrs.Length > 0) { ssortingng childrenProperty = (attrs[0] as ContentPropertyAtsortingbute).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 … J’avais un problème avec votre code non récursif à travers des contrôles d’utilisateurs. Il frappait la racine de la grid et générait une erreur. Je crois que cela le corrige pour moi:

 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 list = new List(); var attrs = o.GetType().GetCustomAtsortingbutes(typeof(ContentPropertyAtsortingbute), true); if (attrs != null && attrs.Length > 0) { ssortingng childrenProperty = (attrs[0] as ContentPropertyAtsortingbute).Name; if (Ssortingng.Equals(childrenProperty, "Content") || Ssortingng.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 (eg, System.Windows.Controls.Grid) { if (maxDepth == 0 || depth < maxDepth) list.AddRange(RecursiveFindControls( collection, childType, depth + 1, maxDepth)); } } } return list.ToArray(); } 

J’ai une fonction de séquence comme celle-ci (qui est complètement générale):

  public static IEnumerable SelectAllRecursively(this IEnumerable items, Func> func) { return (items ?? Enumerable.Empty()).SelectMany(o => new[] { o }.Concat(SelectAllRecursively(func(o), func))); } 

Obtenir des enfants immédiats:

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

Trouver tous les enfants dans l’arbre hiararchique:

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

Vous pouvez appeler cela sur la fenêtre pour obtenir tous les contrôles.

Une fois que vous avez la collection, vous pouvez utiliser LINQ (c.-à-d. OfType, Where).

Puisque la question est suffisamment générale pour attirer des personnes à la recherche de réponses à des cas très sortingviaux: si vous voulez juste un enfant plutôt qu’un descendant, vous pouvez utiliser 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(); } } 

ou bien sur l’évidence pour l’itération en boucle sur les enfants.

Voici une solution qui utilise un prédicat flexible:

 public static DependencyObject FindChild(DependencyObject parent, Func 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; } 

Vous pouvez par exemple l'appeler comme ceci:

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

Ces options parlent déjà de traverser l’arborescence Visual en C #. Il est également possible de parcourir l’arborescence visuelle de xaml en utilisant l’extension de marquage RelativeSource. msdn

trouver par type

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

Pour trouver un ancêtre d’un type donné à partir du code, vous pouvez utiliser:

 [CanBeNull] public static T FindAncestor(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; } } 

Cette implémentation utilise l’itération au lieu de la récursivité, ce qui peut être légèrement plus rapide.

Si vous utilisez C # 7, cela peut être légèrement plus court:

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

Ce code corrige simplement le bogue de réponse de @CrimsonX:

  public static T FindChild(DependencyObject parent, ssortingng 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(child, childName); // If the child is found, break so we do not overwrite the found child. if (foundChild != null) break; } else if (!ssortingng.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(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; } 

Il vous suffit de continuer à appeler la méthode de manière récursive si les types correspondent, mais pas les noms (cela se produit lorsque vous transmettez FrameworkElement tant que T ). sinon ça va retourner null et c’est faux.

Essaye ça

 Hai Welcom to this page  

Code Derrière

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