在上一篇漫话ID(上)——Name和ID的种种中,我介绍了关于name和id的一些问题,在这片文章中,我们把目光放到ID上来,这篇主要关注自定义控件和repeater等控件中碰到的UniqueID和ClientID。同时,在文末还是依照老习惯,给出一个便于大家理解的例子,而不仅仅是纸上谈兵。
在ASP.NET 的服务器端控件中有三种关于 ID 的属性,即 ID, ClientID 和 UniqueID。ID 表示控件的服务器端编程的标识符,我们写服务器端的代码,就要用到这个 ID, 通过这个 ID 可以在服务器端对服务器端控件的属性、方法和时间进行编程访问。ClientID 表示由服务器端生成的客户端控件的ID, 经常用于在客户端脚本中访问服务器控件所呈现的 HTML 元素。一般情况下与服务器端的 ID 相同,有时,不能为控件生成唯一的名称,例如,如果 Repeater 空间在它的某个模板中包含一个 Label 控件,则将在客户端生成多个该 Lable 的 HTML 元素, 为防止命名冲突,ASP.NET 为各个服务器控件生成一个唯一的 ClientID ,ClientID 通过将子控件的父控件的 UniqueID 值与控件的 ID 值连接生成,各个部分之间以下划线 _ 连接。UniqueID 用于获取服务器控件的唯一的、以分层方式表示的标识符。当将控件放置到重复控件(Repeater、DataList和DataGrid)中时,将可能生成多个服务器端的控件,这就需要区分服务器端的各个控件,以使它们的 ID 属性不冲突。UniqueID 通过将子控件的父控件的 UniqueID 值与控件的 ID 值连接生成,各个部分之间以 IdSeparator 属性指定的字符连接。默认情况下, IdSeparator 属性为冒号字符 (:)。
例如,创建以下 ASP.NET 服务器控件:
<asp:textbox id="TextBox1" runat="server" text="Sample Text" />
ClientID 属性被设置为 TextBox1,在基于 HTML 的浏览器中,其结果元素与以下所示类似:
<input name="TextBox1" type="text" value="Sample Text" id="Text1" />
可以使用这些属性在客户端脚本中引用服务器控件。通常,必须在客户端脚本中用完全限定引用来引用控件;如果控件是页面中 form 元素的子控件,则一般使用document.forms[0].TextBox1.value = "New value"在客户端脚本中引用控件。有些控件将子控件呈现在页面中。这些控件中包括数据列表控件(如 GridView、DetailsView、FormView、DataList 和 Repeater 控件)、用户控件和 Web 部件控件。
例1:
在页面中加入一个DataGrid控件,然后在该控件中加入Button子控件。
前台代码:
<asp:DataGrid ID="DataGrid1" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:TemplateColumn>
<ItemTemplate>
<asp:Button ID="Button1" runat="server" Text="You Can Click Me~"/>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
后台代码:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ArrayList list = new ArrayList();
list.Add(1);
list.Add(2);
DataGrid1.DataSource = list;
DataGrid1.DataBind();
}
}
实际运行结果(html代码):
<table cellspacing="0" rules="all" border="1" id="Table1" style="border-collapse: collapse;">
<tr>
<td>
<input type="submit" name="DataGrid1$ctl02$Button1" value="You Can Click Me~" id="DataGrid1_ctl02_Button1" />
</td>
</tr>
<tr>
<td>
<input type="submit" name="DataGrid1$ctl03$Button1" value="You Can Click Me~" id="DataGrid1_ctl03_Button1" />
</td>
</tr>
</table>
可以看到,在这些情况下,子控件可能不具有唯一的 ID,这可能是因为子控件是在某个模板中定义的,该模板会为每个数据行(数据列表控件)生成新的控件实例,也可能是因为可以从外部源(用户控件和 Web 部件控件)将父控件添加到页面中对于每个子控件:控件的 UniqueID 被呈现为 name。控件的 ClientID 被呈现为 id 属性。 ClientID 和 UniqueID 属性都基于原始的 ID 属性,并用足够的信息进行了修改,以保证页面中结果的唯一性。ClientID的值可在客户端脚本中引用。如果在浏览器中显示一个具有命名容器的页面,则可以查看该页面的源文件,从中找到唯一的 ID,这些 ID 作为命名容器子控件的 name 属性和 id 属性生成。但是,建议不要依赖于直接引用在浏览器中看到的 ID。因为用于生成子控件唯一ID 的公式可能会发生变化,应当获取子控件的 ClientID 属性值,并用该值来引用该子控件。例如,您可能会在页面中动态创建客户端脚本。如果客户端脚本引用一个子控件,则应获取该子控件的 ClientID 属性,并将其嵌入到动态脚本中。
例2:
假设有子控件:
<asp:TextBox ID = "textboxInfo" runat ="server" Text = "Test Text box" name="haha"></asp:TextBox>
那么我们可以在脚本中这样写
var tempt = '<%=textboxInfo.ClientID%>';
var controlname = document.getElementById(tempt).name;
var controlid = document.getElementById(tempt).id;
还有一个问题我们需要注意:在使用UniqueID和ClientID的时候要小心点,千万不要“迫不及待”地使用了这些属性,在运行的过程中可能会碰到一些让人头疼的问题.
例3:
我们试着给例1增加一点点功能:
我们修改代码:
<asp:DataGrid ID="DataGrid1" runat="server" AutoGenerateColumns="false">
改为:
<asp:DataGrid ID="DataGrid1" runat="server" AutoGenerateColumns="false" OnItemCreated="DataGrid1_ItemCreated" >
同时更改button相关代码
<asp:Button ID="Button1" runat="server" Text="You Can Click Me~"/>
改为:
<asp:Button ID="Button1" runat="server" Text="You Can Click Me~" OnClick="Button1_Click" />
同时在后台添加代码:
protected void DataGrid1_ItemCreated(object sender, DataGridItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
string s = e.Item.FindControl("Button1").ClientID;
}
}
protected void Button1_Click(object sender, EventArgs e)
{
Response.Write("Button1_Click");
}
接着运行一下代码,看看会出现什么情况?你会发现Button1_Click方法没有被执行。如果我们注释掉这一行:
string s = e.Item.FindControl("Button1").ClientID;
一切又恢复正常了,页面上显示了“Button1_Click”文本。 至于原因参见http://www.cnblogs.com/GrayZhang/archive/2009/03/05/how-uniqueid-is-generated.html,这篇文章中有详细的解释。(感谢园友Gray Zhang的帮助~)
上面提到,除了repeater之类的控件中会碰到UniqueID和ClientID的问题之外,我们还可以在UserControl中碰到相同的情况,关于具体的代码实例我会在下一篇文章结束后给出的示例代码中给出,就不在这个页面上继续贴代码了。