22

我想在我的所有 TextBoxes 中添加一个事件Form

foreach (Control C in this.Controls)
{
    if (C.GetType() == typeof(System.Windows.Forms.TextBox))
    {
        C.TextChanged += new EventHandler(C_TextChanged);
    }
}

问题是它们存储在几个 GroupBox 中,而我的循环看不到它们。我可以单独遍历每个控件,GroupBox但是是否可以在一个循环中以简单的方式完成所有操作?

4

9 回答 9

38

窗体和容器控件的Controls集合仅包含直接子级。为了获取所有控件,您需要遍历控件树并递归地应用此操作

private void AddTextChangedHandler(Control parent)
{
    foreach (Control c in parent.Controls)
    {
        if (c.GetType() == typeof(TextBox)) {
            c.TextChanged += new EventHandler(C_TextChanged);
        } else {
            AddTextChangedHandler(c);
        }
    }
}

注意:表单也(间接地)派生自Control,并且所有控件都有一个Controls集合。所以你可以在你的表单中调用这样的方法:

AddTextChangedHandler(this);

更通用的解决方案是创建一个扩展方法,将操作递归地应用于所有控件。在静态类(例如WinFormsExtensions)中添加此方法:

public static void ForAllControls(this Control parent, Action<Control> action)
{
    foreach (Control c in parent.Controls) {
        action(c);
        ForAllControls(c, action);
    }
}

using静态类命名空间必须是“可见的”,即,如果它在另一个命名空间中,则添加适当的声明。

然后可以这样调用,thisform在哪里;您还可以替换this为必须影响嵌套控件的表单或控件变量:

this.ForAllControls(c =>
{
    if (c.GetType() == typeof(TextBox)) {
        c.TextChanged += C_TextChanged;
    }
});
于 2013-03-03T14:52:00.557 回答
18

一些简单的通用工具使这个问题变得非常简单。我们可以创建一个简单的方法,该方法将遍历整个控件的树,返回一个包含所有子控件、所有子控件等的序列,覆盖所有控件,而不仅仅是固定的深度。我们可以使用递归,但通过避免递归,它会表现得更好。

public static IEnumerable<Control> GetAllChildren(this Control root)
{
    var stack = new Stack<Control>();
    stack.Push(root);

    while (stack.Any())
    {
        var next = stack.Pop();
        foreach (Control child in next.Controls)
            stack.Push(child);
        yield return next;
    }
}

使用这个我们可以得到所有的孩子,过滤掉我们需要的类型,然后容易地附加处理程序:

foreach(var textbox in GetAllChildren().OfType<Textbox>())
    textbox.TextChanged += C_TextChanged;
于 2013-11-21T15:14:52.867 回答
7

尝试这个

AllSubControls(this).OfType<TextBox>().ToList()
    .ForEach(o => o.TextChanged += C_TextChanged);

AllSubControls 在哪里

private static IEnumerable<Control> AllSubControls(Control control)
    => Enumerable.Repeat(control, 1)
       .Union(control.Controls.OfType<Control>()
                              .SelectMany(AllSubControls)
             );

LINQ很棒!

于 2016-02-22T15:09:12.210 回答
2

还没有看到有人使用 linq 和/或 yield 所以这里是:

public static class UtilitiesX {

    public static IEnumerable<Control> GetEntireControlsTree(this Control rootControl)
    {
        yield return rootControl;
        foreach (var childControl in rootControl.Controls.Cast<Control>().SelectMany(x => x.GetEntireControlsTree()))
        {
            yield return childControl;
        }
    }

    public static void ForEach<T>(this IEnumerable<T> en, Action<T> action)
    {
        foreach (var obj in en) action(obj);
    }
}

然后,您可以根据自己的意愿使用它:

someControl.GetEntireControlsTree().OfType<TextBox>().ForEach(x => x.Click += someHandler);
于 2016-02-12T15:18:53.620 回答
1

正如您所说,您必须更深入,而不仅仅是循环遍历表单中的每个元素。不幸的是,这意味着使用嵌套循环。

在第一个循环中,循环遍历每个元素。如果元素是 GroupBox 类型,那么您知道在继续之前需要循环浏览组框内的每个元素;否则正常添加事件。

您似乎对 C# 有一定的了解,所以我不会给您任何代码;纯粹是为了确保您开发解决问题所涉及的所有重要概念:)

于 2013-03-03T14:46:51.577 回答
1

我知道这是一个较老的话题,但会说来自http://backstreet.ch/coding/code-snippets/mit-c-rekursiv-durch-form-controls-loopen/的代码片段是一个聪明的解决方案问题。

它使用 ControlCollection 的扩展方法。

public static void ApplyToAll<T>(this Control.ControlCollection controlCollection, string tagFilter, Action action)
{
    foreach (Control control in controlCollection)
    {
        if (!string.IsNullOrEmpty(tagFilter))
        {
            if (control.Tag == null)
            {
                control.Tag = "";
            }

            if (!string.IsNullOrEmpty(tagFilter) && control.Tag.ToString() == tagFilter && control is T)
            {
                action(control);
            }
        }
        else
        {
            if (control is T)
            {
                action(control);
            }
        }

        if (control.Controls != null && control.Controls.Count > 0)
        {
            ApplyToAll(control.Controls, tagFilter, action);
        }
    }
}

现在,要将事件分配给所有 TextBox 控件,您可以编写如下语句(其中“this”是表单):

this.Controls.ApplyToAll<TextBox>("", control =>
{
    control.TextChanged += SomeEvent
});

或者,您可以按控件的标签过滤控件。

于 2017-03-13T09:06:25.480 回答
1

您只能使用表单集合在 Windows 表单中循环打开表单,例如为所有打开的表单设置窗口开始位置:

public static void setStartPosition()
        {
            FormCollection fc = Application.OpenForms;

            foreach(Form f in fc)
            {
                f.StartPosition = FormStartPosition.CenterScreen;
            }
        }
于 2016-05-19T17:56:43.620 回答
0

更新的答案:

我需要禁用表单中的所有控件,包括组框。此代码有效:

    private void AlterControlsEnable(bool ControlEnabled)
    {
        foreach (Control i in Controls)
            i.Enabled = ControlEnabled;
    }
于 2019-06-01T05:25:30.127 回答
0

由于关于“向您的文本框添加事件”的问题;已经回答了;我正在提供一些解释,并改为使用 for 循环添加迭代替代方案。


问题:

  • 无法在容器内获取控件。


解决方案:

  • 为了检索容器内的控件,您必须指定包含您希望访问的控件的容器。因此,您的循环必须检查容器内的控件。
    否则,您的循环将找不到容器内的控件。

IE:

foreach (Control control in myContainer.Controls)
{
   if (control is TextBox) { /* Do Something */ }
}
  • 如果您有多个容器:
    最初迭代容器。
    然后迭代容器内的控件(在初始迭代中找到的容器)。


关于如何使用 for 循环的伪代码示例:

    /// <summary> Iterate Controls Inside a Container using a for Loop. </summary>
    public void IterateOverControlsIncontainer()
    {
        // Iterate Controls Inside a Container (i.e: a Panel Container)
        for (int i = 0; i < myContainer.Controls.Count; i++)
        {
            // Get Container Control by Current Iteration Index
            // Note:
            // You don't need to dispose or set a variable to null.
            // The ".NET" GabageCollector (GC); will clear up any unreferenced classes when a method ends in it's own time.
            Control control = myContainer.Controls[i];

            // Perform your Comparison
            if (control is TextBox)
            {
                // Control Iteration Test.
                // Shall Display a MessageBox for Each Matching Control in Specified Container.
                MessageBox.Show("Control Name: " + control.Name);
            }
        }
    }
于 2020-09-28T07:54:02.613 回答