wpf controls find

¿Cómo puedo encontrar los controles de WPF por nombre o tipo?



controls find (18)

Aquí está mi código para encontrar los controles por Tipo mientras se controla la profundidad de la jerarquía (maxDepth == 0 significa infinitamente profundo).

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

Necesito buscar en una jerarquía de control de WPF para los controles que coinciden con un nombre o tipo dado. ¿Cómo puedo hacer esto?


Aquí hay una solución que usa un predicado flexible:

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

Por ejemplo, puedes llamarlo así:

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


Combiné el formato de plantilla utilizado por John Myczek y el algoritmo de Tri Q para crear un algoritmo findChild que se puede usar en cualquier padre. Tenga en cuenta que buscar recursivamente un árbol hacia abajo puede ser un proceso largo. Solo he comprobado esto en una aplicación WPF, comente sobre cualquier error que pueda encontrar y corregiré mi código.

WPF Snoop es una herramienta útil para observar el árbol visual. Recomiendo usarlo mientras prueba o usa este algoritmo para verificar su trabajo.

Hay un pequeño error en el algoritmo de Tri Q Después de que se encuentra el niño, si childrenCount es> 1 y repetimos de nuevo, podemos sobrescribir al niño encontrado correctamente. Por lo tanto, agregué un if (foundChild != null) break; en mi código para hacer frente a esta condición.

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

Llámalo así:

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

Nota Application.Current.MainWindow puede ser cualquier ventana principal.


Dado que la pregunta es lo suficientemente general como para atraer a personas que buscan respuestas en casos muy triviales: si solo desea un hijo en lugar de un descendiente, puede usar 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(); } }

o por supuesto lo obvio para la repetición de bucles sobre niños.


Edité el código de CrimsonX porque no funcionaba con los tipos de superclase:

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


Estas opciones ya hablan sobre atravesar el Árbol Visual en C #. También es posible atravesar el árbol visual en xaml utilizando la extensión de marcado RelativeSource. msdn

encontrar por tipo

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


Este código solo corrige el error de la respuesta de @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; }

Solo debe seguir llamando al método de forma recursiva si los tipos coinciden pero los nombres no (esto sucede cuando pasa FrameworkElement como T ). De lo contrario volverá null y eso está mal.


Esto descartará algunos elementos: debe extenderlo de esta manera para admitir una gama más amplia de controles. Para una breve discusión, eche un vistazo here

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


Mis extensiones al código.

  • Se agregaron sobrecargas para encontrar un niño por tipo, por tipo y criterio (predicado), encontrar todos los hijos del tipo que cumplan con el criterio
  • El método FindChildren es un iterador además de ser un método de extensión para DependencyObject
  • FindChildren camina subárboles lógicos también. Ver la publicación de Josh Smith vinculada en la publicación del blog.

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

Publicación explicativa del blog: http://madcoderspeak.blogspot.com/2010/04/wpf-find-child-control-of-specific-type.html


Para encontrar un ancestro de un tipo dado a partir del código, puede usar:

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

Esta implementación utiliza la iteración en lugar de la recursión, que puede ser un poco más rápida.

Si está utilizando C # 7, esto puede hacerse un poco más corto:

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


Prueba esto

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

Código detrás

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


Puede que solo esté repitiendo a todos los demás, pero tengo un bonito código que extiende la clase DependencyObject con un método FindChild () que le dará al niño por tipo y nombre. Sólo tiene que incluir y utilizar.

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

Esperamos que te sea útil.


Puedes usar VisualTreeHelper para buscar controles. A continuación se muestra un método que usa VisualTreeHelper para encontrar un control principal de un tipo específico. También puede usar VisualTreeHelper para buscar controles de otras maneras.

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

Llámalo así:

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


Si bien me encanta la recursión en general, no es tan eficiente como la iteración cuando se programa en C #, por lo que quizás la siguiente solución sea mejor que la sugerida por John 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; }


Si desea encontrar TODOS los controles de un tipo específico, es posible que también le interese este fragmento de código

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


También puede encontrar un elemento por nombre usando FrameworkElement.FindName(string) .

Dado:

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

En el archivo de código subyacente, podría escribir:

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

Por supuesto, debido a que se define con x: Nombre, solo podría hacer referencia al campo generado, pero quizás desee buscarlo dinámicamente en lugar de hacerlo de forma estática.

Este enfoque también está disponible para las plantillas, en las que el elemento nombrado aparece varias veces (una vez por uso de la plantilla).


Tengo una función de secuencia como esta (que es completamente general):

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

Conseguir hijos inmediatos:

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

Encontrando a todos los niños en el árbol hiarárquico:

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

Puede llamar a esto en la ventana para obtener todos los controles.

Después de tener la colección, puede usar LINQ (es decir, OfType, Where).


exciton80 ... Estaba teniendo un problema con su código no recurrente a través de los controles de usuario. Estaba golpeando la raíz de la rejilla y lanzando un error. Creo que esto lo arregla para mí:

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