WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

ASP.NET 4.5 ScriptManager

by WebSurfer 2018年4月23日 16:47

ASP.NET 4.5 以降での話ですが、クライアントスクリプトを利用するサーバーコントロールが正しく機能するには、必要なクライアントスクリプトの ScriptManager への登録と、全ページでの ScriptManager の配置が必要という話を書きます。

RequiredFieldValidator

元の話は MSDN Forum のスレッド「検証コントロール + マスターページ in WebサイトのWebアプリケーション」です。

MSDN Forum の話は検証コントロールによるクライアント側での検証が働かなかったということですが、ScriptManager を正しく使わないと、多分それ以外(ASP.NET AJAX Extensions など)にも影響があると思われます。

ASP.NET 4.5 では、Microsoft Ajax と WebForms 用のスクリプトファイルはアプリケーションの Scripts フォルダに格納し、そこから ScriptManager を介してダウンロードできるように改善されたそうです。(旧来は WebResource.axd, ScriptResource.axd というハンドラを使ってサーバーコントロールのリソースからダウンロードしていました)

さらに、jQuery, Bootstrap 等のスクリプトも、Microsoft Ajax と WebForms 用のスクリプトに加えて、ScriptManager で統合できるようになりました。

そのあたりの詳しい話は MSDN Blog の記事 ASP.NET 4.5 ScriptManager Improvements in WebForms に書いてありますので一読されると良いと思います。

Visual Studio Commnunity 2015 のテンプレートを利用して ASP.NET Web フォームアプリケーションを作成すると、以下のスクリプト関係の NuGet パッケージが自動的にプロジェクトにインストールされ、App_Start/BundleConfig.cs にバンドル定義のコード、Global.asax の Application_Start メソッドにバンドル定義を登録するためのコードが自動生成されます。

NuGet パッケージ

そして、マスターページ Site.Master に ScriptManager が配置され、必要なスクリプト参照が登録されます。Visual Studio Community 2015 の場合は以下の通りとなります。

<asp:ScriptManager runat="server">
  <Scripts>
    <asp:ScriptReference Name="MsAjaxBundle" />
    <asp:ScriptReference Name="jquery" />
    <asp:ScriptReference Name="bootstrap" />
    <asp:ScriptReference Name="respond" />
    <asp:ScriptReference Name="WebForms.js" 
        Assembly="System.Web" 
        Path="~/Scripts/WebForms/WebForms.js" />
    <asp:ScriptReference Name="WebUIValidation.js" 
        Assembly="System.Web" 
        Path="~/Scripts/WebForms/WebUIValidation.js" />
    <asp:ScriptReference Name="MenuStandards.js" 
        Assembly="System.Web" 
        Path="~/Scripts/WebForms/MenuStandards.js" />
    <asp:ScriptReference Name="GridView.js" 
        Assembly="System.Web" 
        Path="~/Scripts/WebForms/GridView.js" />
    <asp:ScriptReference Name="DetailsView.js" 
        Assembly="System.Web" 
        Path="~/Scripts/WebForms/DetailsView.js" />
    <asp:ScriptReference Name="TreeView.js" 
        Assembly="System.Web" 
        Path="~/Scripts/WebForms/TreeView.js" />
    <asp:ScriptReference Name="WebParts.js" 
        Assembly="System.Web" 
        Path="~/Scripts/WebForms/WebParts.js" />
    <asp:ScriptReference Name="Focus.js" 
        Assembly="System.Web" 
        Path="~/Scripts/WebForms/Focus.js" />
    <asp:ScriptReference Name="WebFormsBundle" />
  </Scripts>
</asp:ScriptManager>

この ScriptManager の新機能には重複排除の機能も含まれているようで、例えば検証コントロールを使っても、旧来だったら自動生成される WebResource.axd ハンドラを使うコードは生成されません。

テンプレートでアプリケーションを作ったら、必ず上記の <Scripts> ~ </Scripts> の間のコードを含めた ScriptManager が全ページに配置されるようにするのが正解と思われます。

単に <asp:ScriptManager runat="server"></asp:ScriptManager> としたら、検証コントロールを配置してもクライアント側での検証が動かなかったというのが上に紹介した MSDN Forum の話でした。

なお、テンプレートの選択で「Web フォーム」ではなく「空」を選んだ場合はスクリプトの設定は一切されず、例えばその状態で検証コントロールを使うと、以下のようなサーバーエラーとなります。

"WebForms UnobtrusiveValidationMode には、'jquery' の ScriptResourceMapping が必要です。jquery (大文字と小文字が区別されます) という名前の ScriptResourceMapping を追加してください。"

ASP.NET 4.5 で利用可能になった控えめな JavaScript による検証を止めて旧来のものに戻すとか(web.config の appSettings で設定可能)、クライアント側での検証を無効にすればエラーは回避できますが、それは望ましい解決法ではないので、エラーメッセージに従って対処した方がよさそうです。

また、検証コントロール以外にも、クライアントスクリプトを利用するサーバーコントロール(UpdatePanel とか)を使うと、必要なスクリプトがダウンロードされず期待通り動かないという問題が出そうな気がします。

なので、テンプレートで「空」を選択した場合は、最低でも jQuery, MSAjax, WebForms の 3 つの NuGet パッケージはインストールして、ScriptManager に登録し、全てのページに ScriptManager を配置するのがよさそうです。

その手順は別の記事「ASP.NET 4.5 スクリプトリソースマッピング」に書きましたので、興味があれば見てください。

Tags: ,

ASP.NET

Validator の Display="Dynamic" 時の注意点

by WebSurfer 2014年1月16日 17:45

ASP.NET の検証コントロール(RequiredFieldValidator, RegularExpressionValidator など)を使用して、Display プロパティを Dynamic に設定したときの注意点です。

Validator の Display="Dynamic" 時の注意点

この件は stackoverflow の記事 でも報告されています。ただ、実は自分はつい最近までこの問題は知らなかったです。(汗)

例えば、以下のように RequiredFieldValidator を配置して、その直後に Button を配置したとします。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  protected void Button1_Click(object sender, EventArgs e)
  {
    if (Page.IsValid)
    {
      // 何らかの処置。
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
      runat="server" 
      ErrorMessage="エラーメッセージ"  
      ControlToValidate="TextBox1" 
      Display="Dynamic">
    </asp:RequiredFieldValidator>
    <asp:Button ID="Button1" 
      runat="server" 
      Text="Button" 
      OnClick="Button1_Click" />                
  </div>
  </form>
</body>
</html>

上記のコードでは以下の手順で問題を再現できます。

  1. TextBox は空のまま Button をクリックする。
  2. クライアント側で検証がかかり、ErrorMessage プロパティに設定した「エラーメッセージ」が表示される。
  3. TextBox に文字を入力して Button をクリックする。 ⇒ 「エラーメッセージ」は消えるがポストバックがかからない。
  4. 再度 Button をクリックする。 ⇒ ポストバックがかかる。

期待される動きは、上記 3 で「エラーメッセージ」が消えるとともにポストバックがかかるということのはずですが、そうはなりません。

上記のコードにおける解決策は、Button の直前に改行 ( <br /> ) を入れることです。そうしないとうまく行かない理由は以下の通りです。

Validator のエラーメッセージは html コードでは span 要素となり、JavaScript による検証結果により表示/非表示を切り替えています。

Display プロパティが Dynamic に設定されている場合は、当該 span 要素の style 属性を "display:none;" または "display:inline;" に設定することにより表示/非常時を切り替えます。

検証対象の TextBox にフォーカスを当ててからフォーカスを外す(例えば、TextBox に入力してから form を submit するために Button をクリックする)と、 そのタイミングで JavaScript による検証がかかるようになっています。

検証結果によって "display:inline;" が "display:none;" に(またはその逆に)書き換えられるので、エラーメッセージの部分のページレイアウトが変わることになります。

従って、上記のコードのように、RequiredFieldValidator の直後に Button が配置されているような場合、エラーメッセージが表示/非表示になる分だけ画面上でボタンが左右に移動します。

ボタンが移動すると、<input type="submit" ... /> タイプのボタンをクリックしたにもかかわらず form が submit されません。 即ち、ポストバックされないという期待に反する動作になります。

なお Button が動くのは左右でなくても、例えばエラーメッセージを p 要素に入れると上下に移動しますが、その場合でも同じくform は submit されません。

このことは、ASP.NET の検証コントロールを使った場合に限った話ではなく、html 要素と JavaScript だけでも再現できます。 以下のサンプルコードは、html 要素と JavaScript だけでこの問題(ボタンが動くと submit されない)を再現する例です。

サンプルコードを実際に動かして試すことができるよう 実験室 にアップしました。興味のある方は試してみてください。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>WebSurfer's Page - 実験室</title>
  <script src="/scripts/jquery-1.8.3.min.js" type="text/javascript">
  </script>
  <script type="text/javascript">
  //<![CDATA[
    function toggleDisplay() {
      var validator = $('#RequiredFieldValidator1');
      if (validator.css("display") == "none") {
        validator.css("display", "inline");
      } else {
        validator.css("display", "none");
      }            
    }
  //]]>
  </script>
</head>
<body>
  <form id="form1" runat="server" 
    onsubmit="javascript:return confirm('Submit しますか?');">

  <div>
    <input name="textbox1" type="text" id="1extbox1" 
      onblur="javascript: toggleDisplay();" />
    <span id="RequiredFieldValidator1" 
      style="display:none;">エラーメッセージ</span>
    <input type="submit" name="button1" value="POST" 
      id="button1" />    
  </div>
  </form>
</body>
</html>

ちなみに、Display プロパティが Static に設定されている場合は、style 属性を "visibility:hidden;" または "visibility:visible" に設定するので、 表示/非表示を切り替えてもページのレイアウトは変わりません。結果、ボタンは動かないのでこの問題は起こりません。

Tags: ,

Validation

CustomValidator と RequiredFieldValidator

by WebSurfer 2012年7月22日 12:21

CustomValidatorRequiredFieldValidator について新発見(?)がありましたので、忘れないように書いておきます。

(1) CustomValidator

RequiredFieldValidator 以外では、未入力に対する検証結果は OK 扱いになると思っていましたが、CustomValidator の場合は例外がありました。

それは、.NET 2.0 で追加された ValidateEmptyText プロパティ を true にすることです。

ValidateEmptyText プロパティがデフォルトの false の場合は、RegularExpressionValidator や RangeValidator などと同様に、未入力の場合はその値を評価することなく、検証結果は OK 扱いになります。

ValidateEmptyText プロパティを true にすることにより、検証対象コントロールが未入力でも、CustomValidator は、プログラマが設定した検証ロジックを使用して検証対象のコントロールの値を評価し、検証結果を返します。

では、CustomValidator の ValidateEmptyText プロパティを true にして空白の検証を可能にし、RequiredFieldValidator を使わないで済ませられるかと言えば、機能的にはそれで問題なくても、ユーザビリティの面では問題がありそうです。

検証コントロールは、ユーザーに適切なエラーメッセージを提供するということも重要な目的の一つだそうです。検証条件が異なれば適切なエラーメッセージは異なるはずです。

検証コントロールのエラーメッセージは固定的なので、CustomValidator 一つで済ませる場合、エラーメッセージは "未入力もしくは入力形式がXXXと異なります" とせざるを得ません。

それより、RequiredFieldValidator と CustomValidator を併用して、未入力の時は "未入力です" というエラーメッセージを、入力はあるが形式が違っている場合は "入力形式がXXXと異なります" とした方がユーザーフレンドリーだと思います。

というわけで、結局、未入力の検証には、RequiredFieldValidator を使用するのが正解のようです。

なお、上記で言う「未入力」とは String.Empty だけではなくて、半角/全角スペースも含まれますので注意してください。実は、これも今回調べるまで知らなかったです。(笑)

あともうひとつ。CustomValidator がデフォルトで未入力でも検証 OK とするのには理由があります。それは、RequiredFieldValidator と併用した場合、未入力の際に 2 つのエラーメッセージが同時に表示されるのはユーザーにとって煩わしいからだそうです。

(2) RequiredFieldValidator

RequiredFieldValidator は「未入力」をチェックするものだと思っていましたが、正確にはそうではなかったです。

MSDN ライブラリに書いてあったのですが、実は、"入力コントロールに対する検証は、そのコントロールがフォーカスを失ったときに、その値が InitialValue プロパティ の値から変更されていない場合は失敗になります。" ということだそうです。

従って、MSDN ライブラリのサンプルコードにあるように、検証対象の TextBox の Text プロパティに "Enter a value" と設定しておき、RequiredFieldValidator の InitialValue プロパティにも同じ文字列 "Enter a value" を設定しておけば、ユーザーが初期値を変更しなかった場合に検証 NG とします。

InitialValue プロパティのデフォルト値は String.Empty、TextBox の Text プロパティもデフォルトで String.Empty なので、結果的に「未入力」の検証が可能になるということです。

なお、全角/半角スペースを入力しても検証 NG となるのは、MSDN ライブラリにも書いてありますように、"InitialValue プロパティと入力コントロールの両方の文字列は、検証が実行される前にその文字列の前後の余分な空白が削除されてトリムされます。" からです。

Tags:

Validation

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  2018年8月  >>
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar