.NET TIPS

[ASP.NET AJAX]CascadingDropDownコントロールで階層式選択ボックスを生成するには?[2.0のみ、C#、VB]

山田 祥寛
2007/04/05

 複数の選択ボックスを配置したページ上で、上位の選択ボックスでの選択値に基づいて下位の選択ボックスに表示するリストの項目を変更したい、という状況はよくある。

 例えば、以下のようなページを想定してみるとよいかもしれない。

左の欄で「翔泳社」を選択
左の欄で「インプレス・ジャパン」を選択
階層式選択ボックスを利用したページの例
上位の選択ボックスで選択された内容に応じて、下位の選択ボックスの内容が動的に切り替わる。

 上位の[出版社]欄を選択すると、それに基づいて、[書籍]欄には選択された出版社から刊行されている書籍情報だけがセットされるというわけだ。このような階層式の選択ボックスを利用することで、選択肢が多い場合にもあらかじめ表示項目を絞り込めるので、エンド・ユーザーにとってはより項目の選択がしやすくなる。

 このような階層式選択ボックスを生成するのがCascadingDropDownコントロールの役割だ。このコントロールはASP.NET AJAX Control Toolkit(以降、「Control Toolkit」)で提供されるコントロールの1つで、ASP.NET標準のサーバ・コントロールであるDropDownListコントロールを拡張し、複数の選択ボックスに階層関係を設定するための機能を提供する。

 それではさっそく、CascadingDropDownListコントロールを利用して、上の画面で見たような階層式選択ボックスをASP.NETページに設置してみることにしよう。なお、本サンプルを動作させるに当たっては、「TIPS:[ASP.NET AJAX]ASP.NET AJAX Control Toolkitを利用するには?」で紹介した手順に従って、Control Toolkitを利用可能な状態にしておく必要がある。

1. 値リスト(選択ボックス)のデータソースを用意する

 まずは、選択ボックス(DropDownListコントロール)に表示すべきデータソースとして、以下のようなPublish/Bookテーブルをデータベース上に作成しておこう。

フィールド名 データ型 概要
publish_id INT 出版社コード(主キー)
name VARCHAR(50) 出版社名
Publishテーブルのフィールド・レイアウト
 
フィールド名 データ型 概要
isbn VARCHAR(30) ISBNコード(主キー)
title VARCHAR(100) 書籍タイトル
publish_id INT 出版社コード
Bookテーブルのフィールド・レイアウト

 Publish/Bookテーブルにはそれぞれ以下のようなデータをあらかじめ入力しておくものとする。

publish_id name
1
翔泳社
2
秀和システム
3
ソシム
4
インプレス・ジャパン
Publishテーブルのデータ(例)
 
isbn title publish_id
4-7980-1270-X Pocket詳解PHP辞典
2
4-7980-1363-3 Pocket詳解ASP.NET辞典
2
4-7981-0959-2 PEAR入門
1
4-7981-1062-0 10日でおぼえるASP.NET2.0入門教室
1
4-7981-1070-1 XMLデータベース入門
1
4-7981-1206-2 サーバサイドAjax入門
1
4-8443-2005-X 改訂新版 基礎PHP
4
4-8443-2061-0 基礎XML
4
4-88337-491-2 書き込み式 SQLのドリル
3
Bookテーブルのデータ(例)

2. 新規のXML Webサービス・クラスを定義する

 CascadingDropDownListコントロールを利用するには、エンド・ユーザーが上位の選択ボックスで選択した値をキーに、下位の選択ボックスの値リストを取得するXML Webサービス・クラス(.asmxファイル)を用意しておく必要がある。

 まずは具体的な.asmxファイルのコードを見てみよう。

<%@ WebService Language="C#" Class="CascadingDropDownList" %>

using AjaxControlToolkit;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Script.Services;

[WebService(Namespace="http://tempuri.org/")]
[WebServiceBinding(ConformsTo=WsiProfiles.BasicProfile1_1)]
[ScriptService()]
public class CascadingDropDownList : System.Web.Services.WebService {
  [WebMethod()]
  public CascadingDropDownNameValue[] GetDropDownData(string knownCategoryValues, string category) {
    List<CascadingDropDownNameValue> result =
      new List<CascadingDropDownNameValue>();
    ConnectionStringSettings setting =
      ConfigurationManager.ConnectionStrings["MyDB"];
    DbProviderFactory factory =
      DbProviderFactories.GetFactory(setting.ProviderName);
    using (DbConnection db = factory.CreateConnection()) {
      db.ConnectionString = setting.ConnectionString;
      DbCommand comm = factory.CreateCommand();
      comm.Connection = db;

      switch (category) {
        // カテゴリ“Publish”の場合、Publishテーブルから
        // 出版社コード昇順ですべての出版社情報を取得
        case "Publish" :
          comm.CommandText = "SELECT publish_id, name FROM Publish ORDER BY publish_id";
          break;

        // カテゴリ“Book”の場合、上位選択ボックス(ddlPublish)で
        // 選択された出版社コードをキーにBookテーブルを検索、
        // 該当の出版社に属する書籍情報のみを取得
        case "Book" :
          comm.CommandText = "SELECT isbn ,title FROM Book WHERE publish_id=@publish_id";
          DbParameter param = factory.CreateParameter();
          param.ParameterName = "@publish_id";
          StringDictionary sd = CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);
          param.Value = sd["Publish"];
          comm.Parameters.Add(param);
          break;
      }
      db.Open();

      // 取得した結果セットから選択ボックスに表示する
      // 値リスト(CascadingDropDownNameValueオブジェクト
      // の配列)を生成
      DbDataReader reader = comm.ExecuteReader();

      while(reader.Read()){
        CascadingDropDownNameValue cv = new CascadingDropDownNameValue(reader[1].ToString(), reader[0].ToString());
        result.Add(cv);
      }
    }
    return result.ToArray();
  }
}
<%@ WebService Language="VB" Class="CascadingDropDownList" %>

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 CascadingDropDownList
  Inherits System.Web.Services.WebService

  <WebMethod()> _
  Public Function GetDropDownData(ByVal knownCategoryValues As String, ByVal category As String) As CascadingDropDownNameValue()
    Dim result As New List(Of CascadingDropDownNameValue)
    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

      Select Case category
        ' カテゴリ"Publish"の場合、Publishテーブルから
        ' 出版社コード昇順ですべての出版社情報を取得
        Case "Publish"
          comm.CommandText = "SELECT publish_id, name FROM Publish ORDER BY publish_id"

        ' カテゴリ"Book"の場合、上位選択ボックス(ddlPublish)で
        ' 選択された出版社コードをキーにBookテーブルを検索、
        ' 該当の出版社に属する書籍情報のみを取得
        Case "Book"
          comm.CommandText = "SELECT isbn ,title FROM Book WHERE publish_id=@publish_id"
          Dim param As DbParameter = factory.CreateParameter()
          param.ParameterName = "@publish_id"
          Dim sd As StringDictionary = CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues)
          param.Value = sd.Item("Publish")
          comm.Parameters.Add(param)
      End Select

      db.Open()

      ' 取得した結果セットから選択ボックスに
      ' 表示する値リスト(CascadingDropDownNameValueオブジェクト
      ' の配列)を生成
      Dim reader As DbDataReader = comm.ExecuteReader()

      Do While reader.Read()
        Dim cv As New CascadingDropDownNameValue(reader(1), reader(0))
        result.Add(cv)
      Loop
    End Using
    Return result
  End Function
End Class
選択ボックスの値リストを取得するためのXML Webサービス・クラス(CascadingDropDownList.asmx)(上:C#、下:VB)

 サンプル・アプリケーションの大きな流れについては、コード内のコメントを参照していただくとして、ここで注目していただきたいのは、以下の3点だ(なお、ここではXML Webサービス・クラスそのものの記法については割愛する。詳細については、「ASP.NETを使用したXML Webサービス」を参照いただきたい)。

(1)XML Webサービス・クラスにはScriptService属性を付与する

 ScriptService属性(System.Web.Script.Services名前空間)は、該当のXML Webサービス・クラスがクライアント側スクリプトから呼び出し可能であるかどうかを表す。

 CascadingDropDownListコントロールは、内部的に自動生成したJavaScript経由でサーバ側との通信を行っているので、通信先となるXML Webサービス・クラスでも必ずScriptService属性を付与する必要がある。

(2)Webサービス・メソッドのパラメータ/戻り値型は固定

 CascadingDropDownListコントロールにおいて、Webサービス・メソッドの名前は自由に決めることができるが(ここではGetDropDownData)、戻り値のデータ型、および、パラメータの名前/データ型は完全に固定であり、アプリケーション開発者が変更することはできない。つまり、いかなる場合にもWebサービス・メソッドは以下のシグニチャに従う必要がある。C#/Visual Basic(VB)いずれを利用しているかにかかわらず、パラメータの名前は大文字/小文字まで厳密に区別されるので、注意してほしい。

[WebMethod()]
public CascadingDropDownNameValue[] <メソッド名>(string knownCategoryValues, string category)
<WebMethod()> _
Public Function <メソッド名>(ByVal knownCategoryValues As String, ByVal category As String) As CascadingDropDownNameValue()
CascadingDropDownListコントロールから呼び出されるWebサービス・メソッドのシグニチャ(上:C#、下:VB)

 このWebサービス・メソッドがパラメータを介して受け取るのは、上位ボックスのカテゴリと選択値のセット(knownCategoryValues)、現在の選択ボックスのカテゴリ(category)だ。

 ここでカテゴリとは、階層式選択ボックスの中で、個々の選択ボックスを識別するための任意の値のこと(カテゴリの設定については後述する)。knownCategoryValuesパラメータには、このカテゴリと値のセットが「カテゴリ;値;……」の形式でセットされている。

 本サンプルでは選択ボックスが2階層しかないが、上位に複数階層の選択ボックスが関連付いている場合には、最上位ボックスから直上のボックスまでのすべてのカテゴリ/値セットが順に格納されることになる。

 knownCategoryValuesパラメータに渡された個々の選択ボックスの値を取得するのは、CascadingDropDownクラス(AjaxControlToolkit名前空間)のParseKnownCategoryValuesStringメソッドの役割だ。このメソッドは、パラメータに渡されたカテゴリ/値のセミコロン区切り文字列を解析し、その内容をカテゴリ/値の連想配列(StringDictionaryオブジェクト)として返す。あとは、カテゴリ名をキーに、以下の要領で対応する選択ボックスの値を取得することができる。

sd["Publish"]           …… C#の場合
sd.Item("Publish")      …… VBの場合
選択ボックスの値の取得方法

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

 先ほどのシグニチャでも示したように、Webサービス・メソッドは選択ボックス(DropDownListコントロール)に引き渡す値リストを、CascadingDropDownNameValueオブジェクトの配列として返す必要がある。CascadingDropDownNameValueクラスのコンストラクタの構文は、以下のとおり。

CascadingDropDownNameValue(表示テキスト, オプションの値)
CascadingDropDownNameValueクラスのコンストラクタの構文

 以上のポイントを押さえていれば、あとはカテゴリごとに処理を分岐しているものの、個々の処理自体はごくシンプルなものであることが見て取れるはずだ。上位の選択ボックス(カテゴリ名は“Publish”とする)を表示する際にはPublishテーブルから全出版社情報を、下位の選択ボックス(カテゴリ名は“Book”)を表示する際には“Publish”選択ボックスでの選択値をキーにBookテーブルから「指定された出版社に属する書籍情報のみ」を、それぞれ取得していることをいま一度確認していただきたい。

3. 新規のWebフォームを作成する

 新規のWebフォーム(CascadingDropDownList.aspx)を作成したら、フォーム・デザイナから以下の画面の要領でサーバ・コントロールを配置する。また、それぞれのコントロールに対しては、表の内容でプロパティ値を設定しておこう。

Webフォーム(CascadingDropDownList.aspx)のレイアウト
コントロール(ID) プロパティ 値
ScriptManager(manager) − −
DropDownList(ddlPublish) − −
DropDownList(ddlBook) − −
CascadingDropDown(ccdPublish) TargetControlID ddlPublish
Category Publish
LoadingText ロード中...
PromptText 出版社を選択してください
ServiceMethod GetDropDownData
ServicePath CascadingDropDownList.asmx
CascadingDropDown(ccdBook) TargetControlID ddlBook
Category Book
LoadingText ロード中...
ParentControlID ddlPublish
PromptText 書籍を選択してください
ServiceMethod GetDropDownData
ServicePath CascadingDropDownList.asmx>

 CascadingDropDownListコントロールのTargetControlIDプロパティは、階層式選択ボックスの機能を付与するDropDownListコントロールを指定するものだ。これによって、DropDownListコントロールddlPublish/ddlBookにCascadingDropDownListコントロールの機能が追加されたことになるわけだ。

 なお、CascadingDropDownListコントロールのそのほかのプロパティを設定する場合、(CascadingDropDownListコントロールではなく)関連付けたDropDownListコントロールのプロパティとして設定する必要がある点に注意してほしい。この場合であれば、それぞれのDropDownListコントロールのプロパティ・ウィンドウに[ccdPublish(CascadingDropDownList)]のような項目が追加されているので、この項目配下から個々の値を設定することができる。

 CascadingDropDownListコントロールで利用可能な主なプロパティは以下のとおり。

プロパティ名 概要
TargetControlID 関連付けるDropDownListコントロールのID値
Category 階層内で選択ボックスを識別するためのカテゴリ値
PromptText 選択ボックスが空の場合に表示するテキスト
LoadingText Webサービス・メソッドからのデータ取得時に表示するテキスト
ServicePath 値リスト取得に使用する.asmxファイルのパス
ServiceMethod 値リスト取得に使用するWebサービス・メソッドの名前
ParentControlID 親となる選択ボックス(DropDownListコントロール)のID値
SelectedValue デフォルトでの選択値
CascadingDropDownListコントロールで利用可能な主なプロパティ

 多くのプロパティが用意されているが、最低限、設定が必要なのは値リストを取得するためのWebサービス・メソッドを特定するServicePath/ServiceMethodプロパティ、個々の選択ボックスを特定するためのCategoryプロパティ、そして、上位の選択ボックスを表すParentControlIDプロパティだ。ParentControlIDプロパティの指定によって、互いに選択ボックスは階層内での親子関係を認識できるというわけだ。

 親を持たない、最上位の選択ボックスに対してはParentControlIDプロパティを指定する必要はない。Categoryプロパティで指定された値は、Webサービス・メソッドを呼び出す際にcategoryパラメータに引き渡される値を表す。

 なお、参考までに、ここまでにVisual Studio 2005で自動生成されたコードを引用しておく。プロパティ・ウィンドウ上ではDropDownListコントロール配下のプロパティとして設定したServicePath/ServiceMethodプロパティなども、コード上はCascadingDropDownListコントロールの属性として記述されていることが確認できるはずだ。

<%--Control Toolkitを使用する場合には先頭にScriptManagerの配置は必須--%>
<asp:ScriptManager ID="manager" runat="server">
  </asp:ScriptManager>
<asp:DropDownList ID="ddlPublish" runat="server">
  </asp:DropDownList>
<asp:DropDownList ID="ddlBook" runat="server">
  </asp:DropDownList>
<%--CascadingDropDownコントロールの諸設定--%>
<ajaxToolkit:CascadingDropDown ID="ccdPublish" runat="server"
  TargetControlID="ddlPublish" Category="Publish"
  LoadingText="ロード中..." PromptText="出版社を選択してください"
  ServiceMethod="GetDropDownData" ServicePath="CascadingDropDownList.asmx">
  </ajaxToolkit:CascadingDropDown>
<ajaxToolkit:CascadingDropDown ID="ccdBook" runat="server"
  TargetControlID="ddlBook" Category="Book" LoadingText="ロード中..."
  ParentControlID="ddlPublish" ServiceMethod="GetDropDownData"
  ServicePath="CascadingDropDownList.asmx" PromptText="書籍を選択してください">
  </ajaxToolkit:CascadingDropDown>
AutoComplete.aspxのソース・コード(抜粋)
一連のレイアウト編集を行った後、Visual Studio 2005によって自動生成されたコードを引用したもの。なお、<%--〜--%>は筆者によるコメント。

 以上を理解したら、さっそく作成したサンプル・プログラムを実行してみよう。冒頭の画面のように、上位の選択ボックス(出版社)を選択すると、選択された値に対応した書籍情報のみが下位の選択ボックスに表示されることが確認できるはずだ。End of Article

利用可能バージョン:.NET Framework 2.0のみ
カテゴリ:Webフォーム 処理対象:ASP.NET AJAX
使用ライブラリ:CascadingDropDownコントロール
使用ライブラリ:ScriptService属性(System.Web.Script.Services名前空間)
関連TIPS:[ASP.NET AJAX]ASP.NET AJAX Control Toolkitを利用するには?

この記事と関連性の高い別の.NET TIPS
[ASP.NET]GridViewコントロールの表示データを特定条件で絞り込むには?
Visual Studioのコード・エディタでボックス選択をするには?
[ASP.NET AJAX]DynamicPopulateコントロールでXML Webサービスを非同期に呼び出すには?
[ASP.NET]GridViewコントロールで選択ボックスを表示するには?
[ASP.NET AJAX]ダイナミック・コンテキスト機能でポップアップ・コントロールの内容を動的に生成するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間