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
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 :)
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;
}
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>());
}
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)));
}
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;
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;
}
}
}
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());
}
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
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);
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>();
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>());
}
}
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;
}
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);
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
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;
}
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;
}
}
}
}
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;
}
}
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);
}
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);
}
Einfach:
For Each ctrl In Me.Controls.OfType(Of Button)()
ctrl.Text = "Hello World!"
Next
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.
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;
}
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)()
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;
}
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;
}
}