Buscar todos los controles en WPF Ventana por tipo

Estoy buscando una forma de encontrar todos los controles en la ventana por su tipo,

por ejemplo: encuentre todos los TextBoxes , encuentre todos los controles que implementen una interfaz específica, etc.

Esto debería funcionar

 public static IEnumerable FindVisualChildren(DependencyObject depObj) where T : DependencyObject { if (depObj != null) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { DependencyObject child = VisualTreeHelper.GetChild(depObj, i); if (child != null && child is T) { yield return (T)child; } foreach (T childOfChild in FindVisualChildren(child)) { yield return childOfChild; } } } } 

entonces enumeras los controles como tal

 foreach (TextBlock tb in FindVisualChildren(window)) { // do something with tb here } 

Esta es la manera más fácil:

 IEnumerable collection = control.Children.OfType(); 

donde el control es el elemento raíz de la ventana.

Adapte la respuesta de @Bryce Kahle para seguir la sugerencia de @Mathias Lykkegaard Lorenzen y use LogicalTreeHelper.

Parece que funciona bien. 😉

  public static IEnumerable FindLogicalChildren ( DependencyObject depObj ) where T : DependencyObject { if( depObj != null ) { foreach( object rawChild in LogicalTreeHelper.GetChildren( depObj ) ){ if( rawChild is DependencyObject ) { DependencyObject child = (DependencyObject)rawChild; if( child is T ) { yield return (T)child; } foreach( T childOfChild in FindLogicalChildren( child ) ) { yield return childOfChild; } } } } } 

(Todavía no se verificará el control de las tabs o las rejillas dentro de los GroupBoxes mencionados por @Benjamin Berry y @David R, respectivamente.) (También siguió la sugerencia de @noonand y eliminó el hijo redundante! = Nulo)

Utilice las clases auxiliares VisualTreeHelper o LogicalTreeHelper según el árbol que le interese. Ambos proporcionan métodos para obtener los elementos LogicalTreeHelper de un elemento (aunque la syntax difiere un poco). A menudo uso estas clases para encontrar la primera aparición de un tipo específico, pero puede modificarlo fácilmente para encontrar todos los objetos de ese tipo:

 public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type) { if (obj != null) { if (obj.GetType() == type) { return obj; } for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject childReturn = FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type); if (childReturn != null) { return childReturn; } } } return null; } 

Encontré que la línea, VisualTreeHelper.GetChildrenCount (depObj) ;, utilizada en varios ejemplos anteriores no devuelve un recuento distinto de cero para los GroupBoxes, en particular, donde el GroupBox contiene una cuadrícula, y la cuadrícula contiene elementos secundarios. Creo que esto puede deberse a que GroupBox no puede contener más de un elemento secundario, y esto se almacena en su propiedad de contenido. No hay ningún tipo de propiedad GroupBox.Children. Estoy seguro de que no hice esto de manera muy eficiente, pero modifiqué el primer ejemplo de “FindVisualChildren” en esta cadena de la siguiente manera:

  public IEnumerable FindVisualChildren(DependencyObject depObj) where T : DependencyObject { if (depObj != null) { int depObjCount = VisualTreeHelper.GetChildrenCount(depObj); for (int i = 0; i (child)) { yield return childOfChild; } } } } 

Para obtener una lista de todos los niños de un tipo específico, puede usar:

 private static IEnumerable FindInVisualTreeDown(DependencyObject obj, Type type) { if (obj != null) { if (obj.GetType() == type) { yield return obj; } for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type)) { if (child != null) { yield return child; } } } } yield break; } 

Pequeño cambio en la recursión para que pueda, por ejemplo, encontrar el control de tabs hijo de un control de tabs.

  public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type) { if (obj != null) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child.GetType() == type) { return child; } DependencyObject childReturn = FindInVisualTreeDown(child, type); if (childReturn != null) { return childReturn; } } } return null; } 

Y así es como funciona hacia arriba

  private T FindParent(DependencyObject item, Type StopAt) where T : class { if (item is T) { return item as T; } else { DependencyObject _parent = VisualTreeHelper.GetParent(item); if (_parent == null) { return default(T); } else { Type _type = _parent.GetType(); if (StopAt != null) { if ((_type.IsSubclassOf(StopAt) == true) || (_type == StopAt)) { return null; } } if ((_type.IsSubclassOf(typeof(T)) == true) || (_type == typeof(T))) { return _parent as T; } else { return FindParent(_parent, StopAt); } } } } 

Tenga en cuenta que el uso de VisualTreeHelper solo funciona en controles que se derivan de Visual o Visual 3D. Si también necesita inspeccionar otros elementos (por ejemplo, TextBlock, FlowDocument, etc.), el uso de VisualTreeHelper generará una excepción.

Aquí hay una alternativa que recae en el árbol lógico si es necesario:

http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways

Aquí hay otra versión más compacta, con la syntax genérica:

  public static IEnumerable FindLogicalChildren(DependencyObject obj) where T : DependencyObject { if (obj != null) { if (obj is T) yield return obj as T; foreach (DependencyObject child in LogicalTreeHelper.GetChildren(obj).OfType()) foreach (T c in FindLogicalChildren(child)) yield return c; } } 

Quise agregar un comentario, pero tengo menos de 50 puntos, así que solo puedo responder. Tenga en cuenta que si utiliza el método “VisualTreeHelper” para recuperar objetos XAML “TextBlock”, también obtendrá objetos “Button” de XAML. Si reinicializa el objeto “TextBlock” escribiendo en el parámetro Textblock.Text, ya no podrá cambiar el texto del botón utilizando el parámetro Button.Content. El botón mostrará permanentemente el texto escrito en él desde la acción de escritura Textblock.Text (desde que se recuperó,

 foreach (TextBlock tb in FindVisualChildren(window)) { // do something with tb here tb.Text = ""; //this will overwrite Button.Content and render the //Button.Content{set} permanently disabled. } 

Para evitar esto, puede intentar usar un “cuadro de texto” XAML y agregar métodos (o eventos) para imitar un botón XAMAL. XAML “TextBox” no se recostack mediante una búsqueda de “TextBlock”.

Mi versión para C ++ / CLI

 template < class T, class U > bool Isinst(U u) { return dynamic_cast< T >(u) != nullptr; } template  T FindVisualChildByType(Windows::UI::Xaml::DependencyObject^ element, Platform::String^ name) { if (Isinst(element) && dynamic_cast(element)->Name == name) { return dynamic_cast(element); } int childcount = Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(element); for (int i = 0; i < childcount; ++i) { auto childElement = FindVisualChildByType(Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(element, i), name); if (childElement != nullptr) { return childElement; } } return nullptr; }; 

Por alguna razón, ninguna de las respuestas publicadas aquí me ayudó a obtener todos los controles del tipo dado contenidos en un control dado en mi MainWindow. Necesitaba encontrar todos los elementos del menú en un menú para iterarlos. No todos eran descendientes directos del menú, así que logré recolectar solo el primer lilne de ellos usando cualquiera de los códigos anteriores. Este método de extensión es mi solución para el problema para cualquiera que continúe leyendo todo el camino hasta aquí.

 public static void FindVisualChildren(this ICollection children, DependencyObject depObj) where T : DependencyObject { if (depObj != null) { var brethren = LogicalTreeHelper.GetChildren(depObj); var brethrenOfType = LogicalTreeHelper.GetChildren(depObj).OfType(); foreach (var childOfType in brethrenOfType) { children.Add(childOfType); } foreach (var rawChild in brethren) { if (rawChild is DependencyObject) { var child = rawChild as DependencyObject; FindVisualChildren(children, child); } } } } 

Espero eso ayude.

Muy buena respuesta.

Versión VB.NET:

 Public Shared Iterator Function FindVisualChildren(Of T As DependencyObject)(depObj As DependencyObject) As IEnumerable(Of T) If depObj IsNot Nothing Then For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(depObj) - 1 Dim child As DependencyObject = VisualTreeHelper.GetChild(depObj, i) If child IsNot Nothing AndAlso TypeOf child Is T Then Yield DirectCast(child, T) End If For Each childOfChild As T In FindVisualChildren(Of T)(child) Yield childOfChild Next Next End If End Function 

Uso (esto deshabilita todos los cuadros de texto en una ventana):

  For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me) tb.IsEnabled = False Next 

Lo encontré más fácil sin Visual Tree Helpers:

 foreach (UIElement element in MainWindow.Children) { if (element is TextBox) { if ((element as TextBox).Text != "") { //Do something } } };