从列表中排除控件容器

本文关键字:控件 排除 列表 | 更新日期: 2023-09-27 18:16:44

如果我有一个带有这个容器的表单例如:

+---------------------------------panel 1----+
|                                            |
|   +------------------panel 2---+           |
|   |                            |           |
|   | textbox1                   |           |
|   | combobox1        checkBox1 |           |
|   +----------------------------+           |
|                                            |
|   +------------tableLayoutPanel1-+         |
|   |                              |         |
|   | textbox2                     |         |
|   +------------------------------+         |
|                                            |
|   +-------------FlowLayoutPanel1-+         |
|   |textbox3  Combobox2           |         |
|   +------------------------------+         |
|                                            |
+--------------------------------------------+

我已经有了一个函数,用于从给定的容器中获取特定类型的所有控件(通过递归调用来获取甚至包含的控件):

public static IEnumerable<T> FindAllChildrenByType<T>(this Control control)
    {
        IEnumerable<Control> controls = control.Controls.Cast<Control>();
        return controls
            .OfType<T>()
            .Concat<T>(controls.SelectMany<Control, T>(ctrl => FindAllChildrenByType<T>(ctrl)));
    }

这很好(这里它返回textbox1, combobox1, checkbox1, textbox2, textbox3, combobox2)

现在,我想要一个具有类似行为的新函数:从容器中获取所有控件,但这些控件不包含在特定类型的容器中。在我的例子中,该函数可以返回panel1中包含的所有控件,这些控件从未包含在tableLayoutPanel中(这里是textbox1, combobox1, checkbox1, textbox3, combobox2)。

I've try:

public static IEnumerable<T> FindAllChildrenByType<T>(this Control control)
    {
        IEnumerable<Control> controls = control.Controls.Cast<Control>();
        return controls
            .OfType<T>()
            .Concat<T>(controls .Where(ctrl => ctrl.GetType() != typeof(TableLayoutPanel))
                                .SelectMany<Control, T>(ctrl => FindAllChildrenByType<T>(ctrl))
                        );
    }

:

public static IEnumerable<T> FindAllChildrenByType2<T>(this Control control)
    {
        IEnumerable<Control> controls = control.Controls.Cast<Control>();
        return controls
            .OfType<T>()
            .Concat<T>(controls.SelectMany<Control, T>(ctrl => FindAllChildrenByType<T>(ctrl)))
            .Where<T>(ctrl => ctrl.GetType() != typeof(TableLayoutPanel));
    }

具有相同的结果:我得到了所有控件的列表,甚至是那些必须被排除的控件(在本例中tabelayoutpanel中的textBox2)。

你知道我在哪里迷路了吗?

从列表中排除控件容器

基于这个使用非递归方法遍历树结构的漂亮答案,我想出了这个方法:

public static IEnumerable<TControl> GetChildControls<TControl>(this Control root, 
    Type excludedContainer = null)
        where TControl : Control
{
    var queue = new Queue<Control>();
    queue.Enqueue(root);
    while (queue.Any())
    {
        var next = queue.Dequeue();
        
        if (!next.GetType().Equals(excludedContainer))
        foreach (Control child in next.Controls)
        {
            queue.Enqueue(child);
        }
        if (!next.HasChildren && next is TControl typed)
        {
            yield return typed;
        }
    }
}

非递归方法总是首选,因为它们不会耗尽调用堆栈(尽管在本例中不太可能)。

它本质上和原来的一样:在while循环中逐级收集控件。我只是用Queue代替了Stack,所以控制从上到下返回。当然,还添加了您要查找的条件:过滤类型和排除的容器类型。