Tutorial :Avoiding Infinite Loops While Traversing Children Controls



Question:

I'm writing a simple extension method to perform an action on a control and all of its children, and I'm wondering if I have to worry about running into the same control twice.

Safe:

public static void Traverse(this Control control, Action<Control> action)  {      Traverse(control, action, new HashSet<control>());  }    private static void Traverse(this Control control, Action<Control> action, HashSet<Control> handled)  {      handled.Add(control);      foreach (Control child in control.Controls)          if (!handled.Contains(child))              Traverse(child, action, handled);      action.Invoke(control);  }  

Possibly Unsafe:

public static void Traverse(this Control control, Action<Control> action)  {      foreach (Control child in control.Controls)          Traverse(child, action, handled);      action.Invoke(control);  }  

Is the hash set necessary to keep this code safe? It needs to invoke the action on every control only once, and it can't enter an infinite loop. Is the structure of parent-child controls such that I don't need to worry about this?

Usage:

this.Traverse(o => o.SuspendLayout());    // Do lots of UI changes    this.Traverse(o => o.ResumeLayout());  

The (possibly) comprehensive way to do this:

public static class ControlExtensions  {      public static void Traverse(this Control control, Action<Control> action)      {          Traverse(control, action, TraversalMethod.DepthFirst);      }        public static void Traverse(this Control control, Action<Control> action, TraversalMethod method)      {          switch (method)          {              case TraversalMethod.DepthFirst:                  TraverseDepth(control, action);                  break;              case TraversalMethod.BreadthFirst:                  TraverseBreadth(control, action);                  break;              case TraversalMethod.ReversedDepthFirst:                  TraverseDepthReversed(control, action);                  break;              case TraversalMethod.ReversedBreadthFirst:                  TraverseBreadthReversed(control, action);                  break;          }      }        private static void TraverseDepth(Control control, Action<Control> action)      {          Stack<Control> controls = new Stack<Control>();          Queue<Control> queue = new Queue<Control>();            controls.Push(control);          while (controls.Count != 0)          {              control = controls.Pop();              foreach (Control child in control.Controls)                  controls.Push(child);              queue.Enqueue(control);          }          while (queue.Count != 0)              action.Invoke(queue.Dequeue());      }        private static void TraverseBreadth(Control control, Action<Control> action)      {          Queue<Control> controls = new Queue<Control>();          Queue<Control> queue = new Queue<Control>();            controls.Enqueue(control);          while (controls.Count != 0)          {              control = controls.Dequeue();              foreach (Control child in control.Controls)                  controls.Enqueue(child);              queue.Enqueue(control);          }          while (queue.Count != 0)              action.Invoke(queue.Dequeue());      }        private static void TraverseDepthReversed(Control control, Action<Control> action)      {          Stack<Control> controls = new Stack<Control>();          Stack<Control> stack = new Stack<Control>();            controls.Push(control);          while (controls.Count != 0)          {              control = controls.Pop();              foreach (Control child in control.Controls)                  controls.Push(child);              stack.Push(control);          }          while (stack.Count != 0)              action.Invoke(stack.Pop());      }        private static void TraverseBreadthReversed(Control control, Action<Control> action)      {          Queue<Control> controls = new Queue<Control>();          Stack<Control> stack = new Stack<Control>();            controls.Enqueue(control);          while (controls.Count != 0)          {              control = controls.Dequeue();              foreach (Control child in control.Controls)                  controls.Enqueue(child);              stack.Push(control);          }          while (stack.Count != 0)              action.Invoke(stack.Pop());      }  }  


Solution:1

Each child has one parent, so there's no need to worry.


Solution:2

Controls really can only have a single parent. There isn't really a reason to track "handled", as you'll only execute your method on the control a single time.

Now, if you're using a framework that allows controls to have multiple parents (I'm not aware of any .NET frameworks that allow this), then this might be required. If, however, you're using Windows Forms (which is what this appears to be) or WPF, you can just simplify this to:

private static void Traverse(this Control control, Action<Control> action)  {      foreach (Control child in control.Controls)          Traverse(child, action);      action(control);  }  


Solution:3

You need to use recursion

public sub DoStuffToControlAndChildren(TargetControl as Control)        'Insert code to do stuff to TargetControl here         if TargetControl.Controls.count = 0 then           return       end if          For each ChildControl in TargetControl.Controls          DoStuffToControlAndChildren(ChildControl)       next    end sub  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »