【问题标题】:How to get ALL child controls of a Windows Forms form of a specific type (Button/Textbox)?如何获取特定类型(按钮/文本框)的 Windows 窗体窗体的所有子控件?
【发布时间】:2020-06-30 15:37:32
【问题描述】:

我需要获取类型为 x 的表单上的所有控件。我很确定我过去曾看到过使用类似这样的代码:

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

我知道我可以使用递归函数遍历所有控件,但是 有没有更简单或更直接的方法,可能像下面这样?

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

【问题讨论】:

标签: c# .net vb.net winforms controls


【解决方案1】:

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

也许可以在一个 LINQ 语句中使用Descendants 函数来执行此操作,尽管我不太熟悉它。有关更多信息,请参阅this page

编辑 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 仅返回当前控件的子项(在我的情况下为 Form),我希望在一次调用中“递归”获取 Forma 中的所有控件(子项、子项, sub-sub-childs,.....) 在单个集合中。
  • 我希望一个名为 GetAllControls 的方法返回一个控件集合,我会将其分配给 ControlList。只是似乎更好的做法。
  • @ProfK 我同意你的看法;相应地更改示例。
【解决方案2】:

您可以使用 LINQ 查询来执行此操作。这将查询表单上类型为 TextBox 的所有内容

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

【讨论】:

  • 谢谢,但与答案相同的问题,它只返回孩子而不是子孩子等,我想要所有的 ensted 控件。我很确定我看到它可以通过 .NET 3.5 或 4.0 中新的单个方法调用来实现,记得我在某个演示中看到过
  • 忽略没有递归,var c = this.Controls.OfType&lt;TextBox&gt;() 不会给出同样的结果吗?
  • @Dennis:是的,这是一个偏好问题(通常)。请参阅stackoverflow.com/questions/214500/… 了解有关此问题的有趣讨论。
【解决方案3】:

这可能有效:

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

【讨论】:

    【解决方案4】:

    这是您的另一个选择。我通过创建一个示例应用程序对其进行了测试,然后在初始 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());
    }
    

    而且它每次都返回正确的计数,所以我认为这非常适合您正在寻找的内容:)

    【讨论】:

    • 此处定义的 GetAll() 非常适合作为 Control 类的扩展方法
    • 我喜欢你使用 lambda 表达式的方式。哪里可以详细学习 lambda 表达式?
    • "'System.Windows.Forms.Control.ControlCollection' 不包含 'Cast' 的定义,并且没有扩展方法 'Cast' 接受类型为 'System.Windows.Forms.Control 的第一个参数可以找到 .ControlCollection'(您是否缺少 using 指令或程序集引用?)“我在 .NET 4.5 上,并且“控件”没有“Cast”函数/方法/任何东西。我错过了什么?
    • @soulblazer 添加 System.Linq 命名空间。
    • var allCtl = GetAll(this.FindForm(), typeof(TextBox)); //这是一个用户控件没有返回任何东西!!
    【解决方案5】:

    这是递归 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>());
        }
    

    【讨论】:

      【解决方案6】:

      不要忘记,您也可以在其他控件中拥有一个文本框除了容器控件。您甚至可以将 TextBox 添加到 PictureBox。

      所以你还需要检查是否

      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
      

      在表单上使用一个按钮一个 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
      

      【讨论】:

        【解决方案7】:

        这可能是古老的技术,但它的作用就像魅力一样。我使用递归来更改控件所有标签的颜色。效果很好。

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

        【讨论】:

          【解决方案8】:

          我从@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);
          }
          

          【讨论】:

            【解决方案9】:

            我将一堆之前的想法组合成一个扩展方法。这样做的好处是您可以返回正确类型的枚举,并且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)));
            }
            

            【讨论】:

              【解决方案10】:

              这是解决方案。

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

              我写了这段代码,只选择了面板,你可以添加更多的开关或ifs。在里面

              【讨论】:

                【解决方案11】:

                这是一个经过测试且有效的通用解决方案:

                我有大量的 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;
                       }
                    }   
                

                【讨论】:

                • 如果子控件有自己的子控件,这将不起作用。
                【解决方案12】:

                我想修改 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[] ??控制.ToArray();然后改为: return enumerable.SelectMany(FindControls).Concat(enumerable) .Where(c => c.GetType() == typeof(T)).Cast();
                • 使用.OfType&lt;T&gt;()Linq 的方法不是比.Where(c =&gt; c.GetType() == typeof(T)).Cast&lt;T&gt;(); 更有效吗?
                【解决方案13】:
                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;
                }
                

                【讨论】:

                  【解决方案14】:

                  如果你愿意,你可以试试这个:)

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

                  【讨论】:

                  • 简单地发布代码对帮助 OP 了解他们的问题或您的解决方案几乎没有帮助。你应该几乎总是在你的代码中包含某种解释。
                  • 问题没有说清除表格。
                  • 是的,不回答“问题”,但它是一个很好的补充。谢谢!
                  【解决方案15】:

                  虽然其他几个用户已经发布了足够的解决方案,但我想发布一个更通用的方法,它可能更有用。

                  这主要基于 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;
                  }
                  

                  【讨论】:

                    【解决方案16】:

                    使用反射:

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

                    【讨论】:

                      【解决方案17】:

                      这是我对Control 的扩展方法,使用 LINQ,作为 @PsychoCoder 版本的改编:

                      它需要一个类型列表,而不是让你不需要多次调用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;
                      }
                      

                      【讨论】:

                        【解决方案18】:
                            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;
                                }
                            }
                        

                        【讨论】:

                          【解决方案19】:
                             IEnumerable<Control> Ctrls = from Control ctrl in Me.Controls where ctrl is TextBox | ctrl is GroupBox select ctr;
                          

                          Lambda 表达式

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

                          【讨论】:

                          • 请在您的回答中添加更多内容,解释正在发生的事情以及它与问题的关系。
                          【解决方案20】:
                              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;
                              }
                          

                          【讨论】:

                            【解决方案21】:

                            一个干净简单的解决方案(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>();
                            

                            【讨论】:

                              【解决方案22】:

                              这是我的扩展方法。效率很高,也很懒。

                              用法:

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

                              【讨论】:

                              • 这是一个更干净的版本,懒惰,可以枚举和按需获取。
                              【解决方案23】:

                              对于正在寻找作为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)()
                              

                              【讨论】:

                                【解决方案24】:

                                你可以使用下面的代码

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

                                【讨论】:

                                  【解决方案25】:

                                  简单地说:

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

                                  【讨论】:

                                  • 这只会直接在“我”的控件集合中找到控件,而不是在海报试图通过“全部”暗示的任何子容器中找到按钮控件。
                                  【解决方案26】:

                                  创建方法

                                  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
                                  

                                  【讨论】:

                                    【解决方案27】:

                                    我很喜欢用VB,所以我写了一个扩展方法。检索控件的所有子项和子项

                                    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)
                                    

                                    在这种情况下,它将选择选定的单选按钮。

                                    【讨论】:

                                      【解决方案28】:

                                      VISUAL BASIC VB.NET 对于我们中的一些拒绝将 230,000 多行代码移植到 c# 的人来说,这是我的贡献,如果只需要特定类型,只需根据需要添加一个 'where'。

                                      Private Shared Function getAll(control As Control) As IEnumerable(Of Control)
                                          Return control.Controls.Cast(Of Control) _
                                              .SelectMany(Function(f) getAll(f).Concat(control.Controls.Cast(Of Control)))
                                      End Function
                                      

                                      【讨论】:

                                        猜你喜欢
                                        • 2011-02-13
                                        • 1970-01-01
                                        • 2011-09-19
                                        • 1970-01-01
                                        • 1970-01-01
                                        • 2021-08-18
                                        • 1970-01-01
                                        相关资源
                                        最近更新 更多