I will tell my requirement. I need to have a keydown
event for each control in the Windows Forms form. It\'s better to do so rather than manually doing it for all c
Here are some non-recursive options for traversing the control collection. My particular implementation is doing interface validation, but could be adapted to your purpose.
Why even mess with a non-recursive solution you say? Well I got a stack overflow error when debugging one day, so I looked at replacing it with a loop (which is considerably more difficult). As it turns out that error was a fluke and has never happened again
//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;
}