遍历儿童控制时避免无限循环

时间:2010-07-09 17:38:58

标签: c# .net winforms controls

我正在编写一个简单的扩展方法来对控件及其所有子节点执行操作,我想知道是否必须担心两次运行相同的控件。

安全:

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 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:0)

您需要使用递归

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