CodeZine(コードジン)

特集ページ一覧

日付入力ができるポップアップウィンドウを作成する

ASP.NETのCalendarコントロールを利用したポップアップウィンドウの実装

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2005/07/20 12:00

本稿ではASP.NETで、テキストボックスに日付を入力するためのカレンダを、ポップアップウィンドウで表示する方法を解説します。また、それに伴うポストバックの問題点と改良案も併せて紹介します。

前提知識

  • C#、Visual Studio .NETに精通し、ASP.NETアプリケーションの作成に習熟していること
  • .NETのイベントについての基礎知識があること
  • JavaScriptの基礎知識があること
  • HTMLの基礎知識があること

はじめに

 少し前、私はWebフォームに取り組んでいて、ユーザーがTextBoxコントロールに簡単に日付を入力できるような方法を実現したいと考えていました。クラシックASPの時代には、小さなASPページをポップアップウィンドウで開き、そこでカレンダを表示するという方法をよく使用していました。これで、ユーザーは前月/翌月に移動することができ、日付をクリックすると、選択された日付が基本的なJavaScriptによって入力フィールドに入力されました。

 しかし、今は.NETの時代です。私は、ASP.NETに組み込まれているリッチコントロールを利用して、これを簡単に行う方法を考え出したいと思いました。そこでGoogleで軽く検索をかけてみた結果、解決策が見つかりました。それは、ASP.NET CalendarコントロールのSelectionChangedイベントに新しいEventHanderを追加するという方法です。これで、TextBoxの値を設定するのに必要なJavaScriptを実行するわけです。

 この方法を試したところ、すべてうまくいきました。サードパーティコントロールを使用したり、自分で一から作成することなく、望んでいた機能を実現するソリューションを手に入れたのです。しかし、それはテストの始まりでした。

 すべて動作はしたものの、一方で、ユーザーが日付をクリックしてからTextBoxに値が入力されるまでに2、3秒の遅れが発生するという問題がありました。しかし、その原因がPostBackにあることに気付くまでに長くはかかりませんでした。

 CalendarコントロールはJavaScriptを使用してTextBoxの値を設定しているのですが、その処理をSelectionChangedイベントの発生時に行っていました。このイベントはPostBack時に発生するため、ユーザーがクリックしてからTextBoxにデータが入力されるまでに遅延が生じていたのです。PostBackは、自分自身に対してWebフォームを発行します。これは、ユーザーに無駄な待ち時間を与え、Webサーバーに不要な負荷をかける原因になります。

改良案の模索

 私は依然として、自分でカスタムのカレンダコントロールを作るつもりはありませんでしたが、このPostBack方式には不満がありました。そこで、ASP.NET Calendarコントロールの機能を拡張する方法を探ることにしました。特に、CalendarコントロールのDayRenderメソッドに興味を引かれたので、あれこれ調べてみました。

 その結果、カレンダの個々の日付から標準の日付リンクを消去し、自分でリンクを作成できることを発見しました。そこで、TextBoxに値を入力してポップアップウィンドウを閉じるためのHTMLとJavaScriptをリンクに組み込むことにしました。さて、ここからは実際の作業です。

ソリューションの作成

 では、今回のソリューションを構成する要素を見てみましょう。

  • TextBoxコントロールを備えたWebフォーム。TextBoxの値はカレンダによって入力される
  • 日付入力ポップアップを起動するJavaScriptを備えた、Webフォーム上のリンク
  • 日付入力ポップアップのためのWebフォーム
  • 特別なDayRenderEventHandlerがアタッチされた、日付入力ポップアップページ上のCalendarコントロール
  • 標準の日付リンクをカスタムのJavaScriptリンクで置き換えるためのDayRenderEventHandlerデリゲート

 Webフォームは、次のように単純なものです。

 以下は「pick」リンクのHTMLです。このリンクから、日付入力ポップアップが起動します。

<a href="javascript:;" onclick="calendarPicker('Form1.txtEventDate');" 
    title="Pick Date from Calendar">pick</a>

 <a>タグにrunat="server"属性が指定されていないので、onClickイベントはサーバー上でなく、クライアント側(ブラウザ内)で発生します。

 コードからわかるとおり、このリンクはcalendarPickerというJavaScript関数を呼び出します。フォーム上に複数の日付フィールドを用意する場合を考慮して、私はインラインでコードを書くのではなく、リンクから関数を呼び出す方法を採用しました。パラメータを使用して、設定したいフィールドへの参照を指定する関数を作成し、その関数内にコードを書いておけば、単一ページ上のTextBoxコントロール用に、好きなだけコードを再利用することができます。JavaScript関数をデバッグすることもできますが、それについては後で説明します。

 calendarPicker関数には、「FormName.FieldName」形式で、編集したいフィールドの名前を引き渡すことにします。VS .NETの標準の「Form1」の代わりに独自のフォームIDを使用したい場合があるかもしれませんから、この部分をハードコードするのはお勧めしません。

 calendarPicker関数は次のとおりです。

<script language="javascript" type="text/javascript">
    /// <summary>
    /// Launches the DatePicker page in a popup window,
    /// passing a JavaScript reference to the field that we want to set.
    /// </summary>
    /// <param name="strField">String. 
    /// The JavaScript reference to the field that we want to set,
    /// in the format: FormName.FieldName
    /// Please note that JavaScript is case-sensitive.</param>
    function calendarPicker(strField)
    {
        window.open('DatePicker.aspx?field=' + strField,
         'calendarPopup', 'width=250,height=190,resizable=yes');
    }
</script>

 calendarPicker関数は、新しいウィンドウの中に「DatePicker.aspx」というページを開きます。この関数は、カレンダで設定するフィールドの名前をQueryString内に設定します。私は、クライアントサイドスクリプトにはVisual Studioスタイルのコメントを使用するのが好きです。というのは、コードの文書化を習慣付けてくれるからです。

 次に示すのは、DatePicker ASPXページです。

 DatePickerページは、1つの標準ASP.NET Calendarコントロールを含んでいます。

<%@ Page language="c#" Codebehind="DatePicker.aspx.cs" AutoEventWireup="false" 
    Inherits="CalendarPopup.DatePicker" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<html>
    <head>
        <title>DatePicker</title>
        <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
        <meta name="CODE_LANGUAGE" content="C#">
        <meta name="vs_defaultClientScript" content="JavaScript">
        <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
        <style type="text/css">
        BODY { PADDING-RIGHT: 0px; PADDING-LEFT: 0px; 
            PADDING-BOTTOM: 0px; MARGIN: 4px; PADDING-TOP: 0px }
        BODY { FONT-SIZE: 9pt; FONT-FAMILY: Verdana, Geneva, Sans-Serif }
        TABLE { FONT-SIZE: 9pt; FONT-FAMILY: Verdana, Geneva, Sans-Serif }
        TR { FONT-SIZE: 9pt; FONT-FAMILY: Verdana, Geneva, Sans-Serif }
        TD { FONT-SIZE: 9pt; FONT-FAMILY: Verdana, Geneva, Sans-Serif }
        </style>
    </head>
    <body onblur="this.window.focus();" ms_positioning="FlowLayout">
        <form id="Form1" method="post" runat="server">
            <div align="center">
            <asp:calendar id="Calendar1" runat="server" 
                showgridlines="True" bordercolor="Black">
                <todaydaystyle forecolor="White" backcolor="#FFCC66"/>
                <selectorstyle backcolor="#FFCC66"/>
                <nextprevstyle font-size="9pt" forecolor="#FFFFCC"/>
                <dayheaderstyle height="1px" backcolor="#FFCC66"/>
                <selecteddaystyle font-bold="True" backcolor="#CCCCFF/>
                <titlestyle font-size="9pt" font-bold="True" 
                    forecolor="#FFFFCC" backcolor="#990000"/>
                <othermonthdaystyle forecolor="#CC9966"/>
                </asp:calendar>
            </div>
        </form>
    </body>
</html>

 BODYタグは、onBlurイベント(クライアント側)で起動し、ポップアップをモーダルダイアログボックスにする、簡単なスクリプトを含んでいます。ポップアップは小さくしたいので、またポップアップの唯一の目的はメインページ内のフィールド値を設定することなので、他のウィンドウに切り替える理由はありません。

 今回のソリューションの鍵になるのは、CalendarコントロールのDayRenderイベントにアタッチする次のメソッドです。

/// <summary>
/// Replaces the standard post-back link for each calendar day
/// with the javascript to set the opener window's TextBox text.
/// Eliminates a needless round-trip to the server.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Calendar1_DayRender(object sender, System.Web.UI.WebControls.DayRenderEventArgs e)
{
    // Clear the link from this day
    e.Cell.Controls.Clear();

    // Add the custom link
    System.Web.UI.HtmlControls.HtmlGenericControl Link = 
        new System.Web.UI.HtmlControls.HtmlGenericControl();
    Link.TagName = "a";
    Link.InnerText = e.Day.DayNumberText;
    Link.Attributes.Add("href", String.Format("JavaScript:window.opener.document.{0}.value" +  
        "= \'{1:d}\'; window.close();", Request.QueryString["field"], e.Day.Date));
     
    // By default, this will highlight today's date.
    if(e.Day.IsSelected)
    {
        Link.Attributes.Add("style", this.Calendar1.SelectedDayStyle.ToString());
    }

    // Now add our custom link to the page
    e.Cell.Controls.Add(Link);
}

 このメソッドは、Calendarコントロールが日にちをレンダリングするたびに呼び出されます。このメソッドが最初に行うことは、レンダリングされている日にちに対応するテーブルのセルの内容を消去することです。次に、プログラムによってハイパーリンク(<A>タグ)を作成します。Calendarコントロールが通常行うのと同様に、Calendarからの現在の日にち番号をリンクのInnerTextとして使用します。openerウィンドウ内のTextBoxコントロールの値を設定するために必要なJavaScriptを含む文字列を作成することにより、リンクのHREFプロパティを設定します。それから、DatePickerウィンドウを閉じます。

 JavaScriptにおけるopenerは、window.openコマンドによって現在のウィンドウを開いたウィンドウを参照するためのキーワードです。ASP.NETのTextBoxコントロールは、HTML内では<INPUT>としてレンダリングされます。スクリプトは続いて、変更したい特定のフィールドの値を設定します。今回は、選択された日付を使用(ShortDatePatternで書式化)してフィールドの値を設定するように指定しています。

 Calendar1_DayRenderメソッドでは、次に、今いる日にちが今日の日付を表しているかどうかを調べます。CalendarSelectedDateプロパティを指定していないため、既定で今日の日付が選択されます。ユーザーに基準点を示したいので、リンクにstyle属性を追加し、CalendarSelectedDayStyleを使用しています。

 今いる日にちに対応するテーブルセル内に、置き換えたハイパーリンクを挿入すれば、すべて完了です。

 ただし、まだカスタムメソッドを呼び出すようにCalendarコントロールを配線し直していません。Calendar1_DayRenderCalendarDayRenderイベントにアタッチするには、VisualStudio .NETのデザインモードで「DatePicker.aspx」ファイルを編集する方法と、分離コードファイル内でページのInitializeComponentメソッドを直接修正する方法があります。

 InitializeComponentメソッド内が自動生成されたコードでごちゃごちゃするのを防ぐには、デザインモードでDatePickerページを表示することによって、EventHanderを追加します。Calendarコントロールをクリックし、[Properties]ウィンドウ内のイベントアイコン(稲妻マーク)をクリックします。DayRenderアクションの隣のドロップダウンリストから、Calendar1_DayRenderメソッドを選択できるはずです。

 VS .NETは、ASP.NET CalendarコントロールのDayRenderイベントデリゲートシグニチャにマッチするメソッドの分離コードファイルを検索することで、利用可能な選択肢をドロップダウンリストに表示してくれます(DayRenderEventHandlerデリゲートは、1つのオブジェクトとDayRenderEventArgs型という、2つのパラメータを取ります)。

 カスタムデリゲートを自分でアタッチしたい場合(あるいは単にVS .NETのIDEによって変更された箇所を確認したい場合)は、分離コードファイル内の「Web Form Designer generated code」と書かれた場所でInitializeComponentメソッドを見つけることができます。

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
    this.Calendar1.DayRender += 
        new System.Web.UI.WebControls.DayRenderEventHandler(this.Calendar1_DayRender);
    this.Load += new System.EventHandler(this.Page_Load);
}

 Calendarコントロールの組み込みのEventHandlerでなく、独自のDayRenderEventHandlerを追加していることに注目してください。これは+=構文で示されています。カレンダのHTMLテーブルの作成処理、および月内のすべての日にちの処理はASP.NETにさせましょう。私たちは、各日にちのテーブルセルを独自の内容に置き換えればよいだけです。

テスト

 これで、求めている機能を達成するための作業はすべて終わりました。プロジェクトをビルドし、実行して、結果をテストしましょう。「pick」リンクをクリックすれば、DatePickerポップアップを起動できるはずです。Calendarコントロールでは、標準のカレンダと同様に、(PostBackによって)前月または翌月に移動できます。また、特定の日にちをクリックすると、ほぼ瞬時にサンプルのフォーム内のTextBoxコントロールの値が設定され、ポップアップが閉じます。

起こり得る問題

 動作が正しく行われているか確認しなければならない箇所がいくつかあります。最初に、分離コードファイル内のInitializeComponentメソッドが独自のDayRenderEventHandlerデリゲートにアタッチされているか確認します。VS .NETには、ASPXページにおいてデザインモードとHTMLモードをあまり頻繁に切り替えると、InitializeComponentの内容が消去されてしまうという奇妙な癖があります。

 ページがまだ正常に動作しない場合は、JavaScriptエラーの可能性があります。JavaScriptは大文字と小文字を区別することに注意してください。正しい「FormName.FieldName」をcalendarPicker関数に引き渡しているかどうか確認しましょう。

 疑わしい場合はデバッグをすることです。VS .NETではJavaScriptコードのデバッグも可能です。JavaScriptをデバッグするには、Internet Explorerの[Tools]メニューから[Internet Options]ダイアログボックスを開きます。そして、[Advanced]タブで[Disable script debugging]のチェックを外します。

 スクリプトのデバッグを有効にしたら、プロジェクトをデバッグモードで起動します。[Debug]メニューから[Windows]→[Running Documents]を選択します。

 [Running Documents]ウィンドウ内のDefault.aspxをダブルクリックし、JavaScriptにブレークポイントを設定します。

 ブラウザの呼び出しがブレークポイントに達すると、コントロールからVisual Studioに戻ります。そこでスクリプト内のJavaScriptパラメータの値を調べることができます。この機能は非常に役に立ちます。

さらなる改善の可能性

 PostBackの回数をさらに減らすために、自分で「月入力」コントロールを作成して、ユーザーが何回もクリックすることなく特定の月に1ステップで到達できるようにすることもできるでしょう。この単純なサンプルは、さまざまに拡張および改善することができます。

結論

 .NET以前に覚えたWeb開発について、すべてを忘れる必要はありません。Microsoftは、Webコントロールの機能拡張を簡単にしてくれています。この記事を読んで、ASP.NETが開発者が物事のしくみに好奇心を持ち、変更を加えることを奨励するように作られていることをわかっていただけたら、と思います。



  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • Conrad Jalali(Conrad Jalali)

    ユーザビリティに重点を置く設計会社、Useful Studiosの共同創立者。ここ5年間はActive Server Pages(ASP)に取り組んでおり、C#とSQL Server 2000を使用したASP.NET開発を専門とする。Sarah Lawrence Collegeを卒業し、教養課程の学...

  • japan.internet.com(ジャパンインターネットコム)

    japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.c...

バックナンバー

連載:japan.internet.com翻訳記事

もっと読む

All contents copyright © 2005-2020 Shoeisha Co., Ltd. All rights reserved. ver.1.5