publicstaticIEnumerable<T>FindVisualChildren<T>(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){yieldreturn(T)child;}foreach(T childOfChild inFindVisualChildren<T>(child)){yieldreturn childOfChild;}}}}
luego enumeras los controles así
foreach(TextBlock tb inFindVisualChildren<TextBlock>(window)){// do something with tb here}
Nota: Si está tratando de hacer que esto funcione y descubra que su ventana (por ejemplo) tiene 0 elementos secundarios visuales, intente ejecutar este método en el controlador de eventos Cargado. Si lo ejecuta en el constructor (incluso después de InitializeComponent ()), los elementos visuales aún no se cargan y no funcionará.
Ryan Lundy
24
Cambiar de VisualTreeHelper a LogicalTreeHelpers también hará que se incluyan elementos invisibles.
Mathias Lykkegaard Lorenzen
11
¿No es redundante la línea "child! = Null && child is T"? En caso de que no se acaba de leer "niño es T"
noonand
1
Lo convertiría en un método de extensión con solo inscribir un thisantes DependencyObject=>this DependencyObject depObj
Johannes Wanzek
1
@JohannesWanzek No olvides que también necesitarías cambiar el bit donde lo llamas en el niño: foreach (ChildofChild.FindVisualChildren <T> ()) {bla bla bla}
¿Qué quieres decir con "elemento raíz"? ¿Qué debo escribir para conectarme con mi formulario de ventana principal?
deadfish
Lo entiendo, en XAML ver que tenía que nombre del conjunto de rejilla <Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>y luego podría utilizarAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
Deadfish
68
Esto no responde la pregunta que se hizo. Solo devuelve controles secundarios de un nivel de profundidad.
Jim
21
Adapte la respuesta de @Bryce Kahle para seguir la sugerencia y el uso de @Mathias Lykkegaard Lorenzen LogicalTreeHelper.
Parece funcionar bien. ;)
publicstaticIEnumerable<T>FindLogicalChildren<T>(DependencyObject depObj )where T :DependencyObject{if( depObj !=null){foreach(object rawChild inLogicalTreeHelper.GetChildren( depObj )){if( rawChild isDependencyObject){DependencyObject child =(DependencyObject)rawChild;if( child is T ){yieldreturn(T)child;}foreach( T childOfChild inFindLogicalChildren<T>( child )){yieldreturn childOfChild;}}}}}
(Todavía no verificará los controles de pestañas o las Cuadrículas dentro de GroupBoxes como lo mencionan @Benjamin Berry y @David R respectivamente.) (¡También siguió la sugerencia de @noonand y eliminó el hijo redundante! = Nulo)
He estado buscando durante un tiempo cómo borrar todos mis cuadros de texto, tengo varias pestañas y este es el único código que funcionó :) gracias
JohnChris
13
Use las clases auxiliares VisualTreeHelpero LogicalTreeHelpersegún el árbol que le interese. Ambas proporcionan métodos para obtener los elementos secundarios de un elemento (aunque la sintaxis difiere un poco). A menudo uso estas clases para encontrar la primera aparición de un tipo específico, pero podría modificarlo fácilmente para encontrar todos los objetos de ese tipo:
publicstaticDependencyObjectFindInVisualTreeDown(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;}}}returnnull;}
+1 para explicación y publicación, pero Bryce Kahle publicó una función que funciona completamente Gracias
Andrija
Esto no soluciona el problema de la pregunta, y también la respuesta con el tipo genérico es mucho más clara. Combinándolo con el uso de VisualTreeHelper.GetChildrenCount (obj) solucionará el problema. Sin embargo, es útil ser considerado como una opción.
Vasil Popov
9
Descubrí que la línea, VisualTreeHelper.GetChildrenCount(depObj);utilizada en varios ejemplos anteriores no devuelve un recuento distinto de cero para GroupBoxes, en particular, donde GroupBoxcontiene Gridlos Gridelementos a y los elementos secundarios. Creo que esto puede deberse a que GroupBoxno se permite que contenga más de un hijo, y esto se almacena en su Contentpropiedad. No hay GroupBox.Childrentipo de propiedad. Estoy seguro de que no hice esto de manera muy eficiente, pero modifiqué el primer ejemplo "FindVisualChildren" en esta cadena de la siguiente manera:
publicIEnumerable<T>FindVisualChildren<T>(DependencyObject depObj)where T :DependencyObject{if(depObj !=null){int depObjCount =VisualTreeHelper.GetChildrenCount(depObj);for(int i =0; i <depObjCount; i++){DependencyObject child =VisualTreeHelper.GetChild(depObj, i);if(child !=null&& child is T){yieldreturn(T)child;}if(child isGroupBox){GroupBox gb = child asGroupBox;Object gpchild = gb.Content;if(gpchild is T){yieldreturn(T)child;
child = gpchild as T;}}foreach(T childOfChild inFindVisualChildren<T>(child)){yieldreturn childOfChild;}}}}
Aquí hay otra versión compacta, con la sintaxis genérica:
publicstaticIEnumerable<T>FindLogicalChildren<T>(DependencyObject obj)where T :DependencyObject{if(obj !=null){if(obj is T)yieldreturn obj as T;foreach(DependencyObject child inLogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>())foreach(T c inFindLogicalChildren<T>(child))yieldreturn c;}}
private T FindParent<T>(DependencyObject item,TypeStopAt)where T :class{if(item is T){return item as T;}else{DependencyObject _parent =VisualTreeHelper.GetParent(item);if(_parent ==null){returndefault(T);}else{Type _type = _parent.GetType();if(StopAt!=null){if((_type.IsSubclassOf(StopAt)==true)||(_type ==StopAt)){returnnull;}}if((_type.IsSubclassOf(typeof(T))==true)||(_type ==typeof(T))){return _parent as T;}else{returnFindParent<T>(_parent,StopAt);}}}}
Tenga en cuenta que el uso de VisualTreeHelper solo funciona en controles que se derivan de Visual o Visual3D. Si también necesita inspeccionar otros elementos (por ejemplo, TextBlock, FlowDocument, etc.), el uso de VisualTreeHelper arrojará una excepción.
Aquí hay una alternativa que recurre al árbol lógico si es necesario:
Quería 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 capturará objetos XAML "Button". Si reinicializa el objeto "TextBlock" escribiendo en el parámetro Textblock.Text, ya no podrá cambiar el texto del botón con el parámetro Button.Content. El botón mostrará permanentemente el texto escrito desde Textblock. Acción de escritura de texto (desde cuando se recuperó -
foreach(TextBlock tb inFindVisualChildren<TextBlock>(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 recopila mediante una búsqueda de "TextBlock".
Esa es la diferencia entre el árbol visual y el lógico. El árbol visual contiene todos los controles (incluidos aquellos de los que está hecho un control, que se definen en la plantilla de controles), mientras que el árbol lógico solo contiene los controles reales (sin los definidos en las plantillas). Hay una buena visualización de este concepto aquí: enlace
lauxjpn
1
Mi versión para C ++ / CLI
template <class T,class U >boolIsinst(U u){return dynamic_cast< T >(u)!= nullptr;}
template <typename T>
T FindVisualChildByType(Windows::UI::Xaml::DependencyObject^ element,Platform::String^ name){if(Isinst<T>(element)&& dynamic_cast<Windows::UI::Xaml::FrameworkElement^>(element)->Name== name){return dynamic_cast<T>(element);}int childcount =Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(element);for(int i =0; i < childcount;++i){auto childElement =FindVisualChildByType<T>(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 de un tipo dado contenido 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ú, por lo que logré recopilar 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 de cualquiera que continúe leyendo hasta aquí.
publicstaticvoidFindVisualChildren<T>(thisICollection<T> children,DependencyObject depObj)where T :DependencyObject{if(depObj !=null){var brethren =LogicalTreeHelper.GetChildren(depObj);var brethrenOfType =LogicalTreeHelper.GetChildren(depObj).OfType<T>();foreach(var childOfType in brethrenOfType){
children.Add(childOfType);}foreach(var rawChild in brethren){if(rawChild isDependencyObject){var child = rawChild asDependencyObject;FindVisualChildren<T>(children, child);}}}}
La respuesta aceptada devuelve los elementos descubiertos más o menos desordenados , siguiendo la primera rama secundaria lo más profundo posible, al tiempo que proporciona los elementos descubiertos en el camino, antes de retroceder y repetir los pasos para las ramas de los árboles aún no analizadas.
Si necesita los elementos descendientes en orden descendente , donde los hijos directos se producirán primero, luego sus hijos, etc., funcionará el siguiente algoritmo:
publicstaticIEnumerable<T>GetVisualDescendants<T>(DependencyObject parent,bool applyTemplates =false)where T :DependencyObject{if(parent ==null||!(child isVisual|| child isVisual3D))yieldbreak;var descendants =newQueue<DependencyObject>();
descendants.Enqueue(parent);while(descendants.Count>0){var currentDescendant = descendants.Dequeue();if(applyTemplates)(currentDescendant asFrameworkElement)?.ApplyTemplate();for(var i =0; i <VisualTreeHelper.GetChildrenCount(currentDescendant); i++){var child =VisualTreeHelper.GetChild(currentDescendant, i);if(child isVisual|| child isVisual3D)
descendants.Enqueue(child);if(child is T foundObject)yieldreturn foundObject;}}}
Los elementos resultantes se ordenarán del más cercano al más alejado. Esto será útil, por ejemplo, si está buscando el elemento hijo más cercano de algún tipo y condición:
var foundElement =GetDescendants<StackPanel>(someElement).FirstOrDefault(o => o.SomeProperty==SomeState);
PublicSharedIteratorFunctionFindVisualChildren(Of T AsDependencyObject)(depObj AsDependencyObject)AsIEnumerable(Of T)If depObj IsNotNothingThenFor i AsInteger=0ToVisualTreeHelper.GetChildrenCount(depObj)-1Dim child AsDependencyObject=VisualTreeHelper.GetChild(depObj, i)If child IsNotNothingAndAlsoTypeOf child Is T ThenYieldDirectCast(child, T)EndIfForEach childOfChild As T InFindVisualChildren(Of T)(child)Yield childOfChild
NextNextEndIfEndFunction
Uso (esto deshabilita todos los cuadros de texto en una ventana):
Respuestas:
Esto debería funcionar
luego enumeras los controles así
fuente
this
antesDependencyObject
=>this DependencyObject depObj
Esta es la manera más fácil:
donde control es el elemento raíz de la ventana.
fuente
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
y luego podría utilizarAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
Adapte la respuesta de @Bryce Kahle para seguir la sugerencia y el uso de @Mathias Lykkegaard Lorenzen
LogicalTreeHelper
.Parece funcionar bien. ;)
(Todavía no verificará los controles de pestañas o las Cuadrículas dentro de GroupBoxes como lo mencionan @Benjamin Berry y @David R respectivamente.) (¡También siguió la sugerencia de @noonand y eliminó el hijo redundante! = Nulo)
fuente
Use las clases auxiliares
VisualTreeHelper
oLogicalTreeHelper
según el árbol que le interese. Ambas proporcionan métodos para obtener los elementos secundarios de un elemento (aunque la sintaxis difiere un poco). A menudo uso estas clases para encontrar la primera aparición de un tipo específico, pero podría modificarlo fácilmente para encontrar todos los objetos de ese tipo:fuente
Descubrí que la línea,
VisualTreeHelper.GetChildrenCount(depObj);
utilizada en varios ejemplos anteriores no devuelve un recuento distinto de cero paraGroupBox
es, en particular, dondeGroupBox
contieneGrid
losGrid
elementos a y los elementos secundarios. Creo que esto puede deberse a queGroupBox
no se permite que contenga más de un hijo, y esto se almacena en suContent
propiedad. No hayGroupBox.Children
tipo de propiedad. Estoy seguro de que no hice esto de manera muy eficiente, pero modifiqué el primer ejemplo "FindVisualChildren" en esta cadena de la siguiente manera:fuente
Para obtener una lista de todos los hijos de un tipo específico, puede usar:
fuente
Pequeño cambio en la recursividad para que, por ejemplo, pueda encontrar el control de pestaña secundaria de un control de pestaña.
fuente
Aquí hay otra versión compacta, con la sintaxis genérica:
fuente
Y así es como funciona hacia arriba
fuente
Tenga en cuenta que el uso de VisualTreeHelper solo funciona en controles que se derivan de Visual o Visual3D. Si también necesita inspeccionar otros elementos (por ejemplo, TextBlock, FlowDocument, etc.), el uso de VisualTreeHelper arrojará una excepción.
Aquí hay una alternativa que recurre al árbol lógico si es necesario:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
fuente
Quería 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 capturará objetos XAML "Button". Si reinicializa el objeto "TextBlock" escribiendo en el parámetro Textblock.Text, ya no podrá cambiar el texto del botón con el parámetro Button.Content. El botón mostrará permanentemente el texto escrito desde Textblock. Acción de escritura de texto (desde cuando se recuperó -
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 recopila mediante una búsqueda de "TextBlock".
fuente
Mi versión para C ++ / CLI
fuente
Por alguna razón, ninguna de las respuestas publicadas aquí me ayudó a obtener todos los controles de un tipo dado contenido 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ú, por lo que logré recopilar 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 de cualquiera que continúe leyendo hasta aquí.
Espero eso ayude.
fuente
La respuesta aceptada devuelve los elementos descubiertos más o menos desordenados , siguiendo la primera rama secundaria lo más profundo posible, al tiempo que proporciona los elementos descubiertos en el camino, antes de retroceder y repetir los pasos para las ramas de los árboles aún no analizadas.
Si necesita los elementos descendientes en orden descendente , donde los hijos directos se producirán primero, luego sus hijos, etc., funcionará el siguiente algoritmo:
Los elementos resultantes se ordenarán del más cercano al más alejado. Esto será útil, por ejemplo, si está buscando el elemento hijo más cercano de algún tipo y condición:
fuente
child
es indefinido.@Bryce, muy buena respuesta.
Versión VB.NET:
Uso (esto deshabilita todos los cuadros de texto en una ventana):
fuente
Lo encontré más fácil sin Visual Tree Helpers:
fuente