如何循环Windows窗体表单中的所有控件或如何查找特定控件是否是容器控件?

我会告诉我的要求。 我需要在Windows窗体表单中为每个控件创建一个keydown事件。 如果我必须为所有keydown事件做的事情是相同的,那么最好这样做而不是为所有控件手动执行它。

所以我基本上可以这样做:

 foreach (Control c in this.Controls) c.KeyDown+= new KeyEventHandler(c_KeyDown); 

但是在这里,foreach不会在位于groupBox或tabControl中的那些控件内循环。 我的意思是如果表单(this)包含groupBox或其他一些容器控件,那么我可以获得该特定容器控件的keydown事件。 并且foreach不会foreach驻留在容器控件内的控件。

问题1:如何为表单中的“所有”控件获取keydown事件?

如果上面的谜题得到解决,那我的问题就结束了。

这是我可以做的事情:

主要是伪代码

 foreach (Control c in this.Controls) { c.KeyDown += new KeyEventHandler(c_KeyDown); if (c is Container control) FunctionWhichGeneratesKeyDownForAllItsChildControls(c) } 

我知道如果组框内有组框,我将不得不多次通过FunctionWhichGeneratesKeyDownForAllItsChildControls(c)来获取所有控件的keydown。 我能做到。 我的问题是,

问题2:如何检查c是否为容器控件?

一个简单的递归函数应该这样做。

 private void AddEvent(Control parentCtrl) { foreach (Control c in parentCtrl.Controls) { c.KeyDown += new KeyEventHandler(c_KeyDown); AddEvent(c); } } 

这与马格努斯的正确答案相同,但更加充实。 请注意,这会将处理程序添加到每个控件,包括标签和容器控件。 这些控件似乎不会引发事件,但您可能希望添加逻辑以仅将处理程序添加到接受用户输入的控件。

 public partial class Form1 : Form { public Form1() { InitializeComponent(); RegisterKeyDownHandlers(this); } private void RegisterKeyDownHandlers(Control control) { foreach (Control ctl in control.Controls) { ctl.KeyDown += KeyDownFired; RegisterKeyDownHandlers(ctl); } } private void KeyDownFired(object sender, EventArgs e) { MessageBox.Show("KeyDown fired for " + sender); } } 

以下是遍历控件集合的一些非递归选项。 我的特定实现是进行界面validation,但可以根据您的目的进行调整。

为什么甚至搞乱你说的非递归解决方案呢? 好吧,我在调试一天时遇到堆栈溢出错误,所以我看着用循环替换它(这要困难得多)。 事实certificate,错误是一种侥幸,并且再也没有发生过

  //recursive //This is the simplest implementation, but the most memory hungry private IEnumerable CheckErrors(Control.ControlCollection controls, ErrorProvider errorProvider) { var errors = new List(); foreach (var control in controls.Cast()) { //insert your own business logic in here var error = errorProvider.GetError(control); if (!string.IsNullOrEmpty(error)) { errors.Add(new DataObjects.Error(error, DataObjects.ErrorLevel.Validation)); } //recursive call errors.AddRange(CheckErrors(control.Controls, errorProvider)); //insert your own business logic in here } return errors; } //Breadth first - Does NOT require child node to have knowledge of parent //Read through the controls at a given level and then blindly delve //deeper until you reach the end of the rainbow //order(max-tree-level-size) memory usage? //tree-level-size, as in the # of nodes at a given depth private IEnumerable CheckErrors_NonRecursive_NeverLookBack(Control control, ErrorProvider errorProvider) { var currentControls = control.Controls.Cast(); var errors = new List(); while (currentControls.Count() > 0) { foreach (var currentControl in currentControls) { //insert your own business logic in here var error = errorProvider.GetError(currentControl); if (!string.IsNullOrEmpty(error)) { errors.Add(new DataObjects.Error(error, DataObjects.ErrorLevel.Validation)); } //insert your own business logic in here } //replace currentControls with ALL of the nodes at a given depth currentControls = currentControls.SelectMany(x => x.Controls.Cast()); } return errors; } //Depth first - Does NOT require child to have knowledge of parent //Approximate recursion by keeping a stack of controls, instead of a call stack. //Traverse the stack as you would have with recursion //order(tree-branch-size) memory usage? tree-branch-size as in the number of nodes //that it takes to get from the root to the bottom of a given branch private IEnumerable CheckErrors_NonRecursive(Control.ControlCollection controls, ErrorProvider errorProvider) { var controlStack = new Stack(); var controlIndicies = new Stack(); var errors = new List(); controlStack.Push(controls); controlIndicies.Push(0); while(controlStack.Count() > 0) { while(controlIndicies.First() < controlStack.First().Count) { var controlIndex = controlIndicies.Pop(); var currentControl = controlStack.First()[controlIndex]; //insert your own business logic in here var error = errorProvider.GetError(currentControl); if (!string.IsNullOrEmpty(error)) { errors.Add(new DataObjects.Error(error, DataObjects.ErrorLevel.Validation)); } //insert your own business logic in here //update the fact that we've processed one more control controlIndicies.Push(controlIndex + 1); if(currentControl.Controls.Count > 0) { //traverse deeper controlStack.Push(currentControl.Controls); controlIndicies.Push(0); } //else allow loop to continue uninterrupted, to allow siblings to be processed } //all siblings have been traversed, now we need to go back up the stack controlStack.Pop(); controlIndicies.Pop(); } return errors; } //Depth first - DOES require child to have knowledge of parent. //Approximate recursion by keeping track of where you are in the control //tree and use the .Parent() and .Controls() methods to traverse the tree. //order(depth(tree)) memory usage? //Best of the bunch as far as I can (in memory usage that is) private IEnumerable CheckErrors_NonRecursiveIndicesOnly(Control control, ErrorProvider errorProvider) { var errors = new List(); var controlIndicies = new Stack(); var controlCount = new Stack(); Control currentControl = control; var currentControls = currentControl.Controls; controlCount.Push(currentControls.Count); controlIndicies.Push(0); while (controlCount.Count() > 0) { while (controlIndicies.First() < controlCount.First()) { var controlIndex = controlIndicies.Pop(); currentControl = currentControls[controlIndex]; //insert your own business logic in here var error = errorProvider.GetError(currentControl); if (!string.IsNullOrEmpty(error)) { errors.Add(new DataObjects.Error(error, DataObjects.ErrorLevel.Validation)); } //insert your own business logic in here //update the fact that we've processed one more control controlIndicies.Push(controlIndex + 1); if (currentControl.Controls.Count > 0) { //traverse deeper currentControls = currentControl.Controls; controlCount.Push(currentControl.Controls.Count); controlIndicies.Push(0); } else { //allow loop to continue uninterrupted, to allow siblings to be processed } } //all siblings have been traversed, now we need to go back up the stack controlCount.Pop(); controlIndicies.Pop(); //need to check our position in the stack... once we get back to the top there is no parent of parent. if (controlCount.Count() > 0) { currentControls = currentControl.Parent.Parent.Controls; } //do nothing, believe it or not once you've gotten to this level you have traversed the entire stack } return errors; } 

问题2的答案是使用您正在检查的控件的GetType()方法。