﻿# 你有用 FindControl 時找不到 Control (控制項) 的經驗嗎

FindControl 是 ASP.NET 工程師十分常用的 Method，但初學者應該常常會遇到使用 FindControl 卻找不到 Control 的狀況！

首先，在 ASP.NET 中有所謂 NamingContainer (命名容器) 的觀念，在使用 Data-bound Control ( 資料控制項 ) 時，會用到 ItemTemplate 之類(ITemplate)的標籤，裡面還會包含許多 Control，這些包含在 Template 裡面的 Control 其實是跟原本頁面(Page)中的控制項是不同階層的！而這些被 ITemplate 包含的 Controls 其 NamingContainer 就是這個 Data-bound Control 的每一個 ItemTemplate！

在這種情況下，若要在 Code behind 中使用 Page.FindControl 想直接找到這些 Data-bound Control 的 Template 中的 Control 的話，就沒辦法直接用 Control ID 找到這個 Control。

例如：

\[code:html\]  
        <asp:repeater ID="Repeater1" runat="server" >  
            <ItemTemplate>  
  
                <asp:TextBox ID="TextBox1" runat="server" Text="Text on row" />  
  
            </ItemTemplate>  
        </asp:repeater>  
\[/code\]

在 Code behind 的 Page\_Load 事件中要尋找 Repeater1 中第一個 TextBox1 時，使用以下的程式碼就會出錯，因為找不到 TextBox1：

\[code:c#\]  
using System;  
using System.Data;  
using System.Configuration;  
using System.Collections;  
using System.Web;  
using System.Web.Security;  
using System.Web.UI;  
using System.Web.UI.WebControls;  
using System.Web.UI.WebControls.WebParts;  
using System.Web.UI.HtmlControls;  
public partial class \_Default : System.Web.UI.Page  
{  
    protected void Page\_Load(object sender, EventArgs e)  
    {  
        System.Collections.ArrayList a = new ArrayList();  
        a.Add("List 1");  
        a.Add("List 2");  
        Repeater1.DataSource = a;  
        Repeater1.DataBind();  
        TextBox txt1 = (TextBox)Page.FindControl("TextBox1");  // txt1 會是 null  
        Response.Write(txt1.Text); // 因為 txt1 是 null 而無法取得 txt1.Text 屬性而發生例外(Exception)  
    }  
}  
\[/code\]

如果要透過 FindControl 找到控制項的話，我是有以下 4 種技巧分享給大家：

**1\. 透過實做 Repeater1 的 ItemDataBound 事件來取得每一個 ItemTemplate 中 TextBox1 的控制項**

> 這是最常見的用法！
> 
> \[code:c#\]  
>     protected void Repeater1\_ItemDataBound(object sender, RepeaterItemEventArgs e)  
>     {  
>         TextBox txt1 = (TextBox)e.Item.FindControl("TextBox1");  
>         Response.Write(txt1.Text);  
>     }  
> \[/code\]

**2\. 透過 Repeater1.Items\[0\].FindControl("TextBox1") 找到第一個 ItemTemplate 中的 TextBox1 控制項**

> 這是取得第一個 ItemTemplate 中的 TextBox1 的常見用法！

**3\. 透過 Page.FindControl("Repeater1$ctl00$TextBox1") 找到第一個 ItemTemplate 中的 TextBox1 控制項**

>  這算是使用 FindControl 的小技巧，因為 ASP.NET 的控制項如果沒有 ID 的話，都是從 ctl00 開始算起的。  
>  因為 ItemTemplate 是 NamingContainer 但卻沒有設定 ID，所以第一筆就是 ctl00 第二筆就是 ctl01 依此類推。

**4\. 透過 FindControl<TextBox>("TextBox1") 找到整個頁面中第一個出現的 TextBox1 控制項（不一定在 Repeater1 裡面）**

> 這是透過一個自訂的泛型遞迴方法(Generic Recursive Method)達成。  
>    
> 要使用這段程式比需將以下的程式碼複製到你的頁面的類別(Code behind)中。  
>    
> \[code:c#\]  
>     public T FindControl<T>(string id) where T : Control  
>     {  
>         return FindControl<T>(Page, id);  
>     }
> 
>     public static T FindControl<T>(Control startingControl, string id) where T : Control  
>     {  
>         // 取得 T 的預設值，通常是 null  
>         T found = default(T);
> 
>         int controlCount = startingControl.Controls.Count;
> 
>         if (controlCount > 0)  
>         {  
>             for (int i = 0; i < controlCount; i++)  
>             {  
>                 Control activeControl = startingControl.Controls\[i\];  
>                 if (activeControl is T)  
>                 {  
>                     found = startingControl.Controls\[i\] as T;  
>                     if (string.Compare(id, found.ID, true) == 0) break;  
>                     else found = null;  
>                 }  
>                 else  
>                 {  
>                     found = FindControl<T>(activeControl, id);  
>                     if (found != null) break;  
>                 }  
>             }  
>         }  
>         return found;  
>     }  
> \[/code\]

參考資料：

-   [Control.NamingContainer 屬性](http://msdn2.microsoft.com/zh-tw/library/system.web.ui.control.namingcontainer\(VS.80\).aspx)
    
-   [Understanding the naming container hierarchy of ASP.NET databound controls](http://aspadvice.com/blogs/joteke/archive/2007/02/25/Understanding-the-naming-container-hierarchy-of-ASP.NET-databound-controls.aspx)
