特定のクラスのみ操作したい

今回はC#とASP.NETの話です。

Webページで登録画面を作成していて、
必須入力項目への入力確認に、
RequiredFieldValidatorクラスを使用しました。

バリデーションを行うコントロール名とエラーメッセージをセットすれば、
指定したコントロールに対して、入力がなされたかクライアント側で確認することができます。

ただし、今回作成している画面が少し厄介で、
ドロップダウンリストの選択した値によって、
必須入力が変更される、という仕様を盛り込んでいました。

そこで、SelectedIndexChangedイベントを使用し、
選択した値が変更した時に、RequiredFieldValidatorの設定を切り替えるようにしました。

初めは、選択した値ごとに、RequiredFieldValidatorのEnabledプロパティに
TrueやFalseをセットしていたのですが、
これだと、Falseのセット漏れが出たりするなぁ、と思い、
一度、ページ内の全てのRequiredFieldValidatorにFalseをセットした後、
必要なコントロールのみ、Trueを設定しようと考えました。

では、どの様にして、全てのRequiredFieldValidatorコントロールに
Falseをセットするのか、考えました。
ASP.NETの各コントロールが継承するクラスにControlクラスが有ります。
このControlクラスには、FindControlというメソッドが有りますが、
これはコントロールのIDでしか探せません。
それに加え、コントロールは階層配置されており、
FindControlメソッドでは、呼出元のコントロールが有する子コントロールが
対象となります。

そこで、下記の記事を参考にし、再帰的に呼び出されるメソッドを作成しました。
下記記事では、IDを使用していますが、今回はクラス名で検索する様にします。

方法: ID を使用してサーバー コントロールにアクセスする

http://msdn.microsoft.com/ja-jp/library/y81z8326(v=vs.100).aspx

上記記事内に記載されているメソッドからの変更点は、
1.引数のIDをクラス名に変更する。
2.コントロールとIDの照合を
コントロールのクラス名と1で変更した引数と照合する。


    public static Control FindControlRecursive(Control rootControl,
                                               string findControlClassName)
    {
        if (rootControl.GetType().Name == findControlClassName)
        {
            return rootControl;
        }

        foreach (System.Web.UI.Control searchControl in rootControl.Controls)
        {
            Control controlToReturn =
                FindControlRecursive(searchControl, findControlClassName);
            if (controlToReturn != null)
            {
                return controlToReturn;
            }
        }
        return null;
    }

このメソッドを実行することで、rootControl以下のコントロールから
検索対象とするクラス名を有するコントロールを取得できます。
後は、取得したコントロールのparentプロパティで、
直近の親コントロールを取得し、
親コントロールが持つ子コントロールをforeachで回し、
GetType().Name で同一のクラス名を持つコントロールのEnabledプロパティに
FALSEをセットすれば、Webページ内のRequiredFieldValidatorオブジェクトを
無効化できます。

ちなみに、呼出元は以下の様なコードを書きました。


    // ページ内のコントロールを対象とする。
    foreach (Control pageChild in Page.Controls)
    {
        // RequiredFieldValidatorクラスのコントロールを検索する。
        var findControl = FindControlRecursive(pageChild,
                                  "RequiredFieldValidator");

        if (findControl != null)
        {
            // 検索結果のコントロールの親コントロールを取得する。
            var parentControl = findControl.Parent;

            // 親コントロールの全てのvalidatorを無効にする。
            foreach (Control childControl in parentControl.Controls)
            {
                If (childControl.GetType().Name == "RequiredFieldValidator")
                {
                    RequiredFieldValidator requiredFieldControl =
                        (RequiredFieldValidator)childControl;
                    requiredFieldControl.Enabled = false;
                }
            }
        }
    }

サーバサイドで同一のクラスを持つコントロールに
一律の操作をする際に、使えると思います。