INamingContainer的影響
撰寫ASP.NET的人都應該使用過FindControl(id)的方法,搜尋中指定父集合中的某個id控件,
通常我們認為,你既然父集合控件具有FindControl()方法,應該能有辦法找出相對應的子控件,
這樣的概念是正確的,但它有一個前提是:父集合控件需要實作INamingContainer介面。
那,什麼是INamingContainer?
「識別在 Page 物件的控制項階層架構內建立新 ID 命名空間的容器控制項。這只是資料標記介面。」
參考MSDN-INamingContainer上的說明是如此,簡單說明,就是讓實作該介面的控件擁有保證子控件
ID命名是唯一的能力,也就是能夠為FindControl()方法做出實際註冊ID的能力。然而,在ASP.NET控
件裡,實作該介面的控件,包括:TemplateControl(Page的父類別)、Repeater、DataGrid、DataList、
CheckBoxList、ChangePassword、LoginView、Menu、SiteMapNodeItem與RadioButtonList。
很可惜的,你沒有看到我們常用的:Panel、PlaceHolder。因為這樣的原因,導致你沒有辦法使用
FindControl()來找到父控件中的子控件。
當然,你也許會有點疑問:當我把Panel加入到Page或Form之後,就可以正確使用FindControl找出控件。
例如:
1: Panel tPanel=new Panel();
2: tPanel.ID="Parent";
3: Page.Controls.Add(tPanel);
4:
5: TextBox tTxtBox=new TextBox();
6: tTxtBox.ID="txtBox1";
7: tTxtBox.Text="Pou";
8: tPanel.Control.Add(tTxtBox);
9:
10: //搜尋txtBox1;
11: if (tPanel.FindControl("txtBox1")!=null)
12: {
13: Reponse.Write("Found");
14: }
15: else
16: {
17: Response.Write("Not Found");
18: }
的確使用上述的程式碼能找到指定的子控件,但如果仔細回去看一下Page的定義,Page實作了TemplateControl,
該TemplateControl實作了INamingContainer,讓加入Page裡的Panel具有了Control所定義的FindControl實作功能,
所以可正確Panel.FindControl("txtBox1")找到所有要子控件。那麼,如果該Panel還沒有加入Page時,你就沒有這
樣的功能了。
這樣要怎麼來解決比較好呢?最簡單的作業,就是建立一個新的類別,並且繼承Panel類別與實作INameContainer
介面,讓該新類別就能直接使用FindControl()方法,來完成搜尋子控件的工作。例如:
1: public class NewPanel: Panel, INamingContainer
2: {
3: public NewPanel():base(){
4: //直接引用Panel本身的建構子。
5: }
6: }
實作該介面的方法,可以簡單解決這樣的問題。但是否存在什麼風險呢?或者有什麼樣的效能影響呢?
這個就有待商確了,因為很難保證為何當初.Netframework裡沒有將Panel實作INamingContainer介面呢?
以上是我自己遇到的問題,剛好看到網路上有人討論把他整理一下。如果有不正確的地方,也請大家告知
我,以為我錯誤的觀念一直錯下去。謝謝。
【補充】
實作INamingContainer介面,究竟對我們的程式有什麼影響呢?我們透過下方的例子來加以說明:
1: public partial class Default2 : System.Web.UI.Page
2: {
3: protected void Page_Load(object sender, EventArgs e)
4: {
5: CreateNewPanel(); //建立NewPanel到Form1
6: CreateOrignPanel(); //建立Panel到Form1
7: }
8:
9: private void CreateNewPanel()
10: {
11: //建立一個NewPanel物件並加入Button物件,命名為Button1
12: NewPanel tPanel = new NewPanel();
13: tPanel.ID = "NewPanel";
14: Button tButton = new Button();
15: tButton.ID = "Button1";
16: tButton.Text = "NewPanel-Button1";
17: tPanel.Controls.Add(tButton);
18: form1.Controls.Add(tPanel);
19: }
20:
21: private void CreateOrignPanel()
22: {
23: //建立一個Panel物件並加入Button物件,命名為Button2
24: Panel tPanel = new Panel();
25: tPanel.ID = "OrignPanel";
26: Button tButton = new Button();
27: tButton.ID = "Button2";
28: tButton.Text = "OrignPanel-Button2";
29: tPanel.Controls.Add(tButton);
30: form1.Controls.Add(tPanel);
31: }
32: }
來看一下它實作建立的HTML Source:
你將會發現,原生的Panel因為沒有實作INamingContainer介面,因此不會特別為本身的子控件進行命名
唯一性做處理,但是:NewPanel卻為原本的Button1改了名稱,變成了NewPanel_Button1,做了命名唯
一性的調整,給了實體的名稱。然而,這樣的修改方法,影響的不是我們所設定的ID,而是真正ASP.NET
所認定的控件(或稱控制項)ID:ClientID。如下圖所示:
‧NewPanel程式區段
‧Panel程式區段
我想這樣會幫助大家比較了解實作INamingContainer對於Panel控件與子控件之間的關聯性與影響。
References:
ASP.Net PlaceHolder、Panel等控件未实现INamingContainer,导致