it-swarm.com.de

Wie bekomme ich ALLE untergeordneten Steuerelemente eines Windows Forms-Formulars eines bestimmten Typs (Schaltfläche/Textfeld)?

Ich muss alle Steuerelemente in einem Formular abrufen, die vom Typ x sind. Ich bin mir ziemlich sicher, dass ich diesen Code einmal in der Vergangenheit gesehen habe, in dem so etwas verwendet wurde:

dim ctrls() as Control
ctrls = Me.Controls(GetType(TextBox))

Ich weiß, dass ich über alle Steuerelemente iterieren kann, um Kinder mit einer rekursiven Funktion zu erhalten, aber Gibt es etwas, das einfacher oder unkomplizierter ist, vielleicht wie das Folgende?

Dim Ctrls = From ctrl In Me.Controls Where ctrl.GetType Is Textbox
102
Luis

Hier ist eine weitere Option für Sie. Ich habe es getestet, indem ich eine Beispielanwendung erstellt habe. Dann habe ich eine GroupBox und eine GroupBox in die ursprüngliche GroupBox eingefügt. In die verschachtelte GroupBox habe ich 3 TextBox-Steuerelemente und eine Schaltfläche eingefügt. Dies ist der Code, den ich verwendet habe (beinhaltet sogar die Rekursion, nach der Sie gesucht haben)

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);
}

Zum Testen des Formularladeereignisses wollte ich eine Zählung aller Steuerelemente in der ursprünglichen GroupBox

private void Form1_Load(object sender, EventArgs e)
{
    var c = GetAll(this,typeof(TextBox));
    MessageBox.Show("Total Controls: " + c.Count());
}

Und es hat jedes Mal die richtige Anzahl zurückgegeben, also denke ich, dass dies perfekt für das funktioniert, wonach du suchst :)

196
PsychoCoder

In C # (da Sie es als solches markiert haben), können Sie einen LINQ-Ausdruck wie folgt verwenden:

List<Control> c = Controls.OfType<TextBox>().Cast<Control>().ToList();

Bearbeiten für Rekursion:

In diesem Beispiel erstellen Sie zuerst die Liste der Steuerelemente und rufen dann eine Methode zum Füllen auf. Da die Methode rekursiv ist, gibt sie die Liste nicht zurück, sondern aktualisiert sie nur.

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);
    }
}

Möglicherweise ist dies in einer LINQ-Anweisung mit der Funktion Descendants möglich, obwohl ich damit nicht so vertraut bin. Siehe diese Seite für weitere Informationen dazu.

Edit 2, um eine Sammlung zurückzugeben:

Wie @ProfK vorschlägt, ist eine Methode, die einfach die gewünschten Steuerelemente zurückgibt, wahrscheinlich die bessere Praxis. Um dies zu veranschaulichen, habe ich den Code wie folgt geändert:

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;
}
30
JYelton

Dies ist eine verbesserte Version des rekursiven GetAllControls (), das tatsächlich für private Variablen funktioniert:

    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>());
    }
12
VictorEspina

Ich habe einige der vorherigen Ideen in einer Erweiterungsmethode zusammengefasst. Die Vorteile hier sind, dass Sie die korrekt typisierte Aufzählungsliste zurückerhalten und die Vererbung von OfType() korrekt verarbeitet wird.

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)));
}
8
Entiat

Sie können dazu eine LINQ-Abfrage verwenden. Dadurch wird alles in dem Formular mit dem Typ TextBox abgefragt

var c = from controls in this.Controls.OfType<TextBox>()
              select controls;
7
PsychoCoder

Es ist vielleicht die alte Technik, aber sie wirkt wie ein Zauber. Ich habe Rekursion verwendet, um die Farbe aller Beschriftungen des Steuerelements zu ändern. Es funktioniert super.

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;
            }
    }
}
5
Aditya Bokade

Ich möchte die Antwort von PsychoCoders ändern: Da der Benutzer alle Steuerelemente eines bestimmten Typs erhalten möchte, können wir Generika folgendermaßen verwenden:

    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>();
    }

Auf diese Weise können wir die Funktion wie folgt aufrufen:

private void Form1_Load(object sender, EventArgs e)
{
    var c = FindControls<TextBox>(this);
    MessageBox.Show("Total Controls: " + c.Count());
}
4
Adam

Vergessen Sie nicht, dass Sie auch ein Textfeld in anderen Steuerelementen außer Containercontrollern haben können. Sie können sogar eine Textbox zu einer PictureBox hinzufügen.

Sie müssen also auch prüfen, ob

someControl.HasChildren = True

in jeder rekursiven Funktion.

Dies ist das Ergebnis, das ich von einem Layout hatte, um diesen Code zu testen:

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

Versuchen Sie dies mitone Buttonund one RichTextBox in einem Formular.

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
3

Reflexion verwenden:

// 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);
2
MiMenda

Eine saubere und einfache Lösung (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;
    }
}

Holen Sie sich alle Textfelder:

List<TextBox> textboxes = myControl.GetAllControls<TextBox>();
1
Omar

Sie können den folgenden Code verwenden

public static class ExtensionMethods
{
    public static IEnumerable<T> GetAll<T>(this Control control)
    {
        var controls = control.Controls.Cast<Control>();

        return controls.SelectMany(ctrl => ctrl.GetAll<T>())
                                  .Concat(controls.OfType<T>());
    }
}
1
Santanu Sarkar
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;
}
1
Muhammad
   IEnumerable<Control> Ctrls = from Control ctrl in Me.Controls where ctrl is TextBox | ctrl is GroupBox select ctr;

Lambda-Ausdrücke

IEnumerable<Control> Ctrls = Me.Controls.Cast<Control>().Where(c => c is Button | c is GroupBox);
1
Memo Arfaa

Hier ist die Lösung.

https://stackoverflow.com/a/19224936/1147352

Ich habe dieses Stück Code geschrieben und nur die Panels ausgewählt. Sie können weitere Schalter oder Ifs hinzufügen. drin

1
DareDevil

Hier ist meine Erweiterungsmethode für Control mit LINQ als Anpassung an @PsychoCoder version:

Stattdessen wird eine Liste des Typs benötigt, mit der Sie nicht mehrere Aufrufe von GetAll benötigen, um das zu erhalten, was Sie möchten. Ich verwende es derzeit als Überlastungsversion.

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));
}

Verwendungszweck:

//   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;
}
1
gfache

Hier ist meine Erweiterungsmethode. Es ist sehr effizient und faul.

Verwendungszweck:

var checkBoxes = tableLayoutPanel1.FindChildControlsOfType<CheckBox>();

foreach (var checkBox in checkBoxes)
{
    checkBox.Checked = false;
}

Der Code lautet:

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;
                }
            }
        }
    }
1
Jone Polvora

Hier ist eine getestete und funktionierende generische Lösung:

Ich habe eine große Anzahl von UpDownNumeric-Steuerelementen, einige im Hauptformular, einige in Groupboxen innerhalb des Formulars .. __ Ich möchte, dass nur das zuletzt ausgewählte Steuerelement die Hintergrundfarbe in Grün ändert, für das ich zuerst alle anderen auf Weiß gesetzt habe. mit dieser Methode: (kann auch auf Enkelkinder erweitert werden)

    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;
       }
    }   
0
samtal

Ich habe von @PsychoCoder aus etwas geändert.

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);
}
0
ted

Sie können dies versuchen, wenn Sie wollen :)

    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);
    }
0
rashi

Einfach:

For Each ctrl In Me.Controls.OfType(Of Button)()
   ctrl.Text = "Hello World!"
Next
0
Leebeedev

Das kann funktionieren:

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

Ich denke, die Funktion, um alle Steuerelemente zu erhalten, über die Sie sprechen, ist nur für WPF verfügbar.

0
Alex Rouillard
    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;
    }
0

Für alle, die eine VB -Version von Adams C # -Code suchen, die als Erweiterung der Control-Klasse geschrieben wurde:

''' <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

HINWEIS: Ich habe BaseType für abgeleitete benutzerdefinierte Steuerelemente hinzugefügt. Sie können dies entfernen oder sogar als optionalen Parameter festlegen, wenn Sie möchten.

Verwendungszweck

Dim oButtons As IEnumerable(Of Button) = Me.GetControls(Of Button)()
0
SteveCinq

Obwohl mehrere andere Benutzer angemessene Lösungen bereitgestellt haben, möchte ich einen allgemeineren Ansatz posten, der möglicherweise nützlicher ist. 

Dies basiert weitgehend auf der Antwort von 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;
}
0
JamesFaix
    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;
        }
    }
0
Koray