Belirli bir türdeki (Düğme / Metin Kutusu) bir Windows Forms formunun TÜM alt denetimlerini nasıl elde edebilirim?


120

X tipi bir formdaki tüm kontrolleri almam gerekiyor. Geçmişte böyle bir şeyin kullanıldığı kodu gördüğüme oldukça eminim:

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

Özyinelemeli bir işlev kullanarak çocukları elde eden tüm kontrolleri yineleyebileceğimi biliyorum, ancak aşağıdakiler gibi daha kolay veya daha basit bir şey var mı?

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

Yanıtlar:


232

İşte sizin için başka bir seçenek. Örnek bir uygulama oluşturarak test ettim, ardından ilk GroupBox içine bir GroupBox ve bir GroupBox koydum. Yuvalanmış GroupBox'ın içine 3 TextBox denetimi ve bir düğme koydum. Bu, kullandığım koddur (aradığınız özyinelemeyi bile içerir)

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

Form yükleme olayında test etmek için, ilk GroupBox içindeki tüm kontrollerin sayısını istedim

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

Ve her seferinde doğru sayımı döndürdü, bu yüzden aradığınız şey için mükemmel çalışacağını düşünüyorum :)


21
Burada tanımlanan GetAll (), Control sınıfına genişletme yöntemi için çok iyi bir adaydır
Michael Bahig

Lambda ifadelerini kullanma şeklini beğendim. Lambda ifadelerini ayrıntılı olarak nereden öğrenebilirim?
Aditya Bokade

"'System.Windows.Forms.Control.ControlCollection', 'Cast' için bir tanım içermiyor ve 'System.Windows.Forms.Control.ControlCollection' türünde ilk bağımsız değişkeni kabul eden 'Cast' uzantı yöntemi bulunamadı ( kullanma yönergesi veya derleme başvurusu eksik mi?) ".NET 4.5 kullanıyorum ve" Kontroller "'in" Yayınlama "işlevi / yöntemi / ne olursa olsun yok. Neyi kaçırıyorum?
soulblazer

2
@soulblazer System.Linq ad alanını ekleyin.
Ivan-Mark Debono

var allCtl = GetAll (this.FindForm (), typeof (TextBox)); // bu bir Kullanıcı denetimi Hiçbir şey döndürmez !!
bh_earth0

33

C # 'da (bu şekilde etiketlediğinizden beri) aşağıdaki gibi bir LINQ ifadesi kullanabilirsiniz:

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

Özyineleme için düzenle:

Bu örnekte, önce denetim listesini oluşturursunuz ve ardından onu doldurmak için bir yöntem çağırırsınız. Yöntem özyinelemeli olduğu için listeyi döndürmez, sadece günceller.

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

DescendantsBen o kadar aşina olmasam da, bunu işlevi kullanarak bir LINQ deyiminde yapmak mümkün olabilir . Bununla ilgili daha fazla bilgi için bu sayfaya bakın .

Bir koleksiyon iade etmek için 2'yi düzenleyin:

@ ProfK'nın önerdiği gibi, istenen kontrolleri basitçe döndüren bir yöntem muhtemelen daha iyi bir uygulamadır. Bunu açıklamak için kodu aşağıdaki gibi değiştirdim:

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

Teşekkürler, C # veya VB benim için uygundur. Ancak sorun şu ki, Controls.OfType <TExtbox> yalnızca geçerli denetimin alt öğelerini (benim durumumda Form) döndürür ve tek bir çağrıda Forma'daki TÜM denetimleri "özyinelemeli" olarak almak istiyorum (çocuklar, alt çocuklar , sub-sub-childs, .....) tek koleksiyonda.
Luis

GetAllControls adlı bir yöntemin, ControlList'e atayacağım bir denetim koleksiyonu döndürmesini beklerdim. Daha iyi bir pratik gibi görünüyor.
ProfK

@ProfK Size katılıyorum; buna göre değişen örnek.
JYelton

13

Bu, aslında özel değişkenler üzerinde çalışan özyinelemeli GetAllControls () 'nin geliştirilmiş bir sürümüdür:

    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

Önceki fikirlerden bir kısmını tek bir uzatma yönteminde birleştirdim. Buradaki avantajlar, doğru yazılan numaralandırmayı geri almanız ve ayrıca mirasın doğru şekilde işlenmesidir 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

Bunu yapmak için bir LINQ sorgusu kullanabilirsiniz. Bu, TextBox türündeki formdaki her şeyi sorgulayacaktır.

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

Teşekkürler, ancak yanıtla aynı sorun, yalnızca çocukları döndürür, alt çocukları vb. Döndürmez ve tüm kontrolleri istiyorum. NET 3.5 veya 4.0'da yeni olan tek bir yöntem çağrısı ile mümkün olduğunu gördüğümden oldukça eminim, bunu bir şekilde bir demoda gördüğümü hatırlayın
Luis

Özyineleme eksikliğini görmezden gelmek var c = this.Controls.OfType<TextBox>()aynı sonucu vermez mi?
CoderDennis

2
@Dennis: Evet olur, bu bir tercih meselesi (genellikle). Bkz stackoverflow.com/questions/214500/... konuda ilginç bir tartışma için.
JYelton

5

Eski teknik olabilir, ama cazibe gibi işliyor. Denetimin tüm etiketlerinin rengini değiştirmek için özyinelemeyi kullandım. Harika çalışıyor.

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

PsychoCoders cevabını değiştirmek istiyorum: kullanıcı belirli bir türdeki tüm kontrolleri almak istediğinden, jenerikleri aşağıdaki şekilde kullanabiliriz:

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

Bu şekilde fonksiyonu şu şekilde çağırabiliriz:

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

bence bu en iyi (ve testlerime göre en hızlı) çözüm bu sayfadaki. Ama kontrolleri bir diziye değiştirmenizi öneririm: var enumerable = Control as Control [] ?? controls.ToArray (); ve sonra şunu değiştirin: return enumerable.SelectMany (FindControls <T>) .Concat (numaralandırılabilir) .Where (c => c.GetType () == typeof (T)). Cast <T> ();
Randall Flagg

Aynı etkiyi elde .OfType<T>()etmektense Linq yöntemini kullanmak daha verimli değil .Where(c => c.GetType() == typeof(T)).Cast<T>();mi?
TheHitchenator

3

Konteyner kontrolleri dışındaki diğer kontroller içinde de bir TextBox'a sahip olabileceğinizi unutmayın . Bir PictureBox'a bir TextBox bile ekleyebilirsiniz.

Bu yüzden ayrıca kontrol etmelisiniz

someControl.HasChildren = True

herhangi bir özyinelemeli işlevde.

Bu kodu test etmek için bir düzende elde ettiğim sonuç bu:

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

Bunu bir Form üzerinde bir Düğme ve bir RichTextBox ile deneyin .

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

Yansımayı kullanma:

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

@PsychoCoder sürümünün Controlbir uyarlaması olarak LINQ kullanmak için benim genişletme yöntemim :

Bunun yerine, GetAllistediğinizi elde etmek için birden fazla aramaya ihtiyaç duymamanıza izin veren bir tür listesi alır . Şu anda bir aşırı yük versiyonu olarak kullanıyorum.

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

Kullanımı:

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

Temiz ve kolay bir çözüm (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;
    }
}

Tüm metin kutularını al:

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

2

Aşağıdaki kodu kullanabilirsiniz

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

İşte benim Uzatma yöntemim. Çok verimli ve tembel.

Kullanımı:

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

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

Kod şu şekildedir:

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

bu, tembel, numaralandırılabilen ve talep üzerine getirilebilen daha temiz bir versiyondur.
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;

Lambda İfadeleri

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

Lütfen yanıtınıza neler olduğunu ve soruyla nasıl ilgili olduğunu açıklayan daha fazla şey ekleyin.
Fencer04

0

@ PsychoCoder'dan değiştirdim. Tüm kontroller artık bulunabilir (iç içe olanlar dahil).

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

Bu işe yarayabilir:

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

Sanırım bahsettiğiniz tüm kontrolleri alma işlevi yalnızca WPF tarafından kullanılabilir .


0

İşte test edilmiş ve çalışan genel bir çözüm:

Bazıları ana formda, bazıları form içindeki grup kutularında olmak üzere çok sayıda UpDownNumeric denetimim var. Yalnızca son seçilen bir denetimin arka rengi yeşile çevirmesini istiyorum, bunun için önce diğerlerini beyaza ayarlıyorum, bu yöntemi kullanarak: (torunlara da genişletebilir)

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

Çocuk kontrolünün kendi çocukları varsa bu işe yaramaz.
soulblazer

0

İstersen bunu deneyebilirsin :)

    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
Yalnızca kod göndermek, OP'nin sorunlarını veya çözümünüzü anlamasına yardımcı olmak için çok az şey yapar. Neredeyse HER ZAMAN kodunuza eşlik edecek bir tür açıklama eklemelisiniz.
leigero

Soru, formu temizlemekle ilgili hiçbir şey söylemedi.
LarsTech

Evet, "soruya" cevap vermiyor, ama buna güzel bir katkı. Teşekkür ederim!

0

Diğer birkaç kullanıcı yeterli çözümler yayınlamış olsa da, daha yararlı olabilecek daha genel bir yaklaşım yayınlamak istiyorum.

Bu büyük ölçüde JYelton'un cevabına dayanmaktadır.

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

Adam's C # kodunun Controlsınıfın bir uzantısı olarak yazılmış bir VB sürümünü arayanlar için :

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

NOT: BaseTypeTüretilmiş özel kontroller için eşleştirme ekledim . Bunu kaldırabilir veya isterseniz isteğe bağlı bir parametre yapabilirsiniz.

kullanım

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

0

Yöntem Oluştur

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

Ve Gibi Kullan

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

0

VB kullanıyorum, bu yüzden bir uzatma yöntemi yazdım. Bu, bir kontrolün tüm çocuklarını ve alt çocuklarını alır

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

Daha sonra, "btnList" bir denetim olduğu gibi kullanabilirsiniz.

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

Bu durumda, seçilen radyo düğmesini seçecektir.


-1

basitçe:

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

Bu, yalnızca doğrudan "Ben" denetim koleksiyonunda bulunan denetimleri bulacak ve poster "TÜMÜ" ile ima etmeye çalıştığı için herhangi bir alt kapsayıcıda bulunan Düğme denetimlerini bulamayacaktır.
ChrisPBacon
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.