ASP.NET のテンプレート機能では、サーバーサイドコントロールの定義を1つ用意しておくことで生成するデータ件数分コントロールを生成してくれます。繰り返し表示されるテーブルのレコードにユーザーコントロールを埋め込んだりするのにとても便利です。例えばこんな感じで TextBox を Repeater の ItemTemplate に1つ用意しておけば、バインドデータの件数分 TextBox を生成してくれます。
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text="<%# Container.DataItem %>"></asp:TextBox>
</ItemTemplate>
</asp:Repeater>
データをバインドすると、TextBox が繰り返し表示されます。
protected void Page_Load(object sender, EventArgs e)
{
List<string> values = new List<string>();
values.Add("アイ");
values.Add("マイ");
values.Add("ミー");
values.Add("マイン");
this.Repeater1.DataSource = values;
this.Repeater1.DataBind();
}
ただ、テンプレート内に表示されるコントロールのうち、特定のものにアクセスする際に困ることがあります。例えば、特定の TextBox にフォーカスを設定するシナリオを考えてみましょう。単独の TextBox コントロールのようにサーバーサイドで単純に TextBox1.Focus() と指定することができません。そもそもこの例では TextBox1 を元に生成されたコントロールが4つもあります!
利用するテンプレートよってはサーバーサイドで Intellisense が出て、動きそうな雰囲気のものもありますが、テンプレートに配置されているサーバーサイドコントロールはこのように ASPX 設計時の ID 指定では到達することができません。実際に生成された HTML を見るとより分かるのですが、TextBox1 を元に生成された input エレメント達はそれぞれホストコントロール(ここでは Repeater)の ID と テンプレートコントロール(TextBox)の ID を組み合わせた一意の値が割り当てられています。
<input name="Repeater1$ctl00$TextBox1" type="text" value="アイ" id="Repeater1_TextBox1_0" /><br />
<input name="Repeater1$ctl01$TextBox1" type="text" value="マイ" id="Repeater1_TextBox1_1" /><br />
<input name="Repeater1$ctl02$TextBox1" type="text" value="ミー" id="Repeater1_TextBox1_2" /><br />
<input name="Repeater1$ctl03$TextBox1" type="text" value="マイン" id="Repeater1_TextBox1_3" /><br />
直接 TextBox1 を ID 指定して TextBox にアクセスできないのですが、ASP.NET のテンプレートが提供している FindControl メソッドを利用することでこの ID の差異を吸収してテンプレート内のコントロールを設計時の ID で指定して取得することができます。
protected void Button1_Click(object sender, EventArgs e)
{
foreach(RepeaterItem item in this.Repeater1.Items)
{
TextBox tbox = item.FindControl("TextBox1") as TextBox;
if (tbox != null)
{
Debug.WriteLine(item.ItemIndex + 1 + " つ目の値: " + tbox.Text);
if (item.ItemIndex == 1)
{
tbox.Focus();
}
}
}
}