代码之家  ›  专栏  ›  技术社区  ›  dlras2

遍历子控件时避免无限循环

  •  2
  • dlras2  · 技术社区  · 14 年前

    我正在编写一个简单的扩展方法来对一个控件及其所有子控件执行一个操作,我想知道是否需要担心两次遇到同一个控件。

    安全:

    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);
    }
    

    可能不安全:

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

    哈希集是否是保持此代码安全所必需的?它只需要对每个控件调用一次操作,并且不能进入无限循环。父子控制的结构是否使我不必担心?

    用途:

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

    实现这一点的(可能)综合方法:

    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());
        }
    }
    
    3 回复  |  直到 14 年前
        1
  •  4
  •   Steven Sudit    14 年前

    每个孩子都有一个父母,所以没必要担心。

        2
  •  2
  •   Reed Copsey    14 年前

    控件实际上只能有一个父级。没有真正的理由跟踪“handled”,因为您将只在控件上执行一次方法。

    现在,如果您使用的框架允许控件具有多个父级(我不知道任何.NET框架允许这样做), 然后 这可能是必需的。但是,如果您使用的是Windows窗体(这看起来就是Windows窗体)或WPF,则可以将其简化为:

    private static void Traverse(this Control control, Action<Control> action)
    {
        foreach (Control child in control.Controls)
            Traverse(child, action);
        action(control);
    }
    
        3
  •  0
  •   Achilles    14 年前

    你需要使用递归

    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