22

私のすべての TextBoxes にイベントを追加したいForm:

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

問題は、それらが複数の GroupBoxes に格納されており、ループがそれらを認識しないことです。それぞれのコントロールをGroupBox個別にループすることはできますが、1 つのループですべてを簡単な方法で行うことは可能ですか?

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別の名前空間にある場合は適切な宣言を追加します。

次に、このように呼び出すことができますthis。フォームはどこにありますか。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

たとえば、開いているすべてのフォームのウィンドウの開始位置を設定するには、フォーム コレクションを使用して 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

「TextBoxにイベントを追加する」に関する質問以来。すでに回答されています。いくつかの説明を提供し、代わりに for ループを使用して反復の代替手段を追加しています。


問題:

  • コンテナ内でコントロールを取得できない。


解決:

  • コンテナ内のコントロールを取得するには、アクセスしたいコントロールを含むコンテナを指定する必要があります。したがって、ループはコンテナ内のコントロールをチェックする必要があります。
    そうしないと、ループはコンテナ内のコントロールを見つけられません。

すなわち:

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 に答える