c# - tutorial - wpf que es
Encuentra todos los controles en la ventana WPF por tipo (15)
Adapté la respuesta de @Bryce Kahle para seguir la sugerencia de @Mathias Lykkegaard Lorenzen y usar 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 mencionaron @Benjamin Berry y @David R respectivamente.) (También siguió la sugerencia de @noonand y eliminó al niño redundante! = Nulo)
Estoy buscando una manera de encontrar todos los controles en Windows por su tipo,
por ejemplo: encuentre todos los TextBoxes
, encuentre todos los controles implementando una interfaz específica, etc.
Aquí hay otra versión más compacta, con la sintaxis de los genéricos:
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;
}
}
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 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 hijo, y esto se almacena en su propiedad de Contenido. No hay ningún tipo de propiedad GroupBox.Children. Estoy seguro de que no lo hice de manera muy eficiente, pero modifiqué el primer ejemplo de "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;
}
}
}
}
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.
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;
}
}
}
}
entonces enumeras los controles como tal
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
Lo encontré más fácil sin los Ayudantes de Visual Tree:
foreach (UIElement element in MainWindow.Children) {
if (element is TextBox) {
if ((element as TextBox).Text != "")
{
//Do something
}
}
};
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;
};
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 TextBoxes en una ventana):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
tb.IsEnabled = False
Next
Para obtener una lista de todos los hijos de un tipo específico, puede utilizar:
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 recursión para que, por ejemplo, pueda encontrar el control de pestaña secundario 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;
}
Por alguna razón, ninguna de las respuestas publicadas aquí me ayudó a obtener todos los controles de un 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ú, por lo que pude recopilar solo la primera línea de ellos usando cualquiera de los códigos anteriores. Este método de extensión es mi solución para el problema para cualquier persona 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.
Quería agregar un comentario, pero tengo menos de 50 puntos por lo que solo puedo "Responder". Tenga en cuenta que si utiliza el método "VisualTreeHelper" para recuperar los objetos "TextBlock" de XAML, también tomará los objetos "Button" de XAML. Si reinicializa el objeto "TextBlock" al escribir en el parámetro Textblock.Text, ya no podrá cambiar el texto del botón usando el parámetro Button.Content. El botón mostrará permanentemente el texto escrito en él desde la acción de escritura Textblock.Text (desde el momento en que 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 solucionar esto, puede intentar usar un "TextBox" de XAML y agregar métodos (o eventos) para imitar un botón de XAMAL. XAML "TextBox" no se recopila mediante una búsqueda de "TextBlock".
Tenga en cuenta que usar VisualTreeHelper solo funciona en los controles que se derivan de Visual o Visual3D. Si también necesita inspeccionar otros elementos (por ejemplo, TextBlock, FlowDocument, etc.), usar VisualTreeHelper generará 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
Use las clases de ayuda VisualTreeHelper
o LogicalTreeHelper
según el tree que esté interesado. Ambos proporcionan métodos para obtener los hijos de un elemento (aunque la sintaxis difiere un poco). A menudo utilizo 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;
}
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);
}
}
}
}