ASP.NET

ASP.NET FindControl で テンプレート内のコントロールを探し出す

2014年6月25日

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();
                }
            }
        }
    }

-ASP.NET