Aaron的测试生活小说

半两五钱,笃志向前
   :: 首页 :: 新随笔 :: 联系 ::  :: 管理

  在上一篇漫话ID(上)——NameID的种种中,我介绍了关于nameid的一些问题,在这片文章中,我们把目光放到ID上来,这篇主要关注自定义控件和repeater等控件中碰到的UniqueIDClientID。同时,在文末还是依照老习惯,给出一个便于大家理解的例子,而不仅仅是纸上谈兵。

  ASP.NET 的服务器端控件中有三种关于 ID 的属性,即 ID, ClientID UniqueIDID 表示控件的服务器端编程的标识符,我们写服务器端的代码,就要用到这个 ID, 通过这个 ID 可以在服务器端对服务器端控件的属性、方法和时间进行编程访问。ClientID 表示由服务器端生成的客户端控件的ID,  经常用于在客户端脚本中访问服务器控件所呈现的 HTML 元素。一般情况下与服务器端的 ID 相同,有时,不能为控件生成唯一的名称,例如,如果 Repeater 空间在它的某个模板中包含一个 Label 控件,则将在客户端生成多个该 Lable HTML 元素, 为防止命名冲突,ASP.NET 为各个服务器控件生成一个唯一的 ClientID ,ClientID 通过将子控件的父控件的 UniqueID 值与控件的 ID 值连接生成,各个部分之间以下划线 _ 连接。UniqueID 用于获取服务器控件的唯一的、以分层方式表示的标识符。当将控件放置到重复控件(RepeaterDataListDataGrid)中时,将可能生成多个服务器端的控件,这就需要区分服务器端的各个控件,以使它们的 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"在客户端脚本中引用控件。有些控件将子控件呈现在页面中。这些控件中包括数据列表控件(如 GridViewDetailsViewFormViewDataList 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;

  还有一个问题我们需要注意:在使用UniqueIDClientID的时候要小心点,千万不要“迫不及待”地使用了这些属性,在运行的过程中可能会碰到一些让人头疼的问题.

例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之类的控件中会碰到UniqueIDClientID的问题之外,我们还可以在UserControl中碰到相同的情况,关于具体的代码实例我会在下一篇文章结束后给出的示例代码中给出,就不在这个页面上继续贴代码了。

Feedback

#1楼  回复 引用 查看   

2009-03-05 08:44 by 老鼠      
我菜,还没看明白

#2楼  回复 引用 查看   

2009-03-05 08:48 by hdl253      
我按照你的代码试了一下,果然有问题,加上这段代码之后
string s = e.Item.FindControl("Button1").ClientID;

生成的客户端代码,两个button的id和name均为button1

这是为什么?

#3楼  回复 引用 查看   

2009-03-05 08:50 by sunlovesea      
谢谢分享..
Asp.net 的生成的HTML代码让我很头疼..

#4楼  回复 引用 查看   

2009-03-05 09:12 by virus      
ID确实值得我们研究

#5楼  回复 引用 查看   

2009-03-05 09:14 by Jeffrey Zhao      
好复杂。
其实我觉得如果自己对比一下,应该很容易发现其中规律的,可惜大部分同学没有去自己归纳一下啊。

#6楼  回复 引用 查看   

2009-03-05 09:34 by virus      
首先要在page指令中添加 EnableEventValidation="false",不验证回发事件,应为我们的button没有处理回发
注销
string s = e.Item.FindControl("Button1").ClientID;
我跟了一下,执行的顺序为
1.DataList1_ItemCreated
2.pageload
3、buttonclick

不注销的话,顺序为
1.DataList1_ItemCreated
2.pageload

就没有执行buttonclick,在itemcreated中多加一句其他的也是这样,或者用其他的代替string s = e.Item.FindControl("Button1").ClientID;,还是这样,看来就是这句起了作用了,是不是这句话劫持了click事件了,我们又没有处理click事件,所以就,这只是个人理解,期待后期研究结果

#7楼  回复 引用 查看   

2009-03-05 10:32 by Nick Wang      
刚开始看的时候,我猜测ClientID对应的是name, 而UniqueID对应的是id,因为从上一篇博文中看到id应该是唯一的,所以感觉应该对应UniqueID。

不知道是我的感觉有问题还是这个名字就很误解人。

另外提个小小的建议,能不能将DataList1, Grid1之类的名字替换成有意义的名字,虽然只是示例代码,可是感觉有意义的名字更容易看懂。

#8楼[楼主]  回复 引用 查看   

2009-03-05 11:49 by Aaron Wu      
@老鼠
我正在学习怎么把逻辑理清楚,希望下一篇文章可以让大家都理解我的意思。

#9楼[楼主]  回复 引用 查看   

2009-03-05 11:51 by Aaron Wu      
@hdl253
我也是调试进去看,没有找到原因。所以我才在文章中专门提到,希望园子里面的高手出来讲讲,等有了回音或者我找到了原因,我一定会告之。(我会尽快解决这个问题,以免让大家等太久)

#10楼[楼主]  回复 引用 查看   

2009-03-05 11:52 by Aaron Wu      
@sunlovesea
我也是头疼,这篇文章中的这个问题不是还没有人解答么……

#11楼[楼主]  回复 引用 查看   

2009-03-05 11:54 by Aaron Wu      
@virus
写的比较乱,但是确实已经改了很久时间了,所以希望可以让大家理解得稍微好点~

#12楼[楼主]  回复 引用 查看   

2009-03-05 11:55 by Aaron Wu      
@Jeffrey Zhao
那老赵可不可以帮忙解释一下文末遗留的那个问题啊~

#13楼[楼主]  回复 引用 查看   

2009-03-05 11:57 by Aaron Wu      
--引用--------------------------------------------------
virus: 首先要在page指令中添加 EnableEventValidation=&quot;false&quot;,不验证回发事件,应为我们的button没有处理回发
注销
string s = e.Item.FindControl(&quot;Button1&quot;).ClientID;
我跟了一下,执行的顺序为
1.DataList1_ItemCreated
2.pageload
3、buttonclick

不注销的话,顺序为
1.DataList1_ItemCreated
2.pageload

就没有执行buttonclick,在itemcreated中多加一句其他的也是这样,或者用其他的代替string s = e.Item.FindControl(&quot;Button1&quot;).ClientID;,还是这样,看来就是这句起了作用了,是不是这句话劫持了click事件了,我们又没有处理click事件,所以就,这只是个人理解,期待后期研究结果
--------------------------------------------------------

这些内容我也调试进去看了,就是“劫持”那一块不明就里,希望今明两天可以解决这个问题,也希望园子中的兄弟姐妹可以提出自己的见解或者直接摆平了它~

#14楼[楼主]  回复 引用 查看   

2009-03-05 11:58 by Aaron Wu      
@Nick Wang
这两个东西都是唯一的,只是中间的分隔符不一样而已

非常谢谢你的建议,在之后的示例代码中我一定会注意这些问题。

#15楼[楼主]  回复 引用 查看   

2009-03-05 12:00 by Aaron Wu      
希望接着看帖的兄弟们可以帮忙解决一下文中遗留的问题,我不甚感激。

#16楼  回复 引用   

2009-03-05 16:27 by YokerWu[未注册用户]
大家去看看“e.Item.FindControl("Button1").ClientID”取值的过程是怎么样的?

#17楼[楼主]  回复 引用 查看   

2009-03-05 16:37 by Aaron Wu      
--引用--------------------------------------------------
YokerWu: 大家去看看“e.Item.FindControl(&quot;Button1&quot;).ClientID”取值的过程是怎么样的?
--------------------------------------------------------
这也是我不懂的地方,包括它的执行顺序以及Page_Load的执行顺序都是我不解的地方。
如果仅仅是获得e.Item.FindControl("Button1"),是不会导致Button1_Click时间被Block掉的。

#18楼  回复 引用 查看   

2009-03-05 19:27 by Gray Zhang      
解答在这里,以供后来的人能看到
http://www.cnblogs.com/GrayZhang/archive/2009/03/05/how-uniqueid-is-generated.html

#19楼[楼主]  回复 引用 查看   

2009-03-05 19:53 by Aaron Wu      
@Gray Zhang
我下午刚刚已经看到了,谢谢你的解释。我直接把链接放到文中吧。

另外我也建议你把你摘要中的内容也放到你的正文中,因为否则关于click事件没有执行的解释大家就看不到了~

#20楼  回复 引用 查看   

2009-03-05 20:00 by Gray Zhang      
@Aaron Wu
行,我去修改一下