【问题标题】:Loop through all controls of a Form, even those in GroupBoxes循环遍历表单的所有控件,甚至是 GroupBoxes 中的控件
【发布时间】:2013-03-03 14:43:44
【问题描述】:

我想为我的Form 上的所有文本框添加一个事件:

foreach (Control C in this.Controls)
{
    if (C.GetType() == typeof(System.Windows.Forms.TextBox))
    {
        C.TextChanged += new EventHandler(C_TextChanged);
    }
}

问题是它们存储在几个 GroupBox 中,而我的循环看不到它们。我可以单独循环遍历每个 GroupBox 的控件,但是否可以在一个循环中以简单的方式完成所有操作?

【问题讨论】:

标签: c# groupbox


【解决方案1】:

表单和容器控件的Controls 集合仅包含直接子级。为了得到所有的控件,你需要遍历控件树并递归地应用这个操作

private void AddTextChangedHandler(Control parent)
{
    foreach (Control c in parent.Controls)
    {
        if (c.GetType() == typeof(TextBox)) {
            c.TextChanged += new EventHandler(C_TextChanged);
        } else {
            AddTextChangedHandler(c);
        }
    }
}

注意:表单也(间接)从Control 派生,所有控件都有一个Controls 集合。所以你可以在你的表单中调用这样的方法:

AddTextChangedHandler(this);

更通用的解决方案是创建一个扩展方法,将操作递归地应用于所有控件。在静态类中(例如WinFormsExtensions)添加这个方法:

public static void ForAllControls(this Control parent, Action<Control> action)
{
    foreach (Control c in parent.Controls) {
        action(c);
        ForAllControls(c, action);
    }
}

静态类命名空间必须是“可见的”,即,如果它位于另一个命名空间中,则添加适当的 using 声明。

那你就可以这样调用了,其中this是形式;您还可以将this 替换为必须影响嵌套控件的表单或控件变量:

this.ForAllControls(c =>
{
    if (c.GetType() == typeof(TextBox)) {
        c.TextChanged += C_TextChanged;
    }
});

【讨论】:

    【解决方案2】:

    一些简单的通用工具使这个问题变得非常简单。我们可以创建一个简单的方法来遍历整个控件的树,返回一个包含所有子控件、所有子控件等的序列,覆盖所有控件,而不仅仅是固定的深度。我们可以使用递归,但通过避免递归,它的性能会更好。

    public static IEnumerable<Control> GetAllChildren(this Control root)
    {
        var stack = new Stack<Control>();
        stack.Push(root);
    
        while (stack.Any())
        {
            var next = stack.Pop();
            foreach (Control child in next.Controls)
                stack.Push(child);
            yield return next;
        }
    }
    

    使用这个我们可以得到所有的孩子,过滤掉我们需要的类型,然后很容易地附加处理程序非常

    foreach(var textbox in GetAllChildren().OfType<Textbox>())
        textbox.TextChanged += C_TextChanged;
    

    【讨论】:

    • 我不知道递归还有其他解决方案。一直在学习。
    【解决方案3】:

    试试这个

    AllSubControls(this).OfType<TextBox>().ToList()
        .ForEach(o => o.TextChanged += C_TextChanged);
    

    AllSubControls 在哪里

    private static IEnumerable<Control> AllSubControls(Control control)
        => Enumerable.Repeat(control, 1)
           .Union(control.Controls.OfType<Control>()
                                  .SelectMany(AllSubControls)
                 );
    

    LINQ 很棒!

    【讨论】:

      【解决方案4】:

      还没有看到任何人使用 linq 和/或 yield 所以这里是:

      public static class UtilitiesX {
      
          public static IEnumerable<Control> GetEntireControlsTree(this Control rootControl)
          {
              yield return rootControl;
              foreach (var childControl in rootControl.Controls.Cast<Control>().SelectMany(x => x.GetEntireControlsTree()))
              {
                  yield return childControl;
              }
          }
      
          public static void ForEach<T>(this IEnumerable<T> en, Action<T> action)
          {
              foreach (var obj in en) action(obj);
          }
      }
      

      然后您可以根据自己的意愿使用它:

      someControl.GetEntireControlsTree().OfType<TextBox>().ForEach(x => x.Click += someHandler);
      

      【讨论】:

        【解决方案5】:

        正如您所说,您必须更深入地了解表单中的每个元素。不幸的是,这意味着使用嵌套循环。

        在第一个循环中,循环遍历每个元素。如果元素是 GroupBox 类型,那么您知道在继续之前需要循环浏览组框内的每个元素;否则正常添加事件。

        您似乎对 C# 有一定的了解,所以我不会给您任何代码;纯粹是为了确保您开发解决问题所涉及的所有重要概念:)

        【讨论】:

          【解决方案6】:

          您只能使用表单集合在 Windows 表单中循环打开表单,例如为所有打开的表单设置窗口开始位置:

          public static void setStartPosition()
                  {
                      FormCollection fc = Application.OpenForms;
          
                      foreach(Form f in fc)
                      {
                          f.StartPosition = FormStartPosition.CenterScreen;
                      }
                  }
          

          【讨论】:

            【解决方案7】:

            我知道这是一个较老的话题,但我会说来自http://backstreet.ch/coding/code-snippets/mit-c-rekursiv-durch-form-controls-loopen/ 的代码 sn-p 是解决这个问题的巧妙方法。

            它使用 ControlCollection 的扩展方法。

            public static void ApplyToAll<T>(this Control.ControlCollection controlCollection, string tagFilter, Action action)
            {
                foreach (Control control in controlCollection)
                {
                    if (!string.IsNullOrEmpty(tagFilter))
                    {
                        if (control.Tag == null)
                        {
                            control.Tag = "";
                        }
            
                        if (!string.IsNullOrEmpty(tagFilter) && control.Tag.ToString() == tagFilter && control is T)
                        {
                            action(control);
                        }
                    }
                    else
                    {
                        if (control is T)
                        {
                            action(control);
                        }
                    }
            
                    if (control.Controls != null && control.Controls.Count > 0)
                    {
                        ApplyToAll(control.Controls, tagFilter, action);
                    }
                }
            }
            

            现在,要将事件分配给所有 TextBox 控件,您可以编写如下语句(其中“this”是表单):

            this.Controls.ApplyToAll<TextBox>("", control =>
            {
                control.TextChanged += SomeEvent
            });
            

            您可以选择按标签过滤控件。

            【讨论】:

              【解决方案8】:

              更新答案:

              我需要禁用表单中的所有控件,包括组框。此代码有效:

                  private void AlterControlsEnable(bool ControlEnabled)
                  {
                      foreach (Control i in Controls)
                          i.Enabled = ControlEnabled;
                  }
              

              【讨论】:

                【解决方案9】:

                自从关于“向您的文本框添加事件”的问题以来;已经回答了;我提供了一些解释,并使用 for 循环添加了一个迭代替代方案。


                问题:

                • 无法获取容器内的控件。


                解决方案:

                • 为了检索容器内的控件,您必须指定包含您希望访问的控件的容器。 因此,您的循环必须检查容器内的控件。
                  否则您的循环将找不到容器内的控件。

                即:

                foreach (Control control in myContainer.Controls)
                {
                   if (control is TextBox) { /* Do Something */ }
                }
                
                • 如果您有多个容器:
                  最初迭代容器。
                  然后迭代容器内的控件(在初始迭代中找到的容器)。


                关于如何改用 for 循环的伪代码示例:

                    /// <summary> Iterate Controls Inside a Container using a for Loop. </summary>
                    public void IterateOverControlsIncontainer()
                    {
                        // Iterate Controls Inside a Container (i.e: a Panel Container)
                        for (int i = 0; i < myContainer.Controls.Count; i++)
                        {
                            // Get Container Control by Current Iteration Index
                            // Note:
                            // You don't need to dispose or set a variable to null.
                            // The ".NET" GabageCollector (GC); will clear up any unreferenced classes when a method ends in it's own time.
                            Control control = myContainer.Controls[i];
                
                            // Perform your Comparison
                            if (control is TextBox)
                            {
                                // Control Iteration Test.
                                // Shall Display a MessageBox for Each Matching Control in Specified Container.
                                MessageBox.Show("Control Name: " + control.Name);
                            }
                        }
                    }
                

                【讨论】:

                • 很好的解释。干得好!
                猜你喜欢
                • 1970-01-01
                • 2021-03-02
                • 1970-01-01
                • 2012-02-10
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-12-17
                • 1970-01-01
                相关资源
                最近更新 更多