Bagaimana cara mendapatkan SEMUA kontrol anak dari bentuk Windows dari tipe tertentu (Tombol / Kotak Teks)?


120

Saya perlu mendapatkan semua kontrol pada formulir yang berjenis x. Saya cukup yakin saya pernah melihat kode itu di masa lalu yang menggunakan sesuatu seperti ini:

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

Saya tahu saya dapat mengulangi semua kontrol yang membuat anak-anak menggunakan fungsi rekursif, tetapi apakah ada sesuatu yang lebih mudah atau lebih langsung, mungkin seperti berikut ini?

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

Jawaban:


232

Berikut opsi lain untuk Anda. Saya mengujinya dengan membuat aplikasi sampel, saya kemudian meletakkan GroupBox dan GroupBox di dalam GroupBox awal. Di dalam GroupBox bersarang saya meletakkan 3 kontrol TextBox dan sebuah tombol. Ini adalah kode yang saya gunakan (bahkan termasuk rekursi yang Anda cari)

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

Untuk mengujinya dalam acara pemuatan formulir, saya ingin menghitung semua kontrol di dalam GroupBox awal

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

Dan itu mengembalikan hitungan yang tepat setiap kali, jadi saya pikir ini akan berfungsi dengan baik untuk apa yang Anda cari :)


21
GetAll () yang didefinisikan di sini adalah kandidat yang sangat baik untuk metode ekstensi ke kelas Kontrol
Michael Bahig

Saya menyukai cara Anda menggunakan ekspresi lambda. Di mana mempelajari ekspresi lambda secara detail?
Aditya Bokade

"'System.Windows.Forms.Control.ControlCollection' tidak berisi definisi untuk 'Cast' dan tidak ada metode ekstensi 'Cast' yang menerima argumen pertama tipe 'System.Windows.Forms.Control.ControlCollection' dapat ditemukan (adalah Anda kehilangan petunjuk penggunaan atau referensi assembly?) "Saya menggunakan .NET 4.5 dan" Controls "tidak memiliki fungsi / metode / apa pun" Cast ". Apa yang saya lewatkan?
Soulblazer

2
@soulblazer Tambahkan namespace System.Linq.
Ivan-Mark Debono

var allCtl = GetAll (this.FindForm (), typeof (TextBox)); // ini adalah Usercontrol yang mengembalikan Nothing !!
bh_earth0

33

Di C # (karena Anda menandainya seperti itu) Anda bisa menggunakan ekspresi LINQ seperti ini:

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

Edit untuk rekursi:

Dalam contoh ini, Anda terlebih dahulu membuat daftar kontrol dan kemudian memanggil metode untuk mengisinya. Karena metode ini rekursif, itu tidak mengembalikan daftar, itu hanya memperbaruinya.

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

Hal ini dimungkinkan untuk melakukan ini dalam satu pernyataan LINQ menggunakan Descendantsfungsi tersebut, meskipun saya tidak begitu terbiasa dengannya. Lihat halaman ini untuk informasi lebih lanjut tentang itu.

Edit 2 untuk mengembalikan koleksi:

Seperti yang disarankan @ProfK, metode yang hanya mengembalikan kontrol yang diinginkan mungkin merupakan praktik yang lebih baik. Untuk mengilustrasikan ini, saya telah memodifikasi kode sebagai berikut:

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

Terima kasih, C # atau VB baik-baik saja untuk saya. Tetapi masalahnya adalah bahwa Controls.OfType <TExtbox> hanya mengembalikan anak dari kontrol saat ini (dalam kasus saya Formulir), dan saya ingin dalam satu panggilan untuk mendapatkan SEMUA kontrol di Forma "secara rekursif" (chiilds, sub-childs , sub-sub-anak, .....) dalam satu koleksi.
Luis

Saya mengharapkan metode yang disebut GetAllControls untuk mengembalikan kumpulan kontrol, yang akan saya tetapkan ke ControlList. Sepertinya latihan yang lebih baik.
ProfK

@ProfK Saya setuju dengan Anda; mengubah contoh sesuai.
JYelton

13

Ini adalah versi perbaikan dari GetAllControls rekursif () yang benar-benar berfungsi pada vars pribadi:

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

10

Saya menggabungkan banyak ide sebelumnya menjadi satu metode ekstensi. Manfaatnya di sini adalah Anda mendapatkan kembali yang dapat diketik dengan benar, ditambah warisan ditangani dengan benar oleh 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)));
}

5

Anda dapat menggunakan kueri LINQ untuk melakukan ini. Ini akan menanyakan semua yang ada di formulir yaitu tipe TextBox

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

Terima kasih, tapi problema yang sama dengan jawabannya, itu hanya mengembalikan anak-anak tetapi bukan sub-anak, dll, dan saya ingin semua kontrol ensted. Saya cukup yakin saya melihat bahwa itu mungkin dengan satu metode panggilan yang baru di .NET 3.5 atau 4.0, ingat saya melihatnya di demo di suatu tempat
Luis

Mengabaikan kurangnya rekursi, bukankah akan var c = this.Controls.OfType<TextBox>()memberikan hasil yang sama?
CoderDennis

2
@ Dennis: Ya, itu masalah preferensi (biasanya). Lihat stackoverflow.com/questions/214500/… untuk diskusi menarik tentang masalah tersebut.
JYelton

5

Ini mungkin teknik kuno, tetapi bekerja seperti pesona. Saya menggunakan rekursi untuk mengubah warna semua label kontrol. Ini bekerja dengan baik.

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

4

Saya ingin mengubah jawaban PsychoCoders: karena pengguna ingin mendapatkan semua kontrol dari tipe tertentu, kita dapat menggunakan obat generik dengan cara berikut:

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

Dengan cara ini, kita dapat memanggil fungsinya sebagai berikut:

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

ini adalah solusi terbaik (dan tercepat menurut pengujian saya) menurut pendapat saya di halaman ini. Tetapi saya menyarankan agar Anda mengubah kontrol ke array: var enumerable = controls sebagai Control [] ?? control.ToArray (); dan kemudian ubah ke: return enumerable.SelectMany (FindControls <T>) .Concat (enumerable) .Where (c => c.GetType () == typeof (T)). Cast <T> ();
Randall Flagg

Bukankah lebih efisien menggunakan .OfType<T>()metode Linq daripada .Where(c => c.GetType() == typeof(T)).Cast<T>();mendapatkan efek yang sama?
TheHitchenator

3

Jangan lupa bahwa Anda juga dapat memiliki TextBox dalam kontrol lain selain kontrol container juga. Anda bahkan dapat menambahkan TextBox ke PictureBox.

Jadi, Anda juga perlu memeriksa apakah

someControl.HasChildren = True

dalam fungsi rekursif apa pun.

Ini adalah hasil yang saya peroleh dari tata letak untuk menguji kode ini:

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

Coba ini dengan satu Button dan satu RichTextBox pada formulir.

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

2

Menggunakan refleksi:

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

Berikut adalah metode ekstensi saya untuk Control, menggunakan LINQ, sebagai adaptasi dari versi @PsychoCoder :

Dibutuhkan daftar jenis alih-alih yang memungkinkan Anda tidak memerlukan banyak panggilan GetAlluntuk mendapatkan apa yang Anda inginkan. Saat ini saya menggunakannya sebagai versi yang kelebihan beban.

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

Pemakaian:

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

2

Solusi bersih dan mudah (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;
    }
}

Dapatkan semua kotak teks:

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

2

Anda dapat menggunakan Kode di bawah ini

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

2

Berikut adalah metode Ekstensi saya. Sangat efisien dan malas.

Pemakaian:

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

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

Kodenya adalah:

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

ini adalah versi lebih bersih yang malas, dapat dihitung dan diambil sesuai permintaan.
Jone Polvora


1
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
   IEnumerable<Control> Ctrls = from Control ctrl in Me.Controls where ctrl is TextBox | ctrl is GroupBox select ctr;

Ekspresi Lambda

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

Harap tambahkan lebih banyak pada jawaban Anda yang menjelaskan apa yang terjadi dan bagaimana hal itu terkait dengan pertanyaan.
Fencer04

0

Saya memodifikasi dari @PsychoCoder. Semua kontrol dapat ditemukan sekarang (termasuk bersarang).

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

Ini mungkin berhasil:

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

Saya pikir fungsi untuk mendapatkan semua kontrol yang Anda bicarakan hanya tersedia untuk WPF .


0

Berikut ini solusi umum yang teruji dan berfungsi:

Saya memiliki sejumlah besar kontrol UpDownNumeric, beberapa di formulir utama, beberapa di kotak grup di dalam formulir. Saya hanya ingin satu kontrol yang terakhir dipilih untuk mengubah warna belakang menjadi hijau, yang pertama-tama saya atur semua yang lain menjadi putih, menggunakan metode ini: (juga dapat diperluas ke cucu)

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

Ini tidak berfungsi jika kontrol anak memiliki anak sendiri.
Soulblazer

0

Anda dapat mencoba ini jika Anda mau :)

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

1
Hanya memposting kode tidak banyak membantu OP memahami masalah mereka atau solusi Anda. Anda hampir SELALU harus menyertakan semacam penjelasan untuk menyertai kode Anda.
leigero

Pertanyaan itu tidak mengatakan apa-apa tentang membersihkan formulir.
LarsTech

Ya, tidak menjawab "pertanyaan", tetapi merupakan tambahan yang bagus. Terima kasih!

0

Meskipun beberapa pengguna lain telah memposting solusi yang memadai, saya ingin memposting pendekatan yang lebih umum yang mungkin lebih berguna.

Ini sebagian besar didasarkan pada tanggapan 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
    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
    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

Untuk siapa pun yang mencari versi VB dari kode C # Adam yang ditulis sebagai perpanjangan dari Controlkelas:

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

CATATAN: Saya telah menambahkan BaseTypepencocokan untuk setiap kontrol kustom turunan. Anda dapat menghapus ini atau bahkan menjadikannya sebagai parameter opsional jika Anda mau.

Pemakaian

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

0

Buat Metode

public static IEnumerable<Control> GetControlsOfType<T>(Control control)
{
    var controls = control.Controls.Cast<Control>();
    return controls.SelectMany(ctrl => GetControlsOfType<T>(ctrl)).Concat(controls).Where(c => c is T);
}

Dan gunakan seperti itu

Var controls= GetControlsOfType<TextBox>(this);//You can replace this with your control

0

Saya asin menggunakan VB jadi, saya menulis metode ekstensi. Itu mengambil semua anak dan sub anak dari sebuah kontrol

Imports System.Runtime.CompilerServices
Module ControlExt

<Extension()>
Public Function GetAllChildren(Of T As Control)(parentControl As Control) As IEnumerable(Of T)
    Dim controls = parentControl.Controls.Cast(Of Control)
    Return controls.SelectMany(Of Control)(Function(ctrl) _
        GetAllChildren(Of T)(ctrl)) _
        .Concat(controls) _
        .Where(Function(ctrl) ctrl.GetType() = GetType(T)) _
    .Cast(Of T)
End Function

End Module

Kemudian Anda dapat menggunakannya seperti, di mana "btnList" adalah kontrol

btnList.GetAllChildren(Of HtmlInputRadioButton).FirstOrDefault(Function(rb) rb.Checked)

Dalam hal ini, ini akan memilih tombol radio yang dipilih.


-1

Secara sederhana:

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

Ini hanya akan menemukan kontrol secara langsung dalam koleksi kontrol "Saya" dan tidak menemukan kontrol Tombol yang ada di dalam wadah turunan mana pun seperti yang coba disiratkan oleh poster oleh "SEMUA".
ChrisPBacon
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.