如何在Windows Forms中获取一个窗体的所有控件?

6

我有一个名为AForm

A包含许多不同的控件,包括一个主要的GroupBox。这个GroupBox包含许多表格和其他的GroupBox。我想找到一个在A表单中具有例如标签索引9的控件,但我不知道哪个GroupBox包含此控件。

我该如何做?


我想使用Marc的Map扩展方法来处理这种递归:https://dev59.com/T3VC5IYBdhLWcg3w7V73#229442 - Oliver
5个回答

13

使用递归...

public static IEnumerable<T> Descendants<T>( this Control control ) where T : class
{
    foreach (Control child in control.Controls) {

        T childOfT = child as T;
        if (childOfT != null) {
            yield return (T)childOfT;
        }

        if (child.HasChildren) {
            foreach (T descendant in Descendants<T>(child)) {
                yield return descendant;
            }
        }
    }
}

您可以像这样使用上面的函数:

var checkBox = (from c in myForm.Descendants<CheckBox>()
                where c.TabIndex == 9
                select c).FirstOrDefault();

这将获取表单中TabIndex为9的第一个CheckBox。您可以使用任何标准。

如果您不喜欢LINQ查询语法,以上内容可以重写为:

var checkBox = myForm.Descendants<CheckBox>()
                     .FirstOrDefault(x=>x.TabIndex==9);

2
你的方法只能找到TabIndex为9的控件。我的是一种扩展方法,有效地使LINQ支持WinForms。它采用了与LINQ to XML相同的方法。这并不复杂,像其他答案一样是简单的递归。唯一的区别是我在过滤类型,这使得谓词更容易编写。 - Josh
3
+1用于检查类型,yield return也将确保不再评估需要评估的任何其他控件。这种扩展方法也可以轻松应用于许多其他条件,而无需修改,并且还可以检查特定于类型的属性(例如“IsChecked”)!如果您不想再检查TabIndex怎么办?查询(或谓词)是一个不错的主意。 - Cornelius
1
@Ed:这种解决方案的优雅之处在于,将来需要查找没有子控件的控件时,可以重复使用代码,只需使用不同的比较调用即可,而不是通过添加一个新的、几乎相同的方法来扩展代码库以适应新情况。 - Fredrik Mörk
1
@EdS。祝你好运,总是编写只做当天所需的不同代码片段。有了Josh的代码,你只需编写一次,测试一次,然后在需要查找任何表单上的任何控件时使用它。 - ProfK
@ProfK:我不确定我是否同意三年前的自己,但我可以说这一点;编写可重用代码和编写永远不会用到的代码之间有一条细微的界限。后者完全浪费时间(和金钱)。而且我不需要运气;到目前为止,我的表现非常好。实际上比很好还要好。 - Ed S.
显示剩余5条评论

4

递归搜索您的表单的 Controls 集合。

void FindAndSayHi(Control control)
{
    foreach (Control c in control.Controls)
    {
        Find(c.Controls);
        if (c.TabIndex == 9)
        {
            MessageBox.Show("Hi");
        }
    }
}

2
void iterateControls(Control ctrl)
{
    foreach(Control c in ctrl.Controls)
    {
        iterateControls(c);
    }
}

2
您可以创建一个类似这样的方法:
public static Control GetControl(Control.ControlCollection controlCollection, Predicate<Control> match)
{
    foreach (Control control in controlCollection)
    {
        if (match(control))
        {
            return control;
        }

        if (control.Controls.Count > 0)
        {
            Control result = GetControl(control.Controls, match);
            if (result != null)
            {
                return result;
            }
        }
    }

    return null;
}

...这样使用:

Control control = GetControl(this.Controls, ctl => ctl.TabIndex == 9);

请注意,TabIndex是一个棘手的情况,因为它在每个容器内都从0开始,所以在同一表单中可能有几个控件具有相同的TabIndex值。
无论如何,上述方法都可以用于检查控件的任何属性。
Control control = GetControl(this.Controls, ctl => ctl.Text == "Some text");

0

我讨厌递归,所以我总是使用堆栈来处理这种事情。这将为当前控件层次结构中每个单选按钮控件的 CheckedChanged 事件分配一个公共事件处理程序:

Stack<Control> controlStack = new Stack<Control>();
foreach (Control c in this.Controls)
{
    controlStack.Push(c);
}
Control ctl;
while (controlStack.Count > 0 && (ctl = controlStack.Pop()) != null)
{
    if (ctl is RadioButton)
    {
        (ctl as RadioButton).CheckedChanged += new EventHandler(rb_CheckedChanged);
    }
    foreach (Control child in ctl.Controls)
    {
        controlStack.Push(child);
    }
}

您可以轻松地使乔希·爱因斯坦的扩展方法按照这种方式工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接