Estoy buscando una manera de encontrar todos los controles en Windows por su tipo,
por ejemplo: buscar todos TextBoxes
, encontrar todos los controles que implementan una interfaz específica, etc.
Estoy buscando una manera de encontrar todos los controles en Windows por su tipo,
por ejemplo: buscar todos TextBoxes
, encontrar todos los controles que implementan una interfaz específica, etc.
Respuestas:
Esto debería funcionar
public static IEnumerable<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)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
luego enumeras los controles así
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
this
antes DependencyObject
=>this DependencyObject depObj
Esta es la manera más fácil:
IEnumerable<myType> collection = control.Children.OfType<myType>();
donde control es el elemento raíz de la ventana.
<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. ;)
public static IEnumerable<T> FindLogicalChildren<T> ( 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<T>( child ) )
{
yield return 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)
Use las clases auxiliares VisualTreeHelper
o LogicalTreeHelper
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:
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;
}
Descubrí que la línea, VisualTreeHelper.GetChildrenCount(depObj);
utilizada en varios ejemplos anteriores no devuelve un recuento distinto de cero para GroupBox
es, en particular, donde GroupBox
contiene Grid
los Grid
elementos a y los elementos secundarios. Creo que esto puede deberse a que GroupBox
no se permite que contenga más de un hijo, y esto se almacena en su Content
propiedad. No hay GroupBox.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:
public IEnumerable<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)
{
yield return (T)child;
}
if (child is GroupBox)
{
GroupBox gb = child as GroupBox;
Object gpchild = gb.Content;
if (gpchild is T)
{
yield return (T)child;
child = gpchild as T;
}
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
Para obtener una lista de todos los hijos de un tipo específico, puede usar:
private static IEnumerable<DependencyObject> 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 recursividad para que, por ejemplo, pueda encontrar el control de pestaña secundaria de un control de pestaña.
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;
}
Aquí hay otra versión compacta, con la sintaxis genérica:
public static IEnumerable<T> FindLogicalChildren<T>(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<DependencyObject>())
foreach (T c in FindLogicalChildren<T>(child))
yield return c;
}
}
Y así es como funciona hacia arriba
private T FindParent<T>(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<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:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
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 in FindVisualChildren<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".
Mi versión para C ++ / CLI
template < class T, class U >
bool Isinst(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í.
public static void FindVisualChildren<T>(this ICollection<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 is DependencyObject)
{
var child = rawChild as DependencyObject;
FindVisualChildren<T>(children, child);
}
}
}
}
Espero eso ayude.
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:
public static IEnumerable<T> GetVisualDescendants<T>(DependencyObject parent, bool applyTemplates = false)
where T : DependencyObject
{
if (parent == null || !(child is Visual || child is Visual3D))
yield break;
var descendants = new Queue<DependencyObject>();
descendants.Enqueue(parent);
while (descendants.Count > 0)
{
var currentDescendant = descendants.Dequeue();
if (applyTemplates)
(currentDescendant as FrameworkElement)?.ApplyTemplate();
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(currentDescendant); i++)
{
var child = VisualTreeHelper.GetChild(currentDescendant, i);
if (child is Visual || child is Visual3D)
descendants.Enqueue(child);
if (child is T foundObject)
yield return 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);
child
es indefinido.
@Bryce, 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
}
}
};