web-dev-qa-db-ja.com

グループボックス内のコントロールであっても、フォーム上のすべてのコントロールをループします

フォーム上のすべてのテキストボックスにイベントを追加したい:

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

問題は、それらがいくつかのグループボックスに保存されており、私のループがそれらを見ないことです。各グループボックスのコントロールを個別にループすることもできますが、1つのループで簡単にすべてを実行することは可能ですか?

18
RRM

いくつかの単純な汎用ツールを使用すると、この問題が非常に簡単になります。コントロールのツリー全体をトラバースし、そのすべての子、すべての子などのシーケンスを返し、一定の深さだけでなく、すべてのコントロールをカバーする単純なメソッドを作成できます。再帰を使用することもできますが、再帰を避けることでパフォーマンスが向上します。

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

これを使用して、すべての子を取得し、必要なタイプの子を除外して、ハンドラーを簡単にアタッチできますvery

foreach(var textbox in GetAllChildren().OfType<Textbox>())
    textbox.TextChanged += C_TextChanged;
13
Servy

これを試して

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は素晴らしいです!

7
Georg

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);
2
XDS

たとえば、フォームコレクションを使用してWindowsフォームで開いているフォームをループするだけで、開いているすべてのフォームのウィンドウの開始位置を設定できます。

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

            foreach(Form f in fc)
            {
                f.StartPosition = FormStartPosition.CenterScreen;
            }
        }
1
Ashraf Abusada

すでに述べたように、フォームの各要素を単に繰り返すよりも深くする必要があります。残念ながら、これはネストされたループの使用を意味します。

最初のループで、各要素を循環します。要素がGroupBoxタイプの場合、続行する前にグループボックス内の各要素を循環する必要があることがわかります。それ以外の場合は、通常どおりイベントを追加します。

C#を十分に把握しているようですので、コードは提供しません。純粋に問題解決に関与するすべての重要な概念を確実に開発するために:)

1
christopher

私はこれが古いトピックであることを知っていますが、 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
});

オプションで、タグでコントロールをフィルタリングできます。

1
Roger

更新された回答:

グループボックスを含むフォーム内のすべてのコントロールを無効にする必要がありました。このコードは機能しました:

    private void AlterControlsEnable(bool ControlEnabled)
    {
        foreach (Control i in Controls)
            i.Enabled = ControlEnabled;
    }
0
Bogdan Doicin