c# - formulario - ¿Cómo obtener TODOS los controles secundarios de una forma de Windows Forms de un tipo específico(Button/Textbox)?
controles de windows forms (23)
Necesito obtener todos los controles en un formulario que son de tipo x. Estoy bastante seguro de haber visto ese código una vez en el pasado que usó algo como esto:
dim ctrls() as Control
ctrls = Me.Controls(GetType(TextBox))
Sé que puedo repetir todos los controles para que los niños utilicen una función recursiva, pero ¿hay algo más sencillo o más directo, tal vez como el siguiente?
Dim Ctrls = From ctrl In Me.Controls Where ctrl.GetType Is Textbox
Aquí está la Solución.
https://.com/a/19224936/1147352
He escrito este fragmento de código y seleccioné solo los paneles, puede agregar más interruptores o ifs. en eso
Aquí está mi método de extensión para Control
, usando LINQ, como una adaptación de la versión de @PsychoCoder :
En su lugar, lleva una lista de tipo que le permite no necesitar varias llamadas de GetAll
para obtener lo que desea. Actualmente lo uso como una versión de sobrecarga.
public static IEnumerable<Control> GetAll(this Control control, IEnumerable<Type> filteringTypes)
{
var ctrls = control.Controls.Cast<Control>();
return ctrls.SelectMany(ctrl => GetAll(ctrl, filteringTypes))
.Concat(ctrls)
.Where(ctl => filteringTypes.Any(t => ctl.GetType() == t));
}
Uso:
// The types you want to select
var typeToBeSelected = new List<Type>
{
typeof(TextBox)
, typeof(MaskedTextBox)
, typeof(Button)
};
// Only one call
var allControls = MyControlThatContainsOtherControls.GetAll(typeToBeSelected);
// Do something with it
foreach(var ctrl in allControls)
{
ctrl.Enabled = true;
}
Aquí está mi método de extensión. Es muy eficiente y es flojo.
Uso:
var checkBoxes = tableLayoutPanel1.FindChildControlsOfType<CheckBox>();
foreach (var checkBox in checkBoxes)
{
checkBox.Checked = false;
}
El código es:
public static IEnumerable<TControl> FindChildControlsOfType<TControl>(this Control control) where TControl : Control
{
foreach (var childControl in control.Controls.Cast<Control>())
{
if (childControl.GetType() == typeof(TControl))
{
yield return (TControl)childControl;
}
else
{
foreach (var next in FindChildControlsOfType<TControl>(childControl))
{
yield return next;
}
}
}
}
Aquí hay otra opción para ti. Lo probé creando una aplicación de muestra, luego puse un GroupBox y un GroupBox dentro del GroupBox inicial. Dentro del GroupBox anidado pongo 3 controles TextBox y un botón. Este es el código que utilicé (incluso incluye la recursión que estabas buscando)
public IEnumerable<Control> GetAll(Control control,Type type)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetAll(ctrl,type))
.Concat(controls)
.Where(c => c.GetType() == type);
}
Para probarlo en el evento de carga de formulario, quería un recuento de todos los controles dentro del GroupBox inicial
private void Form1_Load(object sender, EventArgs e)
{
var c = GetAll(this,typeof(TextBox));
MessageBox.Show("Total Controls: " + c.Count());
}
Y devolvió el recuento correcto cada vez, así que creo que esto funcionará perfectamente para lo que estás buscando :)
Aquí hay una solución genérica probada y funcional:
Tengo un gran número de controles UpDownNumeric, algunos en el formulario principal, algunos en grupos dentro del formulario. Solo quiero que el último control seleccionado cambie el color de fondo a verde, para lo cual primero configuré todos los demás en blanco, usando este método: (también puede expandirse a los nietos)
public void setAllUpDnBackColorWhite()
{
//To set the numericUpDown background color of the selected control to white:
//and then the last selected control will change to green.
foreach (Control cont in this.Controls)
{
if (cont.HasChildren)
{
foreach (Control contChild in cont.Controls)
if (contChild.GetType() == typeof(NumericUpDown))
contChild.BackColor = Color.White;
}
if (cont.GetType() == typeof(NumericUpDown))
cont.BackColor = Color.White;
}
}
Aunque otros usuarios han publicado soluciones adecuadas, me gustaría publicar un enfoque más general que pueda ser más útil.
Esto se basa principalmente en la respuesta de JYelton.
public static IEnumerable<Control> AllControls(
this Control control,
Func<Control, Boolean> filter = null)
{
if (control == null)
throw new ArgumentNullException("control");
if (filter == null)
filter = (c => true);
var list = new List<Control>();
foreach (Control c in control.Controls) {
list.AddRange(AllControls(c, filter));
if (filter(c))
list.Add(c);
}
return list;
}
Combiné un montón de las ideas anteriores en un método de extensión. Los beneficios aquí son que obtiene el elemento enumerable correctamente tipeado, además OfType()
maneja correctamente la OfType()
.
public static IEnumerable<T> FindAllChildrenByType<T>(this Control control)
{
IEnumerable<Control> controls = control.Controls.Cast<Control>();
return controls
.OfType<T>()
.Concat<T>(controls.SelectMany<Control, T>(ctrl => FindAllChildrenByType<T>(ctrl)));
}
En C # (ya que lo etiquetó como tal) podría usar una expresión LINQ como esta:
List<Control> c = Controls.OfType<TextBox>().Cast<Control>().ToList();
Editar para la recursión:
En este ejemplo, primero crea la lista de controles y luego llama a un método para poblarlo. Como el método es recursivo, no devuelve la lista, solo la actualiza.
List<Control> ControlList = new List<Control>();
private void GetAllControls(Control container)
{
foreach (Control c in container.Controls)
{
GetAllControls(c);
if (c is TextBox) ControlList.Add(c);
}
}
Es posible hacer esto en una declaración LINQ utilizando la función Descendants
, aunque no estoy tan familiarizado con ella. Vea esta página para más información sobre eso.
Edite 2 para devolver una colección:
Como sugirió @ProfK, un método que simplemente devuelve los controles deseados es probablemente una mejor práctica. Para ilustrar esto, he modificado el código de la siguiente manera:
private IEnumerable<Control> GetAllTextBoxControls(Control container)
{
List<Control> controlList = new List<Control>();
foreach (Control c in container.Controls)
{
controlList.AddRange(GetAllTextBoxControls(c));
if (c is TextBox)
controlList.Add(c);
}
return controlList;
}
Esta es una versión mejorada de GetAllControls recursivo () que realmente funciona en vars privados:
private void Test()
{
List<Control> allTextboxes = GetAllControls(this);
}
private List<Control> GetAllControls(Control container, List<Control> list)
{
foreach (Control c in container.Controls)
{
if (c is TextBox) list.Add(c);
if (c.Controls.Count > 0)
list = GetAllControls(c, list);
}
return list;
}
private List<Control> GetAllControls(Control container)
{
return GetAllControls(container, new List<Control>());
}
Esto puede funcionar:
Public Function getControls(Of T)() As List(Of T)
Dim st As New Stack(Of Control)
Dim ctl As Control
Dim li As New List(Of T)
st.Push(Me)
While st.Count > 0
ctl = st.Pop
For Each c In ctl.Controls
st.Push(CType(c, Control))
If c.GetType Is GetType(T) Then
li.Add(CType(c, T))
End If
Next
End While
Return li
End Function
Creo que la función para obtener todos los controles de los que está hablando solo está disponible para WPF .
Me gustaría enmendar la respuesta de PsychoCoders: como el usuario quiere obtener todos los controles de un cierto tipo, podríamos usar los genéricos de la siguiente manera:
public IEnumerable<T> FindControls<T>(Control control) where T : Control
{
// we can''t cast here because some controls in here will most likely not be <T>
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => FindControls<T>(ctrl))
.Concat(controls)
.Where(c => c.GetType() == typeof(T)).Cast<T>();
}
De esta manera, podemos llamar a la función de la siguiente manera:
private void Form1_Load(object sender, EventArgs e)
{
var c = FindControls<TextBox>(this);
MessageBox.Show("Total Controls: " + c.Count());
}
Modifiqué desde @PsychoCoder. Todos los controles se pueden encontrar ahora (incluir anidados).
public static IEnumerable<T> GetChildrens<T>(Control control)
{
var type = typeof (T);
var allControls = GetAllChildrens(control);
return allControls.Where(c => c.GetType() == type).Cast<T>();
}
private static IEnumerable<Control> GetAllChildrens(Control control)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(c => GetAllChildrens(c))
.Concat(controls);
}
No olvide que también puede tener un TextBox dentro de otros controles que no sean controles de contenedor. Incluso puede agregar un TextBox a un PictureBox.
Entonces también debe verificar si
someControl.HasChildren = True
en cualquier función recursiva
Este es el resultado que obtuve de un diseño para probar este código:
TextBox13 Parent = Panel5
TextBox12 Parent = Panel5
TextBox9 Parent = Panel2
TextBox8 Parent = Panel2
TextBox16 Parent = Panel6
TextBox15 Parent = Panel6
TextBox14 Parent = Panel6
TextBox10 Parent = Panel3
TextBox11 Parent = Panel4
TextBox7 Parent = Panel1
TextBox6 Parent = Panel1
TextBox5 Parent = Panel1
TextBox4 Parent = Form1
TextBox3 Parent = Form1
TextBox2 Parent = Form1
TextBox1 Parent = Form1
tbTest Parent = myPicBox
Pruebe esto con un botón y un RichTextBox en un formulario.
Option Strict On
Option Explicit On
Option Infer Off
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim pb As New PictureBox
pb.Name = "myPicBox"
pb.BackColor = Color.Goldenrod
pb.Size = New Size(100, 100)
pb.Location = New Point(0, 0)
Dim tb As New TextBox
tb.Name = "tbTest"
pb.Controls.Add(tb)
Me.Controls.Add(pb)
Dim textBoxList As New List(Of Control)
textBoxList = GetAllControls(Of TextBox)(Me)
Dim sb As New System.Text.StringBuilder
For index As Integer = 0 To textBoxList.Count - 1
sb.Append(textBoxList.Item(index).Name & " Parent = " & textBoxList.Item(index).Parent.Name & System.Environment.NewLine)
Next
RichTextBox1.Text = sb.ToString
End Sub
Private Function GetAllControls(Of T)(ByVal searchWithin As Control) As List(Of Control)
Dim returnList As New List(Of Control)
If searchWithin.HasChildren = True Then
For Each ctrl As Control In searchWithin.Controls
If TypeOf ctrl Is T Then
returnList.Add(ctrl)
End If
returnList.AddRange(GetAllControls(Of T)(ctrl))
Next
ElseIf searchWithin.HasChildren = False Then
For Each ctrl As Control In searchWithin.Controls
If TypeOf ctrl Is T Then
returnList.Add(ctrl)
End If
returnList.AddRange(GetAllControls(Of T)(ctrl))
Next
End If
Return returnList
End Function
End Class
Para cualquiera que busque una versión VB del código C # de Adam escrito como una extensión de la clase Control
:
'''''' <summary>Collects child controls of the specified type or base type within the passed control.</summary>
'''''' <typeparam name="T">The type of child controls to include. Restricted to objects of type Control.</typeparam>
'''''' <param name="Parent">Required. The parent form control.</param>
'''''' <returns>An object of type IEnumerable(Of T) containing the control collection.</returns>
'''''' <remarks>This method recursively calls itself passing child controls as the parent control.</remarks>
<Extension()>
Public Function [GetControls](Of T As Control)(
ByVal Parent As Control) As IEnumerable(Of T)
Dim oControls As IEnumerable(Of Control) = Parent.Controls.Cast(Of Control)()
Return oControls.SelectMany(Function(c) GetControls(Of T)(c)).Concat(oControls.Where(Function(c) c.GetType() Is GetType(T) Or c.GetType().BaseType Is GetType(T))
End Function
NOTA: He agregado coincidencia de BaseType
para cualquier control personalizado derivado. Puede eliminar esto o incluso convertirlo en un parámetro opcional si lo desea.
Uso
Dim oButtons As IEnumerable(Of Button) = Me.GetControls(Of Button)()
Puede ser la técnica antigua, pero funciona como el encanto. Usé recursión para cambiar el color de todas las etiquetas del control. Funciona muy bien.
internal static void changeControlColour(Control f, Color color)
{
foreach (Control c in f.Controls)
{
// MessageBox.Show(c.GetType().ToString());
if (c.HasChildren)
{
changeControlColour(c, color);
}
else
if (c is Label)
{
Label lll = (Label)c;
lll.ForeColor = color;
}
}
}
Puede usar una consulta LINQ para hacer esto. Esto consultará todo en el formulario que es tipo TextBox
var c = from controls in this.Controls.OfType<TextBox>()
select controls;
Puedes probar esto si quieres :)
private void ClearControls(Control.ControlCollection c)
{
foreach (Control control in c)
{
if (control.HasChildren)
{
ClearControls(control.Controls);
}
else
{
if (control is TextBox)
{
TextBox txt = (TextBox)control;
txt.Clear();
}
if (control is ComboBox)
{
ComboBox cmb = (ComboBox)control;
if (cmb.Items.Count > 0)
cmb.SelectedIndex = -1;
}
if (control is CheckBox)
{
CheckBox chk = (CheckBox)control;
chk.Checked = false;
}
if (control is RadioButton)
{
RadioButton rdo = (RadioButton)control;
rdo.Checked = false;
}
if (control is ListBox)
{
ListBox listBox = (ListBox)control;
listBox.ClearSelected();
}
}
}
}
private void btnClear_Click(object sender, EventArgs e)
{
ClearControls((ControlCollection)this.Controls);
}
Una solución limpia y fácil (C #):
static class Utilities {
public static List<T> GetAllControls<T>(this Control container) where T : Control {
List<T> controls = new List<T>();
if (container.Controls.Count > 0) {
controls.AddRange(container.Controls.OfType<T>());
foreach (Control c in container.Controls) {
controls.AddRange(c.GetAllControls<T>());
}
}
return controls;
}
}
Obtener todos los cuadros de texto:
List<TextBox> textboxes = myControl.GetAllControls<TextBox>();
Usando la reflexión:
// Return a list with all the private fields with the same type
List<T> GetAllControlsWithTypeFromControl<T>(Control parentControl)
{
List<T> retValue = new List<T>();
System.Reflection.FieldInfo[] fields = parentControl.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
foreach (System.Reflection.FieldInfo field in fields)
{
if (field.FieldType == typeof(T))
retValue.Add((T)field.GetValue(parentControl));
}
}
List<TextBox> ctrls = GetAllControlsWithTypeFromControl<TextBox>(this);
public IEnumerable<T> GetAll<T>(Control control) where T : Control
{
var type = typeof(T);
var controls = control.Controls.Cast<Control>().ToArray();
foreach (var c in controls.SelectMany(GetAll<T>).Concat(controls))
if (c.GetType() == type) yield return (T)c;
}
public static IEnumerable<T> GetAllControls<T>(this Control control) where T : Control
{
foreach (Control c in control.Controls)
{
if (c is T)
yield return (T)c;
foreach (T c1 in c.GetAllControls<T>())
yield return c1;
}
}
IEnumerable<Control> Ctrls = from Control ctrl in Me.Controls where ctrl is TextBox | ctrl is GroupBox select ctr;
Expresiones Lambda
IEnumerable<Control> Ctrls = Me.Controls.Cast<Control>().Where(c => c is Button | c is GroupBox);
public List<Control> GetAllChildControls(Control Root, Type FilterType = null)
{
List<Control> AllChilds = new List<Control>();
foreach (Control ctl in Root.Controls) {
if (FilterType != null) {
if (ctl.GetType == FilterType) {
AllChilds.Add(ctl);
}
} else {
AllChilds.Add(ctl);
}
if (ctl.HasChildren) {
GetAllChildControls(ctl, FilterType);
}
}
return AllChilds;
}