특정 형식 (버튼 / 텍스트 상자)의 Windows Forms 양식의 모든 자식 컨트롤을 가져 오는 방법은 무엇입니까?


120

x 유형의 양식에 대한 모든 컨트롤을 가져와야합니다. 나는 과거에 다음과 같은 코드를 사용한 코드를 한 번 보았다고 확신합니다.

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

재귀 함수를 사용하여 자식을 가져 오는 모든 컨트롤을 반복 할 수 있다는 것을 알고 있지만 다음과 같이 더 쉽고 간단한 것이 있습니까?

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

답변:


232

여기에 또 다른 옵션이 있습니다. 샘플 애플리케이션을 만들어 테스트 한 다음 초기 GroupBox 안에 GroupBox와 GroupBox를 넣었습니다. 중첩 된 GroupBox 안에 3 개의 TextBox 컨트롤과 버튼이 있습니다. 이것은 내가 사용한 코드입니다 (찾고 있던 재귀도 포함)

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

양식로드 이벤트에서 테스트하기 위해 초기 GroupBox 내부의 모든 컨트롤 수를 원했습니다.

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

그리고 그것은 매번 적절한 카운트를 반환했기 때문에 이것이 당신이 찾고있는 것에 완벽하게 작동 할 것이라고 생각합니다 :)


21
여기에 정의 GETALL ()는 클래스 컨트롤의 확장 방법에 대한 아주 좋은 후보
마이클 Bahig

람다 식을 사용하는 방식이 마음에 들었습니다. 람다 식을 자세히 배울 수있는 곳은 어디입니까?
Aditya Bokade 2013

" 'System.Windows.Forms.Control.ControlCollection'에 'Cast'에 대한 정의가 포함되어 있지 않으며 'System.Windows.Forms.Control.ControlCollection'유형의 첫 번째 인수를 허용하는 확장 메서드 'Cast'를 찾을 수 없습니다. using 지시문 또는 어셈블리 참조가 누락 되었습니까?) "나는 .NET 4.5에 있고"Controls "에는"Cast "함수 / 메서드 / 어떤 것도 없습니다. 내가 무엇을 놓치고 있습니까?
soulblazer

2
@soulblazer System.Linq 네임 스페이스를 추가합니다.
Ivan-Mark Debono

var allCtl = GetAll (this.FindForm (), typeof (TextBox)); // 이것은 Usercontrol이 아무것도 반환하지 않습니다 !!
bh_earth0

33

C #에서는 태그를 지정 했으므로 다음과 같은 LINQ 식을 사용할 수 있습니다.

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

재귀 편집 :

이 예제에서는 먼저 컨트롤 목록을 만든 다음이를 채우는 메서드를 호출합니다. 메서드가 재귀 적이므로 목록을 반환하지 않고 업데이트 만합니다.

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

Descendants내가 익숙하지는 않지만 함수를 사용하여 하나의 LINQ 문에서이 작업을 수행 할 수 있습니다 . 이 페이지 보기 대한 자세한 내용 를 .

컬렉션을 반환하려면 2를 편집합니다.

@ProfK가 제안했듯이 원하는 컨트롤을 단순히 반환하는 방법이 아마도 더 나은 방법 일 것입니다. 이를 설명하기 위해 다음과 같이 코드를 수정했습니다.

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

감사합니다. C # 또는 VB는 저에게 좋습니다. 그러나 문제는 Controls.OfType <TExtbox>가 현재 컨트롤 (내 경우에는 Form)의 자식 만 반환하고 Forma의 모든 컨트롤을 "재귀 적으로"(chiilds, sub-childs , sub-sub-childs, .....) 하나의 컬렉션에서.
Luis

GetAllControls라는 메서드가 ControlList에 할당 할 컨트롤 컬렉션을 반환 할 것으로 예상합니다. 연습이 더 나은 것 같습니다.
ProfK

@ProfK 동의합니다. 그에 따라 예를 변경합니다.
JYelton

13

이것은 실제로 private vars에서 작동하는 재귀 GetAllControls ()의 개선 된 버전입니다 :

    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

이전의 여러 아이디어를 하나의 확장 방법으로 결합했습니다. 여기서 이점은 올바르게 입력 된 열거 형을 다시 가져오고 상속이 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

LINQ 쿼리를 사용하여이를 수행 할 수 있습니다. 이것은 TextBox 유형 인 양식의 모든 것을 쿼리합니다.

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

고마워, 그러나 거기에 대한 대답과 같은 문제, 그것은 단지 chidls를 반환하고 subchilds는 반환하지 않으며, 나는 모든 ensted 컨트롤을 원합니다. .NET 3.5 또는 4.0의 새로운 단일 메서드 호출로 가능하다는 것을 알았습니다. 데모에서 보았 음을 기억하십시오
Luis

재귀 부족을 무시하면 var c = this.Controls.OfType<TextBox>()같은 결과를 얻지 않겠습니까?
CoderDennis 2010 년

2
@Dennis : 예, 그럴 것입니다. (보통) 선호도의 문제입니다. 이 문제에 대한 흥미로운 토론 은 stackoverflow.com/questions/214500/… 을 참조하십시오 .
JYelton 2010 년

5

고대의 기술일지도 모르지만 매력처럼 작동합니다. 컨트롤의 모든 레이블 색상을 변경하기 위해 재귀를 사용했습니다. 잘 작동합니다.

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 답변을 수정하고 싶습니다. 사용자가 특정 유형의 모든 컨트롤을 얻으려면 다음과 같은 방법으로 제네릭을 사용할 수 있습니다.

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

이렇게하면 다음과 같이 함수를 호출 할 수 있습니다.

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

이 페이지에 대한 제 의견으로는 이것이 최고의 (내 테스트에 따르면 가장 빠른) 솔루션입니다. 하지만 컨트롤을 배열로 변경하는 것이 좋습니다. var enumerable = controls as Control [] ?? controls.ToArray (); 다음으로 변경하십시오. return enumerable.SelectMany (FindControls <T>). Concat (enumerable) .Where (c => c.GetType () == typeof (T)). Cast <T> ();
Randall Flagg 2015

동일한 효과를 얻는 .OfType<T>()것보다 Linq 방법 을 사용하는 것이 더 효율적이지 .Where(c => c.GetType() == typeof(T)).Cast<T>();않습니까?
TheHitchenator 2018 년

3

컨테이너 컨트롤이 아닌 다른 컨트롤 내에도 TextBox를 가질 수 있다는 것을 잊지 마십시오 . PictureBox에 TextBox를 추가 할 수도 있습니다.

따라서 다음 사항도 확인해야합니다.

someControl.HasChildren = True

모든 재귀 함수에서.

이 코드를 테스트하기 위해 레이아웃에서 얻은 결과입니다.

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

폼에 하나의 Button하나의 RichTextBox로 이것을 시도하십시오 .

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

반사 사용 :

// 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 버전 Control의 적응으로 LINQ를 사용 하는에 대한 확장 방법입니다 .

대신 GetAll원하는 것을 얻기 위해 여러 번 호출 할 필요가 없도록하는 유형 목록을 사용합니다 . 현재는 과부하 버전으로 사용하고 있습니다.

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

용법:

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

깨끗하고 쉬운 솔루션 (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;
    }
}

모든 텍스트 상자 가져 오기 :

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

2

아래 코드를 사용할 수 있습니다.

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

내 확장 방법은 다음과 같습니다. 매우 효율적이고 게으르다.

용법:

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

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

코드는 다음과 같습니다.

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

이것은 게으르고 필요에 따라 열거 및 가져올 수있는 더 깨끗한 버전입니다.
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;

람다 식

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

무슨 일이 일어나고 있는지 그리고 그것이 질문과 어떻게 관련되어 있는지 설명하는 답변에 더 추가하십시오.
Fencer04 2016-12-05

0

@PsychoCoder에서 수정했습니다. 이제 모든 컨트롤을 찾을 수 있습니다 (중첩 포함).

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

이것은 작동 할 수 있습니다.

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

나는 당신이 말하는 모든 컨트롤을 얻는 기능은 WPF 에서만 사용할 수 있다고 생각합니다 .


0

다음은 테스트되고 작동하는 일반 솔루션입니다.

많은 수의 UpDownNumeric 컨트롤이 있으며 일부는 기본 양식에 있고 일부는 양식 내의 그룹 상자에 있습니다. 마지막으로 선택한 하나의 컨트롤 만 배경색을 녹색으로 변경하고 먼저이 방법을 사용하여 다른 모든 컨트롤을 흰색으로 설정합니다. (손자에게로 확장 할 수도 있음)

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

자식 컨트롤에 자체 자식이있는 경우에는 작동하지 않습니다.
soulblazer

0

원한다면 이것을 시도 할 수 있습니다 :)

    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
단순히 코드를 게시하는 것은 OP가 문제 또는 솔루션을 이해하는 데 거의 도움이되지 않습니다. 코드에 대한 설명은 거의 항상 포함해야합니다.
leigero

질문은 양식을 지우는 것에 대해 아무것도 말하지 않았습니다.
LarsTech

예, "질문"에 대답하지 않지만 좋은 추가 사항입니다. 감사합니다!

0

다른 여러 사용자가 적절한 솔루션을 게시했지만 더 유용 할 수있는보다 일반적인 접근 방식을 게시하고 싶습니다.

이것은 주로 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

Control클래스 의 확장으로 작성된 Adam의 C # 코드의 VB 버전을 찾는 사람 :

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

참고 : 나는 추가했습니다 BaseType 파생 된 사용자 지정 컨트롤에 대한 일치를 . 이를 제거하거나 원하는 경우 선택적 매개 변수로 만들 수도 있습니다.

용법

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

0

생성 방법

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

그리고 그것을 사용하십시오

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

0

나는 VB를 사용하여 saldy이므로 확장 방법을 작성했습니다. 컨트롤의 모든 자식 및 하위 자식을 검색합니다.

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

그런 다음 "btnList"가 컨트롤 인 것처럼 사용할 수 있습니다.

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

이 경우 선택한 라디오 버튼이 선택됩니다.


-1

간단히:

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

이것은 "Me"의 컨트롤 컬렉션에서만 컨트롤을 직접 찾을 수 있으며 포스터가 "ALL"을 의미하는 것처럼 자식 컨테이너 내에있는 Button 컨트롤은 찾지 못합니다.
ChrisPBacon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.