INamingContainer接口

INamingContainer是一个没有任何方法的接口。当用控件实现此接口时,ASP.NET 页框架将在此控件下创建新的命名范围。这样可以保证子控件在控件层次结构树中具有唯一的ID。如果控件是提供数据绑定的复合控件(包含子控件),或者控件是模板化控件,或者控件需要将事件路由到其子控件,则控件必须实现INamingContainer接口。

在开发控件时,如果控件继承了CompositeControl,则不需要再继承INamingContainer接口,因为CompositeControl本身就继承了InamingContainer。

一般一个控件主要使用以下三个属性作为其唯一标志:ID,UniqueID,ClientID。其中ID 表示我们给它命名的ID,而不论在服务端还是客户端程序都不会使用这个ID;UniqueID表示控件的服务端ID,在服务端标志控件的名称;ClientID表示控件的客户端ID,这样在浏览器客户端JavaScript时就可以通过此标记在页面中检索到它。

从使用角度讲,如果继承了此接口,当我们为子控件设定一个ID后,它的UniqueID和ClientID会自动加上父控件的this.UniqueID属性值和分隔符作为前缀;分隔符可以通过this.IdSeparator属性取得。一般来说,在服务端默认使用"$"进行分隔,但是到了客户端会自动将这些"$"转换为下画线"_",即客户端ID和服务端ID名称是一样的,只是分隔符不同。通过反射看.NET控件库中Control类的源代码,看到如下代码:

  1. public virtual string ClientID  
  2. {  
  3.     get  
  4.     {  
  5.         this.EnsureID();  
  6.         string uniqueID = this.UniqueID;  
  7.         if ((uniqueID != null) && (uniqueID.IndexOf
    (this.IdSeparator)
    >= 0))  
  8.         {  
  9.             return uniqueID.Replace(this.IdSeparator, '_');  
  10.         }  
  11.         return uniqueID;  
  12.     }  

从上面可以看到Control类中ClientID属性的默认实现也是把分隔符简单替换成下画线"_",根据需要我们也可以自己定义this.IdSeparator分隔符。

下面通过一个例子来说明当控件继承INamingContainer后控件集中子控件的命名规则。控件源代码如下:

  1. public class INamingContainerControl : WebControl  
  2. {  
  3.     protected override void CreateChildControls()  
  4.     {  
  5.         TextBox textbox = new TextBox();  
  6.         textbox.ID = "btn";  
  7.         this.Controls.Add(textbox);  
  8.  
  9.         Button button = new Button();  
  10.         button.ID = "btnOK";  
  11.         button.Text = "确定";  
  12.         this.Controls.Add(button);  
  13.     }  

本控件主要输出两个控件:一个是Button,另一个是TextBox,注意上面代码还未继承命名空间InamingContainer。在浏览器中运行控件,从生成的HTML源代码可以看到,生成的控件ID就是我们为控件命名的ID。这样如果同时使用了多个控件,则会出现命名冲突问题,为了说明此问题,再在测试页面中增加一个本示例控件,从生成的HTML源代码可以看到,就算我们增加多个控件,它的子控件ID还是与我们在控件内部为text控件命名的ID相同。

在客户端,假如我们通过如下代码检索页面中控件:

  1. var button = document.getElementById('btn'); 

则默认情况下会检索到第一个ID等于"btn"的按钮。控件检索不够精确。在服务端,当需要取客户端用户输入的数据时也不能正确访问到想到取值的控件。在模板控件中,也会遇到此问题。

以上存在的这些问题,就是通过继承INamingContainer接口来解决的。我们要做的就是继承此接口而已,不需要写任何额外的代码。把INamingContainer接口加到控件中:

  1. public class INamingContainerControl : 
    WebControl, INamingContainer  
  2. {  
  3.     …;  

重新编译控件并在浏览器中运行,查看源代码,如下:

控件中子控件的ID都加上了父容器的ID和分隔符作为前缀,由于父容器的ID(即控件的ID)是唯一的,所以也就保证了:即使一个页面中使用了多个控件,生成的所有子控件的ID都是唯一的。
说到这里,INamingContainer的作用就是解决一个页面中使用多个自定义控件的ID命名冲突问题。多个自定义控件也可能不是指多个同一个控件,如果我们开发了多个不同的自定义控件,只要这些控件里面有名字一样的,都会存在命名冲突问题。

原文地址:https://www.cnblogs.com/jsping/p/2688328.html