![]() ![]() ![]() ![]() ListItemの属性をレンダリングできるWebカスタムコントロールの作成この記事のURLhttp://japan.internet.com/developer/20060207/25.html
著者:Scott Mitchell
海外internet.com発の記事
はじめにASP.NET 1.xには、リストコントロールとして使えるWebコントロールが4つあります。
いずれも ただ、どのコントロールにも共通する難点として、「項目属性がレンダリングされない」という事実があります。たとえば、CheckBoxListを表示し、リスト中のいくつかのCheckBoxを特定のCSSクラスで表示したいとしましょう。あるいは、RadioButtonListコントロール中の特定のRadioButtonが選択されたときに、何らかのクライアント側JavaScriptが実行されるようにしたいとします。一般的には、CheckBoxまたはRadioButton Webコントロールの 本稿では、まず、これらのリストコントロールがいったいどのようにレンダリングされるのかを理解した上で、リストコントロールの リストコントロールのレンダリング ASP.NET 1.xでは、RadioButtonListおよびCheckBoxListのレンダリング方法は、ListBoxおよびDropDownListのレンダリング方法とは少し異なります。RadioButtonListとCheckBoxListは、 ![]() ご覧のとおり、 CheckBoxListとRadioButtonListは、この DataListもお忘れなく
ASP.NETに用意されている組み込みWebコントロールのうち、
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のレンダリングには、 リストコントロールの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;" しかし、このページをブラウザに表示してみると、どのリスト項目の背景も白色になっているはずです。ソースを表示してみると、 残念ながら、リストコントロールでは項目属性をレンダリングできません。これは明らかにバグであり、ニュースグループ上でもさまざまに取り上げられています。リストコントロールのソースコードをReflectorで覗いてみると、「Attributes」と見れば、それをすべてオミットするように作られていることがわかります。この理由の一部は、おそらく、リストコントロールの各インスタンスが、レンダリング前は 項目属性もレンダリングできるようにリストコントロールを拡張する ここには、実は2つのバグがあります。1つは、リストコントロールが項目属性をレンダリングしないこと、もう1つは、 以降で見ていく解決方法は、リストコントロールクラスを拡張するという方法です。つまり、項目のレンダリングコードを書き換えて、属性のレンダリングを行わせるようにします(Victorがニュースグループ投稿で採用しているアプローチもこれです)。他に、Microsoftサポート技術情報の記事Q309338では、リスト上の項目ごとに属性を設定する必要があるときはリストコントロールなど使わず、HTMLコントロールを使用するという方法を提案しています。 今回ご紹介する私のアプローチは、かなり無骨で力技に近いものです。実際の CheckBoxListの ここにコード全体を掲載することは控えます。全体を見たい方は、本稿のサンプルコードをダウンロードしてください。肝心なのは public void RenderItem(System.Web.UI.WebControls.ListItemType itemType, int repeatIndex, RepeatInfo repeatInfo, HtmlTextWriter writer) { controlToRepeat.ID = repeatIndex.ToString( NumberFormatInfo.InvariantInfo); controlToRepeat.Text = this.Items[repeatIndex].Text; controlToRepeat.TextAlign = this.TextAlign; controlToRepeat.Checked = this.Items[repeatIndex].Selected; controlToRepeat.Enabled = this.Enabled; controlToRepeat.Attributes.Clear(); foreach (string key in Items[repeatIndex].Attributes.Keys) controlToRepeat.Attributes.Add( key, Items[repeatIndex].Attributes[key]); controlToRepeat.RenderControl(writer); } ご覧のとおり、私が追加したコードでは ListItemレベル属性の使用例 妻はオンラインでいろいろと調べ、これが既知のバグであることを知ると、私のところへやってきて、「4Guysの記事用にちょうどいいネタがあるわよ」と言ったのでした。この拡張されたCheckBoxListコントロールを使用すれば、CheckBoxList中の項目に属性を追加できます。ここで、いま述べたクライアント側動作がどのようなコードで実現されるかを、サンプルとして紹介しておきます。 HTML部分
<%@ Register TagPrefix="cc1" Namespace="skmExtendedControls" Assembly="skmExtendedControls" %> <form runat="server"> Select the Peripherals You’d Like to Include in Your Purchase:<br /> <cc1:skmCheckBoxList id="addOns" runat="server" /> </form> ソースコード部分
Private Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) _ Handles MyBase.Load If Not Page.IsPostBack Then ’Populate CheckBoxList from database ’Also be sure to add on "None" option End If ’Add none client-side script for CheckBoxList AddNoneScriptToCheckBoxList(addOns, 0) End Sub Private Sub AddNoneScriptToCheckBoxList(ByVal cbl As CheckBoxList, _ ByVal noneIndex As Integer) ’Now, Add client-side actions Dim i As Integer For i = 0 To cbl.Items.Count - 1 If i = noneIndex Then cbl.Items(i).Attributes("onclick") = _ String.Format("skm_Uncheck(’{0}’, {1}, {2}, true);", _ cbl.ClientID, noneIndex, cbl.Items.Count) Else cbl.Items(i).Attributes("onclick") = _ String.Format("skm_Uncheck(’{0}’, {1}, {2}, false);", _ cbl.ClientID, noneIndex, cbl.Items.Count) End If Next ’Finally, add the skm_Uncheck client-side function Const SKM_UNCHECK_KEY As String = "skm_Uncheck" If Not Page.IsClientScriptBlockRegistered(SKM_UNCHECK_KEY) Then Page.RegisterClientScriptBlock(SKM_UNCHECK_KEY, _ "<script language=""JavaScript"">" & vbCrLf & _ "function skm_Uncheck(cbID, offset, total, uncheckAllButThisOne) {" & _ _vbCrLf & _ " if (uncheckAllButThisOne)" & vbCrLf & _ " for (var i = 0; i < total; i++) { " & vbCrLf & _ " var cb = document.getElementById(cbID + ’_’ + i);" & vbCrLf & _ " if (cb && offset != i) cb.checked = false;" & vbCrLf & _ " }" & vbCrLf & _ " else {" & vbCrLf & _ " var cb = document.getElementById(cbID + ’_’ + offset);" & _ vbCrLf & " if (cb) cb.checked = false;" & vbCrLf & _ " }" & vbCrLf & _ "} </script>") End If End Sub 初めてのページ訪問では、 次に、 必要なクライアント側スクリプトをCheckBoxListの各項目に追加する作業は、 終わりにASP.NET 1.xのリストコントロールが項目属性をレンダリングしないのは、ASP.NETの組み込みコントロールに内在する問題であり、バグです。リストコントロールの項目属性をいじりたければ、HTMLコントロールを使うのが最も簡単な迂回策ですが、本稿で見たとおり、独自のWebコントロールクラスを作成して、属性のレンダリングを行うこともできます。 本稿では、項目属性もレンダリングされるようにCheckBoxList Webコントロールを拡張する方法を見てきました。これで一応の問題は解決されるものの、まだ、項目属性がビューステートに保存されないという不便さが残ります。たぶん、将来の記事ではこの問題に再度触れることになるでしょう。ビューステート問題は不便ではありますが、項目の この拡張版CheckBoxListコントロールを使って、CheckBoxList中の各チェックボックスでクライアント側イベントを利用するデモを作成してみました。CheckBoxList中の[None]チェックボックスをクリックしてオンにすると、自動的に他のすべてのCheckBoxListチェックボックスがクリアされるようになっています。ライブデモをぜひご覧ください。 それでは、ハッピープログラミング! 著者紹介Scott Mitchell(Scott Mitchell)
|