by WebSurfer
2020年6月27日 12:03
ASP.NET Web Forms アプリでユーザー入力の検証に用いられる CustomValidator のクライアント側での検証について調べたことをまとめて備忘録として書いておきます。
CustomValidator は、プログラマが独自の検証ロジックをコーディングして検証メソッドとしてページに実装し、ユーザー入力の検証を行うために利用されます。複数の入力コントロールにまたがって検証を行うことも可能です。
また、RegularExpressionValidator などと異なり、TextBox の他に CheckBox, RadioButton, DropDownList, FileUpload などのユーザー入力コントロールの検証に利用できます。
クライアント側での検証は、JavaScript を使って検証用メソッドを自力でコーディングし、それをページに実装することで可能になります(サーバー側でなければ検証できない場合は話は別です。ajax を使う手はいろいろ問題がありそうです。詳しくは先の記事「CustomValidator で jQuery.ajax 利用」を見てください)。
この記事の下の方に TextBox, CheckBox, RadioButton, DropDownList, FileUpload を対象として、CustomValidator によるクライアント側での検証を実装したサンプルコードを書いておきます。上の画像を表示したものです。
自分的に注意が必要と思う点を以下に箇条書きにしておきます。
-
クライアント側での検証は html 要素の change イベントでかかるようになっています。CustomValidator を change イベントで動くようにするには ControlToValidate プロパティの設定が必要です。(注: submit でも検証がかかります。というか、change で検証がかかるのはユーザビリティ向上のためで、submit 時の検証がメインです)
-
CheckBox, RadioButton コントロールに対しては CustomValidator の ControlToValidate プロパティを設定できません。設定すると HttpException がスローされ、例えば CheckBox の場合は「'CustomValidator' の ControlToValidate プロパティで参照されたコントロール 'CheckBox' を検証できません。」というエラーメッセージが表示されます。
エラーとなる直接の理由は、ASP.NET 内部で CheckControlValidationProperty メソッドによる検証対象コントロールのチェックを行っていますが、CheckBox RadioButton コントロールには ValidationPropertyAttribute 属性が付与されてないためないためです。
そもそもの理由は、Microsoft のドキュメントによると「ControlToValidate プロパティを設定せずに CustomValidator コントロールを使用することもできます。 これは、複数の入力コントロールを検証する場合や、CheckBox コントロールなどの検証コントロールで使用できない入力コントロールを検証する場合に一般的に行われます」とのことで、もともと CheckBox や RadioButton は検証コントロールを使う対象外のように読めます。
-
TextBox, DropDownList, FileUpload コントロールについては、CustomValidator の ControlToValidate プロパティを検証対象コントロールの ID に設定すれば change イベントで検証がかかります。
なお、ControlToValidate プロパティを設定しなくても submit で検証はかかりますので、change イベントでいちいち検証がかかるのは煩わしいという場合は設定しない方がよさそうです。(RequiredFieldValidator など他の検証コントロールは ControlToValidate プロパティを設定しないとエラーになりますので注意してください。CustomValidator だけ特別です)。
-
どういう html 要素がどのタイミングで change イベントを発生させるかについては MDN の記事 HTMLElement: change event を見てください。その記事に書いてある通り、TextBox はユーザーが入力してフォーカスを外した時、DropDownList はユーザーが選択を変更したとき、FileUpload はユーザーがファイルを選択したとき change イベントが発生し、CustomValidator の ControlToValidate プロパティが設定されていれば検証がかかります。
CheckBox (input type="checkbox"), RadioButton (input type="radio") も change イベントは発生しますが、上に述べたように CustomValidator の ControlToValidate プロパティを設定できないので、change イベントでは CustomValidator による検証はかかりません(submit で検証されます)。
-
クライアント側での検証用 JavaScript のメソッドは CustomValidator の ClientValidationFunction プロパティに設定します。メソッド名が例えば Validate(sender, args) とすると、sender には CustomValidator が html に変換された span 要素が渡されます。args には IsValid, Value プロパティを持つ JavaScript オブジェクトが渡されます。
CustomValidator の ControlToValidate プロパティが検証対象コントロールに対して設定してある場合は、args.Value には検証対象の入力コントロールが html に変換された input 要素の value 属性の値が渡されます。ControlToValidate プロパティが設定されてない場合は args.Value は空 "" になります。
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master"
AutoEventWireup="true" CodeBehind="WebForm6.aspx.cs"
Inherits="WebApplication1.WebForm6" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent"
runat="server">
<script type="text/javascript">
//<![CDATA[
// ControlToValidate が設定されてない場合、引数 args には
// テキストボックスの値が渡されないので注意。args に頼らず
// 以下のようにしておくのがよさそう
function TextBoxValidate(sender, args) {
var tb =
document.getElementById('<%= TextBox1.ClientID%>');
var membership = tb.value.toLowerCase();
if (membership === "gold" || membership === "silver") {
args.IsValid = true;
} else {
args.IsValid = false;
}
}
function CheckBoxValidate(sender, args) {
var cb =
document.getElementById('<%= CheckBox1.ClientID%>');
if (cb.checked == true) {
args.IsValid = false;
} else {
args.IsValid = true;
}
}
function RadioButtonValidate(sender, args) {
var rb =
document.getElementById('<%= RadioButton1.ClientID%>');
if (rb.checked == true) {
args.IsValid = false;
} else {
args.IsValid = true;
}
}
// ControlToValidate が設定されてない場合、引数 args には
// テキストボックスの値が渡されないので注意。args に頼らず
// 以下のようにしておくのがよさそう
function DropDwonListValidate(sender, args) {
var ddl =
document.getElementById('<%= DropDownList1.ClientID%>');
if (ddl.value == "2") {
args.IsValid = false;
} else {
args.IsValid = true;
}
}
function FileUploadValidate(sender, args) {
if (window.File && window.FileList) {
var fileUpload =
document.getElementById("<%=FileUpload1.ClientID%>");
if (fileUpload.files[0] == null) {
args.IsValid = false;
return;
}
if (fileUpload.files[0].type != "image/jpeg") {
args.IsValid = false;
return;
}
} else {
args.IsValid = true;
}
}
//]]>
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent"
runat="server">
<h1>CustomValidator</h1>
<p>CustomValidator のクライアント側での検証のタイミング</p>
<table>
<tr>
<td>
TextBox
</td>
<td>
<asp:TextBox ID="TextBox1" runat="server">
</asp:TextBox>
</td>
<td>
<asp:CustomValidator ID="CustomValidator1"
runat="server"
ForeColor="Red"
Display="Dynamic"
ErrorMessage="Gold または Silver でない"
ClientValidationFunction="TextBoxValidate"
ControlToValidate="TextBox1">
</asp:CustomValidator>
</td>
</tr>
<tr>
<td>
CheckBox
</td>
<td>
<asp:CheckBox ID="CheckBox1" runat="server" />
</td>
<td>
<%--ControlToValidate="CheckBox1" を設定すると
HttpException がスローされる。理由は CheckBox
には ValidationProperty 属性が付与されてない
から。なので submit しないと検証はかからない
--%>
<asp:CustomValidator ID="CustomValidator2"
runat="server"
ForeColor="Red"
Display="Dynamic"
ErrorMessage="チェック不可"
ClientValidationFunction="CheckBoxValidate">
</asp:CustomValidator>
</td>
</tr>
<tr>
<td>
RadioButton
</td>
<td>
<asp:RadioButton ID="RadioButton1" runat="server" />
</td>
<td>
<%--ControlToValidate="RadioButton1" を設定すると
HttpException がスローされる。理由は RadioButton
には ValidationProperty 属性が付与されてないから。
なので submit しないと検証はかからない
--%>
<asp:CustomValidator ID="CustomValidator3"
runat="server"
ForeColor="Red"
Display="Dynamic"
ErrorMessage="選択不可"
ClientValidationFunction="RadioButtonValidate">
</asp:CustomValidator>
</td>
</tr>
<tr>
<td>
DropDownList
</td>
<td>
<asp:DropDownList ID="DropDownList1" runat="server">
<asp:ListItem>0</asp:ListItem>
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
</asp:DropDownList>
</td>
<td>
<asp:CustomValidator ID="CustomValidator4"
runat="server"
ForeColor="Red"
Display="Dynamic"
ErrorMessage="2 は選択不可"
ClientValidationFunction="DropDwonListValidate"
ControlToValidate="DropDownList1">
</asp:CustomValidator>
</td>
</tr>
<tr>
<td>
FileUpload
</td>
<td>
<asp:FileUpload ID="FileUpload1" runat="server" />
</td>
<td>
<asp:CustomValidator ID="CustomValidator5"
runat="server"
ForeColor="Red"
Display="Dynamic"
ErrorMessage="jpg ファイル以外不可"
ClientValidationFunction="FileUploadValidate"
ControlToValidate="FileUpload1">
</asp:CustomValidator>
</td>
</tr>
</table>
<asp:Button ID="Button1" runat="server" Text="Submit" />
</asp:Content>
上のコードはマスターページを利用しています。先の記事「ASP.NET 4.5 ScriptManager」で書きましたように、ASP.NET 4.5 以降でクライアントスクリプトを利用するサーバーコントロールが正しく機能するには、必要なクライアントスクリプトの ScriptManager への登録と全ページでの ScriptManager の配置が必要です。マスターページを使ってそのあたりを解決しています。