さる12月14日,ASP.NET AJAX RC版がリリースされました。これに伴い,本連載最終回でもRC環境で確認しています。RCでの変更点はいくつかありますが,ここでは2点だけ。

 まず一つに,ASP.NET AJAXのアセンブリがMicrosoft.Web.Extensions.dllからSystem.Web.Extensions.dllに変更になりました。これに伴い,名前空間も「Microsoft.Web.*」から「System.Web.*」に変更になっています。本連載のサンプルであれば,第3回で紹介したBookSearch.asmx(リスト1)や第5回で紹介したYahooSearch.asmx(リスト1)を以下のように変更する必要があります。

Imports Microsoft.Web.Script.Services
Imports Microsoft.Web.Script.Serialization
    
Imports System.Web.Script.Services
Imports System.Web.Script.Serialization

 もう一点はアプリケーション構成ファイルの変更です。こちらはβ2からRCにかけて大幅に変更になっていますので,原則は新しいWeb.configで既存のものを差し替えることをお勧めします。もちろん,自前の構成を追加している場合には,適宜,構成を転記するのを忘れないようにしてください。変更の詳細については,ASP.NET AJAXサイト内のページでも紹介されていますので,合わせて参照することをお勧めします。

 さて,それでは前置きはこのくらいにして,本題に行ってみましょう。

 いよいよ本連載も最終回。前回に引き続き,ASP.NET AJAXをベースに構築されたサーバーコントロール集「ASP.NET AJAX Control Toolkit」の紹介です。前回も紹介したように,ASP.NET AJAX Control Toolkitでは多くのサーバーコントロールを提供しています。その内容はごく単純なものから高度にフレームワーク的なコントロールまで様々ですが,限られた誌面の中ですべてを紹介することはできません。そこで今回は,比較的中規模で利用頻度も高いと思われるCascadingDropDownコントロールを利用してみることにしましょう。

 CascadingDropDownコントロールは,その名の通り,階層式(Cascading)のドロップダウン・メニューを生成するためのコントロールです。例えば今回扱うのは,以下のようなサンプルアプリケーションです(図1)。

図1:CascadingDropDownコントロールの実行例

 上位のドロップダウン・メニューで部署名を選択すると,下位のドロップダウン・メニューには選択された部署に対応する課の名前のみを表示するというわけです。特にドロップダウン・メニュー内の項目数が多い場合には,このような機能を設けることで,余計な項目を見せずに済みますので,ユーザーの使いやすさも向上します。

 それではさっそく,具体的な構築の手順を見ていくことにしましょう。

データベース接続の準備を行う

 本サンプルでは,部/課情報をデータベース(SQL Server 2005)上で管理します。サンプル作成に先立って,以下の準備を行う必要があります。

(1)データベースとデータを用意する

 App_Dataフォルダ配下にAjax.mdfを作成したうえで,以下のようなテーブル(表1表2)を作成してください*1

表1:Deptテーブル(部署名テーブル)
フィールド名データ型概要
dept_idVARCHAR(5)部署コード(主キー)
dept_nameVARCHAR(50)部署名

表2:Sectionテーブル(課名テーブル)
フィールド名データ型概要
sec_idVARCHAR(5)課コード(主キー)
sec_nameVARCHAR(50)課名
dept_idVARCHAR(5)部署コード(Deptテーブルに対応)

 また,Dept/Sectionテーブルには,以下のような要領で適当なデータを入力しておくものとします(表3表4)。

表3:Deptテーブルの値(例)
dept_iddept_name
D01総務部
D02営業部
D03生産部

図4:Sectionテーブルの値(例)
sec_idsec_namedept_id
S10 総務第一課D01
S11総務第二D01
S20法人営業課D02
S21個人営業課D02
S22国際営業課D02
S30生産管理課D03
S31生産第一課D03
S32生産第二課D03

(2)アプリケーション構成ファイルにデータベース接続設定を定義する

 アプリケーションからデータベースに接続するための接続文字列を,既存のアプリケーション構成ファイル(Web.config)に追加します。この設定は必ずしも必須ではなく,個別のコード(「.aspx」や「.asmx」ファイル)で直接,データベース接続文字列を指定することも可能です。

 しかし,一般的に接続情報はアプリケーション共通で利用するものです。定義を変更した場合の修正などを考慮すれば,Web.configで一元的に管理することを強くお勧めします。Web.configへの記述は,以下の通りです(リスト1)。

リスト1:Web.configの内容

<?xml version="1.0"?>
<configuration>
  ...中略...
  </configSections>
  <connectionStrings>
    <add name="MyDB" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Ajax.mdf;Integrated Security=True;User Instance=True"
      providerName="System.Data.SqlClient" />
  </connectionStrings>
  <system.web>
  ...後略...

 これでApp_Codeフォルダ配下のAjax.mdf(データベースファイル)を,接続名MyDBでアクセスできるようになりました。

ドロップダウン・メニューの値リストを取得する

 CascadingDropDownListコントロールを利用するには,ユーザーが選択した上位ドロップダウン・メニューの値とカテゴリ値をキーに,下位ドロップダウン・メニューの値リストを取得するための,XML Webサービスメソッドを定義しておく必要があります(リスト2)。

 カテゴリとは,階層式ドロップダウン・メニューに含まれるそれぞれのドロップダウン・メニューを一意に識別するためのキーとなる値です。後述しますが,ここでは上位のドロップダウン・メニューとして"Dept"カテゴリを,下位のドロップダウン・メニューとして"Section"カテゴリが指定されているものとします。

 なお,サービスメソッドの名前は任意に決めることが可能ですが(ここではGetDropDownDataメソッド),引数名であるknownCategoryValues/categoryは変更できないようです。引数名を変更した場合,執筆時点のバージョンでは正しく値が認識されませんので,注意してください。

リスト2:cdd.asmx

<%@ WebService Language="VB" Class="cdd" %>

Imports AjaxControlToolkit
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.Common
Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Web.Script.Services


<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<ScriptService()> _
Public Class cdd
  Inherits System.Web.Services.WebService

  <WebMethod()> _
  Public Function GetDropDownData(ByVal knownCategoryValues As String, _
    ByVal category As String) As List(Of CascadingDropDownNameValue)
    Dim result As New List(Of CascadingDropDownNameValue)
     ' 接続名"MyDB"に対する接続を確立
    Dim setting As ConnectionStringSettings = _
      ConfigurationManager.ConnectionStrings("MyDB")
    Dim factory As DbProviderFactory = _
      DbProviderFactories.GetFactory(setting.ProviderName)
    Using db As DbConnection = factory.CreateConnection()
      db.ConnectionString = setting.ConnectionString
      Dim comm As DbCommand = factory.CreateCommand()
      comm.Connection = db
       ' カテゴリ名(引数category)によって処理を分岐
      Select Case category
         ' "Dept"の場合,Deptテーブルから部署コード,部署名を取得
        Case "Dept"
          comm.CommandText = _
            "SELECT dept_id ,dept_name FROM Dept ORDER BY dept_id"
         ' "Section"の場合,上位ドロップダウン・メニューで選択された部署コード
         ' をキーに,Sectionテーブルから課コード,課名を取得
        Case "Section"
          comm.CommandText = _
            "SELECT sec_id ,sec_name FROM Section WHERE dept_id=@dept_id"
          Dim param As DbParameter = factory.CreateParameter()
          param.ParameterName = "@dept_id"
          Dim sd As StringDictionary = _
            CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues)
          param.Value = sd.Item("Dept")
          comm.Parameters.Add(param)
      End Select  …(1)
      db.Open()
      Dim reader As DbDataReader = comm.ExecuteReader()
       ' 取得した結果セットからCascadingDropDownNameValue配列を生成
      Do While reader.Read()
        Dim cv As New CascadingDropDownNameValue( _
          reader.GetString(1), reader.GetString(0))
        result.Add(cv)
      Loop  …(2)
    End Using
    Return result
  End Function
End Class

 GetDropDownDataメソッドの大まかな流れについては,リスト2内のコメントを参照いただくとして,ここで注目していただきたいのは,リスト内太字の部分です。

(1)カテゴリに応じて処理を分岐する

 メソッド内では引数categoryの値に応じて処理を分岐することで,ドロップダウン・メニューごとに取得する値リストを切り替えることが可能です。後述しますが,取得ロジック自体が全く異なる場合には,メソッド自体を個別のドロップダウン・メニュー単位で分割することも可能です。

 分岐した処理内で,上位ドロップダウン・メニューの選択値を取得するには,CascadingDropDownクラス(AjaxControlToolkit名前空間)のParseKnownCategoryValuesStringメソッドを使用します。ParseKnownCategoryValuesStringメソッドは,引数knownCategoryValuesを経由して渡された値*2を解析し,カテゴリ名と値の連想配列をStringDictionaryオブジェクトとして返します。StringDictionaryオブジェクトの内容は,Itemメソッドによって取得できます。

(2)戻り値はCascadingDropDownNameValueオブジェクトの配列で

 GetDropDownDataメソッドは,下位ドロップダウン・メニューに引き渡す値リストを,CascadingDropDownNameValueオブジェクトの配列(リスト)として返す必要があります。CascadingDropDownNameValueクラスのコンストラクタは,以下の通りです。

New CascadingDropDownNameValue(オプションの表示テキスト, オプション値)

CascadingDropDownコントロールを利用しよう

 値リストを取得するためのサービスクラスが用意できたら,あとはこれを呼び出すための「.aspx」ファイルを作成するだけです。cdd.aspxのフォームレイアウトは図2の通りです(カッコ内はID値)。

図2:cdd.aspxのフォームレイアウト

 また,画面をコードエディタに切り替えたうえで,CascadingDropDownコントロールのプロパティ値を設定しておきましょう(リスト3)。執筆時点のバージョンでは,CascadingDropDownコントロールの一部のプロパティについては,プロパティ・ウィンドウからは設定できませんので,注意してください。

リスト3:cdd.aspx(抜粋)

<cc1:cascadingdropdown id="cdd1" runat="server"
  TargetControlID="ddl_dept" Category="Dept" 
  LoadingText="ロード中..." PromptText="部署名を選択してください"
  ServicePath="cdd.asmx" ServiceMethod="GetDropDownData" />
<cc1:CascadingDropDown id="cdd2" runat="server"
  TargetControlID="ddl_sec" Category="Section"
  LoadingText="ロード中..." PromptText="課名を選択してください"
  ServicePath="cdd.asmx" ServiceMethod="GetDropDownData"
  ParentControlID="ddl_dept" />

 CascadingDropDownコントロールで設定可能な主なプロパティは,表5の通りです。

表5:CascadingDropDownコントロールの主なプロパティ
プロパティ概要
TargetControlID設定対象となるDropDownListコントロール
ParentControlID親となるDropDownListコントロール(最上位のドロップダウン・メニューでは不要)
Category階層内でドロップダウン・メニューを一意に識別するためのカテゴリ名
LoadingText値リスト読み込み時の表示テキスト
PromptTextオプション未選択時に表示するテキスト
ServicePathオプション値を取得するための「.asmx」ファイル
ServiceMethodオプション値を取得するためのメソッド名

 つまり,ここではDropDownListコントロール"ddl_dept"と"ddl_sec"とが親子関係にある階層式ドロップダウン・メニューを定義しているというわけです。本稿ではいずれの値リストを取得するにもcdd.asmxのGetDropDownDataメソッドを使用していますが,ServicePath/ServiceMethodプロパティを指定することで,ドロップダウン・メニュー単位に「.asmx」ファイル,取得メソッドを分けることも可能です。

 また,ここではとりあえずシンプルな例として2階層のドロップダウン・メニューを例としていますが,ParentControlIDプロパティを指定することで,同じ要領で階層を3,4...と増やすこともできます。

 以上を理解したら,cdd.aspxを実行してみましょう。冒頭の図1のように,部署名を選択したタイミングで対応する課名が表示されれば成功です。

☆         ☆         ☆

 以上,全9回を通じて,ASP.NET AJAXプログラミングのごく基本的な事項を学んできました。本連載で扱ったのは,ASP.NET AJAX(あるいはASP.NET AJAX Control Toolkit)で提供されている機能のほんの一部にすぎませんが,ASP.NET AJAXによってAjaxアプリケーションの開発生産性がいかに向上するか,その一端を感じていただけたのではないかと思います。本連載が,ASP.NET AJAXの基本を理解したい皆さんの取っ掛かりとなれば幸いです。