【问题标题】:Exclude control container from list从列表中排除控制容器
【发布时间】:2016-11-07 15:46:48
【问题描述】:

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

+---------------------------------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)。

我试过了:

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

结果相同:我得到了所有控件的列表,即使是那些必须被排除的控件(示例中 TableLayoutPanel 中的 textBox2)。

知道我在哪里迷路了吗?

【问题讨论】:

  • 你试过的有什么问题?
  • 我得到了所有的控件,甚至是tableLayoutPanel中的控件。
  • 我无法重现该问题。 .Concat&lt;T&gt;(controls .Where(ctrl =&gt; ctrl.GetType() != typeof(TableLayoutPanel)) .SelectMany&lt;Control, T&gt;(ctrl =&gt; FindAllChildrenByType&lt;T&gt;(ctrl)) ); 似乎根据您的需要排除了 textbox2 。你怎么称呼这个?请看我的fiddle
  • 事实上,我已经创建了一个示例来说明我的问题,并且我没有尝试使用它来执行程序。实际上,我使用带有用户控件的函数来初始化包含控件的某些属性,并且我调查了调用返回的枚举以查看假定的被忽略控件已添加到列表中。我想这个错误隐藏在其他地方,我会更深入地搜索......

标签: c# linq predicate


【解决方案1】:

基于this nice answer使用非递归方法遍历树结构我想出了这个方法:

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,所以控件从上到下返回。当然还添加了您要查找的条件:过滤类型和排除容器类型。

【讨论】:

    猜你喜欢
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-18
    • 2011-08-26
    • 1970-01-01
    相关资源
    最近更新 更多