遍历表单上的所有控件,甚至是组框中的控件

我想在表单上的所有文本框中添加一个事件:

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

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

Forms和容器控件的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 action) { foreach (Control c in parent.Controls) { action(c); ForAllControls(c, action); } } 

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

然后你就可以这样称呼它, this就是forms; 您还可以通过其嵌套控件必须受到影响的表单或控件变量替换this

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

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

 public static IEnumerable GetAllChildren(this Control root) { var stack = new Stack(); 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.TextChanged += C_TextChanged; 

试试这个

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

AllSubControls在哪里

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

LINQ很棒!

没有看到有人使用linq和/或产量所以这里:

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

然后你可以用它来满足你的心愿:

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

正如您所说,您将不得不深入了解表单中的每个元素。 遗憾的是,这意味着使用嵌套循环。

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

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

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

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

我知道这是一个较老的主题,但是会说来自http://backstreet.ch/coding/code-snippets/mit-c-rekursiv-durch-form-controls-loopen/的代码片段是一个聪明的解决方案。问题。

它使用ControlCollection的扩展方法。

 public static void ApplyToAll(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("", control => { control.TextChanged += SomeEvent }); 

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