はじめに
ASP.NET 1.xには、リストコントロールとして使えるWebコントロールが4つあります。
- DropDownList
- CheckBoxList
- RadioButtonList
- ListBox
いずれもSystem.Web.UI.WebControls.ListControl
クラスから派生したコントロールで、任意の数のListItem
インスタンスを表示できます。その限りでは、4つとも基本的に同じものと言えますが、各コントロールの本体とその関連ListItem
群のレンダリング方法はそれぞれ異なっています。たとえば、CheckBoxListの場合は本体がHTMLの<table>
としてレンダリングされ、表のセル内にはCheckBox Webコントロールが配置されますが、DropDownListの場合は本体がHTMLの<select>
要素、各ListItem
が<option>
要素としてレンダリングされます。
ただ、どのコントロールにも共通する難点として、「項目属性がレンダリングされない」という事実があります。たとえば、CheckBoxListを表示し、リスト中のいくつかのCheckBoxを特定のCSSクラスで表示したいとしましょう。あるいは、RadioButtonListコントロール中の特定のRadioButtonが選択されたときに、何らかのクライアント側JavaScriptが実行されるようにしたいとします。一般的には、CheckBoxまたはRadioButton WebコントロールのAttributes
コレクションを使って設定できる機能ですが、残念なことに、リストコントロールのレンダリングでは、項目属性がレンダリングされません。
本稿では、まず、これらのリストコントロールがいったいどのようにレンダリングされるのかを理解した上で、リストコントロールのListItem
インスタンスの属性もレンダリングされるようにリストコントロールクラスを拡張する方法を見ていきます。最後にちょっとしたデモがありますから、これも見てください。このデモでは、CheckBoxListの中に[None]というチェックボックスオプションを用意しており、このチェックボックスをオンにすると、クライアント側スクリプトが実行されて、リスト内の他のチェックボックスが自動的にオフになります。この機能の実装方法については、続きをお読みください。
リストコントロールのレンダリング
ASP.NET 1.xでは、RadioButtonListおよびCheckBoxListのレンダリング方法は、ListBoxおよびDropDownListのレンダリング方法とは少し異なります。RadioButtonListとCheckBoxListは、ListItem
のレンダリングにSystem.Web.UI.WebControls.RepeatInfo
クラスを使用します。RepeatInfo
クラスは、HTMLの<table>
内に項目群を垂直方向または水平方向にレンダリングします。ただし、RepeatInfo
クラスは外側のHTML <table>
のレンダリングだけを担当するという点に注意してください。内部要素のレンダリング処理は、外から与えられるオブジェクト(IRepeatInfoUser
インターフェイスを実装しているもの)によって行われます。次の図は、RepeatInfo
クラスによるデータレンダリングの流れを示しています。

ご覧のとおり、RepeatInfo
クラスは、RenderRepeater()
メソッドに渡されるIRepeatInfoUser
実装オブジェクトにレンダリングの多くの部分を代行させます。IRepeatInfoUser
インターフェイスには、HasHeader
、HasFooter
、RepeatedItemCount
などの諸プロパティと、実際に項目のレンダリングを行うRenderItem()
メソッドが定義されています。
CheckBoxListとRadioButtonListは、このIRepeatInfoUser
インターフェイスを自身で実装しています。したがって、WebコントロールがRepeatInfo
クラスのRenderRepeater()
メソッドに引き渡すものは、自身のインスタンスに他なりません。つまり、CheckBoxListとRadioButtonListはどちらもIRepeatInfoUser
インターフェイスの必要なプロパティとメソッドを実装しているということです。CheckBoxListとRadioButtonListは、RenderRepeater()
メソッドを呼び出すときに自身のコピーを引き渡します。そのため、リストコントロールの個々の項目のレンダリングの際には、リストコントロール自身のRenderItem()
メソッドが呼び出され、それによって適切な項目がレンダリングされます。つまり、CheckBoxListにはCheckBoxのインスタンス、RadioButtonListにはRadioButtonのインスタンスが追加されます。
IRepeatInfoUser
インターフェイスを実装しているものとしては、RadioButtonListとCheckBoxListの他にあと1つ、DataListがあります。このコントロールも、内部的にRepeatInfo
クラスを使ってレンダリングを行います。前記2つがCheckBoxまたはRadioButtonをレンダリングするのに対し、DataListはDataListItem
をレンダリングします。また、上の図では、
RepeatInfo
クラスが「適切な<table>セルをレンダリングする」(Render appropriate <table> cell)とあります。「適切な」という言葉を使ったのは、RepeatInfo
クラスを使用するWebコントロールでは、1行当たりの項目数などのフォーマットオプションを、RepeatDirection
、RepeatLayout
、RepeatColumns
といったプロパティで指定できるためです。このプロパティをどう設定するかで、RepeatInfo
クラスが当該項目のために表内に新しい行をレンダリングするかどうかが決まります。 DropDownListとListBoxのレンダリングには、RepeatInfo
クラスが使用されません。それぞれの項目は<option>
要素として、RenderContents()
メソッドの内部で直接レンダリングされます。具体的には、Items
プロパティが列挙され、コレクション中のListItem
ごとに、<option>
タグが適切なテキスト属性・値属性とともにレンダリングされます。DropDownListとListBoxとの違いは、ListBoxではレンダリングされる<select>
タグにmultiple
属性が加えられることです。これは、エンドユーザーがリストから複数の項目を選択できることを意味します。
リストコントロールのListItemには、なぜ属性を適用できないか
リストコントロール中の項目に属性を与えようとして、ひどくがっかりした経験をお持ちの方は多いでしょう。たとえば、「Red」、「Green」、「Blue」の3項目を含むDropDownListを作成したいとします。どうせなら各項目の背景色をそれぞれ赤、緑、青にしたいと思い、次のようなコードを書いてみました。
'Assuming items in DropDownList are: 'Red, Green, and Blue (in that order) DropDownListID.Items(0).Attributes("style") = "background-color:red;" DropDownListID.Items(1).Attributes("style") = "background-color:green;" DropDownListID.Items(2).Attributes("style") = "background-color:blue;"
しかし、このページをブラウザに表示してみると、どのリスト項目の背景も白色になっているはずです。ソースを表示してみると、<option>
要素にstyle
属性が付加されていないことがわかります。宣言構文によって属性を指定した場合も同様で、属性は失われます。何が起こったのでしょう。
残念ながら、リストコントロールでは項目属性をレンダリングできません。これは明らかにバグであり、ニュースグループ上でもさまざまに取り上げられています。リストコントロールのソースコードをReflectorで覗いてみると、「Attributes」と見れば、それをすべてオミットするように作られていることがわかります。この理由の一部は、おそらく、リストコントロールの各インスタンスが、レンダリング前はListItem
クラスで表現されていることにあるのでしょう。Attributes
コレクションはSystem.Web.UI.WebControls.WebControl
クラスで定義されており、自動的にビューステートに保存されます。したがって、その値はポストバックが行われても存続します。一方、ListItem
クラスはWebControl
から派生したクラスではなく、Attributes
プロパティを持っていますが、その値はビューステートに保存されません。