Избегание бесконечных циклов при обходе контроля над детьми - PullRequest
2 голосов
/ 09 июля 2010

Я пишу простой метод расширения для выполнения действий над элементом управления и всеми его дочерними элементами, и мне интересно, стоит ли мне беспокоиться о том, чтобы дважды столкнуться с одним и тем же элементом управления.

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

Возможно небезопасно:

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 ]

4 голосов
/ 09 июля 2010

У каждого ребенка есть один родитель, поэтому не нужно беспокоиться.

2 голосов
/ 09 июля 2010

Элементы управления действительно могут иметь только одного родителя. На самом деле нет причины отслеживать «обработанный», поскольку вы будете выполнять свой метод на элементе управления только один раз.

Теперь, если вы используете платформу, которая позволяет элементам управления иметь нескольких родителей (я не знаю ни одной платформы .NET, которая позволяет это), , тогда это может потребоваться. Однако, если вы используете Windows Forms (как это выглядит) или WPF, вы можете просто упростить это до:

private static void Traverse(this Control control, Action<Control> action)
{
    foreach (Control child in control.Controls)
        Traverse(child, action);
    action(control);
}
0 голосов
/ 09 июля 2010

Вам нужно использовать рекурсию

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
...