INamingContainer的影響

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:

INamingContainerResult

你將會發現,原生的Panel因為沒有實作INamingContainer介面,因此不會特別為本身的子控件進行命名

唯一性做處理,但是:NewPanel卻為原本的Button1改了名稱,變成了NewPanel_Button1,做了命名唯

一性的調整,給了實體的名稱。然而,這樣的修改方法,影響的不是我們所設定的ID,而是真正ASP.NET

所認定的控件(或稱控制項)ID:ClientID。如下圖所示:

‧NewPanel程式區段

001

‧Panel程式區段

000

我想這樣會幫助大家比較了解實作INamingContainer對於Panel控件與子控件之間的關聯性與影響。

 

References:

ASP.Net PlaceHolder、Panel等控件未实现INamingContainer,导致

INamingContainer介面