|   | 
| 
 .NET TIPS 
[ASP.NET]複数のボタンが存在するフォーム上で検証コントロールを利用するには?
山田 祥寛 
2005/01/14 | 
  | 
 
 | 
  「Visual Studio .NETでプログラム・レス開発を学ぶ(前編)」でも紹介しているように、ASP.NETの検証コントロールは入力データの妥当性チェックをプログラム・レスで実現してくれる優れものだ。
 しかし、複数のサブミット・ボタン(=送信ボタン)を持つフォーム上で検証コントロールを利用する場合には、気をつけなければならない点がある。
 例えば、以下のようにサブミット・ボタンが複数個存在するWebフォームを想定してみよう。
 
  | 
 
| サブミット・ボタンが複数個存在するWebフォーム | 
 
| [購読開始]ボタンと[購読取消]ボタンという2つのサブミット・ボタン(送信ボタン)がフォーム上に配置されている。 | 
 ここでは、[購読取消]ボタンをクリックしたタイミングで、その購読取消のすぐ上にある[E-Mailアドレス]の必須チェックを検証コントロールにより行いたいわけだが、デフォルト状態ではこれはうまく動作しないはずだ。というのも、[購読取消]ボタンをクリックしても、購読開始の項目である[名前]や[E-Mailアドレス]テキストボックスに用意された検証コントロールが動作してしまうためだ。つまり、デフォルトでは、購読取消を行うために購読開始の項目であるテキストボックスも入力しなければならないことになってしまう。
 以下の画面は実際に、[購読取消]ボタンをクリックしたところだ。
 
  | 
 
| 意図しない検証処理の発生 | 
 
| [購読取消]ボタンをクリックしたにもかかわらず、購読開始の入力項目の検証処理まで働いてしまう。 | 
 これは当然、期待されるべき挙動ではない。このフォームの動作仕様として、[購読取消]をクリックしたときには、購読開始の入力項目の検証処理は実行されてはならない。よって、その検証コントロールの処理を無効にする必要がある。
■検証コントロールの無効化
 そこで登場するのがCausesValidation属性だ。CausesValidation属性は、Button、LinkButton、ImageButtonなどの、いわゆる「ボタン系コントロール」で利用可能な属性で、これをFalseに設定することでWebフォーム上の検証コントロールを一時的に無効にできる。つまり、上記のWebフォームならば、[購読取消]ボタンに対してCausesValidation属性をFalseに設定することで、無用な検証コントロールの動作を抑制することができる。具体的には、上記のWebフォームで[購読取消]ボタンを無効にするには、次のようなコードを記述すればよい。
 
<asp:Button id="btnStop" runat="Server" Text="購読取消" CausesValidation="False"/> | 
 ただし、CausesValidation属性は、あくまでコントロールに対する検証コントロールの実行を「無効にする」にすぎない。クリックするボタンによって検証処理を実行する検証コントロールを「切り替える」には(つまり検証コントロールを選択的に無効化するには)、CausesValidation属性は利用できないのだ。
 そこで本稿では、クリックされたボタンごとに検証処理を実行する検証コントロールを切り替えるためのテクニックを紹介する。
■ボタンごとに適用する検証コントロールを切り替えるテクニック
 さっそく、具体的なコードを見てみよう。
<%@ Page ContentType="text/html" Language="C#" %> 
<script runat="Server"> 
void start_Click(Object sender, EventArgs e){ 
  // 検証コントロール割り当てのメソッドを呼び出し 
  ValidMapping(sender); 
  // 検証処理を実行 
  Page.Validate(); 
  if(Page.IsValid){ 
    // 検証に成功した場合の処理 
  } 
} 
void stop_Click(Object sender, EventArgs e){ 
  // 検証コントロール割り当てのメソッドを呼び出し 
  ValidMapping(sender); 
  // 検証処理を実行 
  Page.Validate(); 
  if(Page.IsValid){ 
    // 検証に成功した場合の処理 
  } 
} 
void ValidMapping(Object sender){ 
  WebControl objSnd=(WebControl)sender; 
  // フォーム内に含まれる検証コントロールを順に取得 
  IEnumerator objEnm=Page.Validators.GetEnumerator(); 
  while(objEnm.MoveNext()){ 
    BaseValidator objVal=(BaseValidator)objEnm.Current; 
    // 検証コントロールのIDが 
    // 「<イベント発生元コントロールのID>_」で 
    // 始まっている場合には有効化し、 
    // それ以外のものは無効にする 
    if(objVal.ID.StartsWith(objSnd.ID + "_")){ 
      objVal.Enabled=true; 
    }else{ 
      objVal.Enabled=false; 
    } 
  } 
} 
</script> 
<html> 
<head> 
<title>検証コントロールのグルーピング</title> 
</head> 
<body> 
<form runat="server"> 
<h1>メールニュース 購読開始</h1> 
名前: 
<asp:TextBox id="txtNam" runat="Server" Size="20" /> 
<asp:RequiredFieldValidator id="start_nam" runat="Server" 
  ControlToValidate="txtNam" Enabled="False" EnableClientScript="False" 
  ErrorMessage="名前は必須入力です。" /> 
<br /> 
E-Mail: 
<asp:TextBox id="txtEmail" runat="Server" Size="50" /> 
<asp:RequiredFieldValidator id="start_email" runat="Server" 
  ControlToValidate="txtEmail" Enabled="False" EnableClientScript="False" 
  ErrorMessage="E-Mailは必須入力です。" /> 
<br /> 
<asp:Button id="start" runat="Server" Text="購読開始" 
  OnClick="start_Click" /> 
<hr /> 
<h1>メールニュース 購読取消</h1> 
E-Mail: 
<asp:TextBox id="txtEmail2" runat="Server" Size="50" /> 
<asp:RequiredFieldValidator id="stop_email" runat="Server" 
  ControlToValidate="txtEmail2" Enabled="False" EnableClientScript="False" 
  ErrorMessage="E-Mailは必須入力です。" /><br /> 
<asp:Button id="stop" runat="Server" Text="購読取消" 
  OnClick="stop_Click" /> 
</form> 
</body> 
</html>
 | 
 
 
 | 
 
| 検証コントロールのグループ化を実装する.aspxファイル(C#の場合) | 
 
<%@ Page ContentType="text/html" Language="VB" %> 
<script runat="Server"> 
Sub start_Click(sender As Object, e As EventArgs) 
  ' 検証コントロール割り当てのメソッドを呼び出し 
  ValidMapping(sender) 
  ' 検証処理を実行 
  Page.Validate() 
  If Page.IsValid Then 
    ' 検証に成功した場合の処理 
  End If 
End Sub 
Sub stop_Click(sender As Object, e As EventArgs) 
  ' 検証コントロール割り当てのメソッドを呼び出し 
  ValidMapping(sender) 
  ' 検証処理を実行 
  Page.Validate() 
  If Page.IsValid Then 
    ' 検証に成功した場合の処理 
  End If 
End Sub 
Sub ValidMapping(sender As Object) 
  Dim objSnd As WebControl = CType(sender,WebControl) 
  ' フォーム内に含まれる検証コントロールを順に取得 
  Dim objEnm As IEnumerator=Page.Validators.GetEnumerator() 
  Do While objEnm.MoveNext() 
    Dim objVal As BaseValidator=CType(objEnm.Current,BaseValidator) 
    ' 検証コントロールのIDが 
    ' 「<イベント発生元コントロールのID>_」で 
    ' 始まっている場合には有効化し、 
    ' それ以外のものは無効にする 
    If objVal.ID.StartsWith(objSnd.ID & "_") Then 
      objVal.Enabled=True 
    Else 
      objVal.Enabled=False 
    End If 
  Loop 
End Sub 
</script> 
<html> 
<head> 
<title>検証コントロールのグルーピング</title> 
</head> 
<body> 
<form runat="server"> 
<h1>メールニュース 購読開始</h1> 
名前: 
<asp:TextBox id="txtNam" runat="Server" Size="20" /> 
<asp:RequiredFieldValidator id="start_nam" runat="Server" 
  ControlToValidate="txtNam" Enabled="False" EnableClientScript="False" 
  ErrorMessage="名前は必須入力です。" /> 
<br /> 
E-Mail: 
<asp:TextBox id="txtEmail" runat="Server" Size="50" /> 
<asp:RequiredFieldValidator id="start_email" runat="Server" 
  ControlToValidate="txtEmail" Enabled="False" EnableClientScript="False" 
  ErrorMessage="E-Mailは必須入力です。" /> 
<br /> 
<asp:Button id="start" runat="Server" Text="購読開始" 
  OnClick="start_Click" /> 
<hr /> 
<h1>メールニュース 購読取消</h1> 
E-Mail: 
<asp:TextBox id="txtEmail2" runat="Server" Size="50" /> 
<asp:RequiredFieldValidator id="stop_email" runat="Server" 
  ControlToValidate="txtEmail2" Enabled="False" EnableClientScript="False" 
  ErrorMessage="E-Mailは必須入力です。" /><br /> 
<asp:Button id="stop" runat="Server" Text="購読取消" 
  OnClick="stop_Click" /> 
</form> 
</body> 
</html>
 | 
 
 
 | 
 
| 検証コントロールのグループ化を実装する.aspxファイル(VB.NETの場合) | 
 以上のサンプルを実行すると、今度は[購読開始]ボタンをクリックしたところで購読開始フォームに割り当てられた検証コントロールだけが、[購読取消]ボタンをクリックしたところで購読取消フォームに割り当てられた検証コントロールだけが、それぞれ動作するはずだ。次の画面は実際の実行画面である。
  | 
  | 
 
  | 
 
| [購読開始]ボタン/[購読取消]ボタンをクリックした場合 | 
 
| それぞれ必要な検証コントロールだけが動作しているのが確認できる。 | 
 ロジックの詳細はコード内に記述されたコメントを参照いただくとして、そのロジックでポイントとなるのは、以下の2点である。
(1)検証コントロールのIDは「<ボタン系コントロールのID>_任意の文字列」とする
 ASP.NET 1.xの検証コントロールには、ボタン系コントロールとの関連付けを行うための専用のプロパティは用意されていない。そこで、本稿ではIDプロパティの値を利用してひも付けを行っている。ValidMappingメソッドの中身をご覧いただけば分かるように、本サンプルでは、検証コントロールのIDが「<イベント発生元コントロールのID>_」で始まっている場合に、これを現在のイベントで処理すべき検証コントロールであると見なしている。
(2)検証コントロールのEnabled属性、EnableClientScript属性はFalseに設定する
 クリック・イベントと検証コントロールとの関連付けは、サーバ側で動的に行われるものだ。つまり、適切な関連付けが設定される前に、クライアント側で勝手に検証処理を行わせるべきではない。デフォルト状態では、すべての検証コントロールを無効に設定しておこう。
 なお、本稿では誌面の都合上、検証コントロールとクリック・イベントとを関連付けるためのValidateMappingメソッドを.aspxファイル上に記述している。しかし、ValidateMappingメソッドはほかの.aspxファイルでも汎用的に利用可能なメソッドなので、再利用性を考慮してビジネス・オブジェクトとして外部化しておくのが好ましい。
 
 
|  
 | 
 
generated by  
 | 
 
 
 |