はじめに
ASP.NETでカスタムの複合コントロールを作る場合、通常はCreateChildControl
で子供のコントロールを自身にぶらさげていきます。
しかし、C#のコードの中にプレゼンテーションに依存する部分を入れてしまうと、見た目を変更したい時に毎回コードの変更が必要になってしまい、保守が困難です。
この問題を解決するために、ASP.NETにはカスタムコントロールのプレゼンテーションを分離する、ITemplateという仕組みがあります。
本稿では、チェックボックスとテキストボックスが連携するカスタムコントロールをITemplate化することで、この仕組みを見ていきます。
対象読者
ASP.NETでカスタムコントロールの作成を十分に理解している方。
必要な環境
Microsoft Visual Web Developer 2005で確認しています。ASP.NET 1.0以降の環境なら、配置の変更だけで対応できるはずです。
変更前のソリューション構成
このように、「App_Code」下にCheckText
というカスタムコントロールがあります。
このコントロールはチェックボックスとテキストボックスを持ち、チェックボックスがチェックされるとテキストボックスのenable/disableがトグルします。
このコードは、以下に示すようにレイアウトに関することがハードコードされてしまっています。
protected override void CreateChildControls() { _box = new CheckBox(); _text = new TextBox(); _box.AutoPostBack = true; _text.Enabled = false; _box.CheckedChanged += new EventHandler(box_CheckedChanged); Controls.Add(_box); Controls.Add(_text); }
これは、カスタムコントロールでなければ、aspx側に次のように記述すべき内容です。
<asp:CheckBox ID="checkBox" runat="server" AutoPostBack="true" /> <asp:TextBox ID="textBox" runat="server" Enabled="false" />
このようになっていれば、チェックボックスをテーブルに入れたり、スタイルを当てたりと、自由に扱うことができますが、現状のつくりではCreateChildControls
を変更して、読みづらいコードを書かなくてはなりません。
ITemplate化に必要な作業(aspx側)
それではITemplate化に必要な作業を見ましょう。
<MU:CheckText runat="server"> <Template> <asp:CheckBox ID="checkBox" runat="server" AutoPostBack="true" /> <asp:TextBox ID="textBox" runat="server" Enabled="false" /> </Template> </MU:CheckText>
CheckText
の中に好きなタグ(この場合は「Template」という名前のタグ)を作り、その内部に通常のaspxのようにタグを記述します。
こうしておくと、ASP.NETは自動生成したITemplateというものをCheckText
コントロールの同名のプロパティ(つまりTemplate)にセットしてくれます。
また、C#側からCheckBox
とTextBox
が取得できるように、IDをふっておきます。
ITemplate化に必要な作業(cs側)
aspx側に合わせてcs側も変更します。
まずはクラス定義にINamingContainer
を追加します。
public class CheckText : WebControl, INamingContainer
次にaspxで決めたタグ名(今回はTemplate
)に対応するITemplate
型のpublic propertyを定義します。
private ITemplate _template; [TemplateContainer(typeof(CheckText))] public ITemplate Template { set { _template = value; } }
TemplateContainer
は自分自身のクラス名を指定します。このセッタがASP.NETから呼ばれます。何をするかは自由ですが、ここでは_template
というローカル変数に代入することにしましょう。
後はCreateChildControls
を次のように書き直します。
protected override void CreateChildControls() { _template.InstantiateIn(this); _box = (CheckBox) FindControl("checkBox"); _text = (TextBox) FindControl("textBox"); _box.CheckedChanged += new EventHandler(box_CheckedChanged); }
このように、_template.InstantiateIn(this)
を呼ぶと、aspx側に記述されたコントロールがthisの下に展開されます。
そこから先は通常のControls.Add()
と同様にFindControl
で取得することができます。
プレゼンテーションを変更してみる
それではせっかくテンプレート化したので、表示を変更してみましょう。
元のページは次のような物です。
これをtable
でレイアウトしましょう。これは、aspxを次のように変更するだけで行うことができます。
<MU:CheckText runat="server"> <Template> <table border="1"> <tr> <td>check!</td> <td> <asp:CheckBox ID="checkBox" runat="server" AutoPostBack="true" /> </td> </tr> <tr> <td colspan="2"> <asp:TextBox ID="textBox" runat="server" Enabled="false" /> </td> </tr> </table> </Template> </MU:CheckText>
変更後は次のように表示されます。プレゼンテーションとロジックが分離されているのが分かると思います。
まとめ
ITemplateを使用することでコントロールのプレゼンテーションを分離することができます。プレゼンテーションをクライアントに任せることにより、通常のコントロールよりも保守性と再利用性を高める事ができます。
後編では、今回解説をせずに導入したTemplateContainer
アトリビュートとINamingContainer
について解説します。TemplateContainer
の理解はカスタムコントロールに限らず、既存のASP.NETコントロールであるRepeater
クラスやDataGrid
クラスを真に理解するのにも欠かせません。ご期待ください。