時光流逝,咱們心愛的ASP.NET也步入了4.0的時代,微軟在ASP.NET 4.0中對不少特性作了修改。好比我將要討論的控件ID機制就是其中之一。javascript
在ASP.NET 4.0以前咱們老是要爲控件的ClientID頭疼,好比明明一個叫lblName的Label放在一個叫作grd的GridView裏面後,在頁面上改Label的ID就變成了諸如grd_clt02_lblName的一長串字符串,若是咱們在前臺想在使用JS的時候找到該Label,咱們不得不用到C#腳原本得到該Label在前臺的確切ID,諸如:html
<
script
type
="text/javascript"
>
var
lblName
=
document.getElementById(
"
<%=lblName.ClientID %>
"
);
</
script
>
在ASP.NET 4.0中的每一個控件上都多了一個叫作ClientIDMode的屬性,這就是解決上面獲取控件ID難的解決方案。這個屬性有四個可選值,根據所選值的不一樣它能夠控制頁面上生成控件的ID格式。java
下面就讓咱們來了解下ClientIDMode屬性的四個值:ide
1,AutoID:post
當控件的ClientIDMode選中爲AutoID時,該控件的ClientID 值是經過串聯每一個祖先容器控件(諸如GridView、ListView、LoginView等就是容器性控件)的ID和父容器控件的ID和其自己的ID 值生成的,固然若是該控件沒有在任何容器控件中其ClientID 值就是其自己的ID值,不會作任何更改。 另外若是該控件所在的父容器控件或祖先容器控件有些是顯示多個數據行的容器控件(例如GridView、ListView就是顯示多數據行的容器控件),那麼還將在這些容器控件的ID值的後面會插入一個遞增的行號格式。 各部分之間如下劃線字符 (_) 分隔。 可見在 ASP.NET 4 以前的版本中使用的就是AutoID方案來生成控件的ClientID 值。url
好比下面這個GridView裏面就有一個名叫Label1的ID,咱們將Label1的ClientIDMode設置爲了AutoID:spa
<
asp:GridView
ID
="grd_Account"
runat
="server"
AllowPaging
="True"
AutoGenerateColumns
="False"
DataKeyNames
="Account Number"
DataSourceID
="sds_account"
Height
="63px"
Width
="676px"
PageSize
="5"
ClientIDMode
="AutoID"
>
<
Columns
>
<
asp:TemplateField
HeaderText
="Account Number"
SortExpression
="Account Number"
>
<
ItemTemplate
>
<
asp:Label
ID
="Label1"
runat
="server"
Text
='<%#
Bind("[Account Number]") %
>
' ClientIDMode="AutoID">
</
asp:Label
>
</
ItemTemplate
>
</
asp:TemplateField
>
</
Columns
>
</
asp:GridView
>
該GridView生成的客戶端HTML代碼就是:code
<
table
cellspacing
="0"
rules
="all"
border
="1"
id
="grd_Account"
style
="height:63px;width:676px;border-collapse:collapse;"
>
<
tr
>
<
th
scope
="col"
>
Account Number
</
th
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_ctl02_Label1"
>
1060
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_ctl03_Label1"
>
1200
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_ctl04_Label1"
>
1510
</
span
>
</
td
>
</
tr
>
</
table
>
能夠看到GirdView裏面的Label造成了諸如grd_Account_ctl02_Label1格式的ClientID,而這正是:父容器ID(grd_Account)+"_"+行號格式(ctl02)+"_"+控件自身ID(ClientID)這種格式生成的。server
2,Static:htm
當控件的ClientIDMode選中爲Static時,該控件的ClientID 值就是其自己設置的 ID 屬性值,其ClientID值不會受到父容器控件的影響。
好比咱們把上面的代碼稍做修改,將Label1的ClientIDMode屬性改成Static:
<
asp:GridView
ID
="grd_Account"
runat
="server"
AllowPaging
="True"
AutoGenerateColumns
="False"
DataKeyNames
="Account Number"
DataSourceID
="sds_account"
Height
="63px"
Width
="676px"
PageSize
="5"
>
<
Columns
>
<
asp:TemplateField
HeaderText
="Account Number"
SortExpression
="Account Number"
>
<
ItemTemplate
>
<
asp:Label
ID
="Label1"
runat
="server"
Text
='<%#
Bind("[Account Number]") %
>
' ClientIDMode="Static">
</
asp:Label
>
</
ItemTemplate
>
</
asp:TemplateField
>
</
Columns
>
</
asp:GridView
>
運行後查看獲得的HTML代碼:
<
table
cellspacing
="0"
rules
="all"
border
="1"
id
="grd_Account"
style
="height:63px;width:676px;border-collapse:collapse;"
>
<
tr
>
<
th
scope
="col"
>
Account Number
</
th
>
</
tr
><
tr
>
<
td
>
<
span
id
="Label1"
>
1060
</
span
>
</
td
>
</
tr
><
tr
>
<
td
>
<
span
id
="Label1"
>
1200
</
span
>
</
td
>
</
tr
><
tr
>
<
td
>
<
span
id
="Label1"
>
1510
</
span
>
</
td
>
</
tr
>
</
table
>
看到了嗎,GridView裏每行的Label1的ClientID都以自身ID的值出現了,不會受到父級容器控件的ID影響,這樣在前臺使用JS時咱們就能通控件自己的ID值找到咱們想要的控件了。
此外使用Static後勢必頁面中會出現不少同名的控件ID,只要這些同名ID的控件處於頁面的不一樣層次(好比某一容器控件的內部和外部就是不一樣層次)上那麼就不會出現問題,可是若是頁面同一層次上有多個同ID的控件,那麼頁面就會報錯。
3,Inherit:
這個屬性其實沒什麼好說的,若是控件的ClientIDMode選中爲Inherit,那麼表示該控件的ClientIDMode會使用父級容器控件的ClientIDMode值,若是父級容器控件的ClientIDMode也爲Inherit,那麼會使用更上層容器控件的ClientIDMode值,直到回溯到頁面的ClientIDMode值爲止,頁面的ClientIDMode值默認爲Predictable ,你能夠在頁面上的<%@ Page%>指令中對該值作更改。此外Inherit也是ASP.NET 4.0中全部控件的ClientIDMode屬性的默認值。
4,Predictable:
首先我先說明下之因此最後寫Predictable,是由於我發現控件的ClientIDMode爲Predictable時生成ClientID的機制會很是複雜,要分好幾個部分分別進行討論,其中還有特殊狀況,因此我在這裏只能說盡可能將我發現的Predictable生成ClientID的機制闡述清楚。
當控件的ClientIDMode選中爲Predictable時,該控件的ClientID 值是經過串聯父容器控件(諸如GridView、ListView、LoginView等就是容器性控件)的 ClientID 值生成的。另外若是該控件是在顯示多個數據行的父容器控件或祖先容器控件中(例如GridView、ListView就是顯示多數據行的容器控件),則還會在該控件ClientID 值的末尾添加 ClientIDRowSuffix 屬性中指定的數據字段的值。 對於 GridView 控件,ClientIDRowSuffix 屬性能夠指定多個數據字段。 若是 ClientIDRowSuffix 屬性爲空白,則在末尾添加遞增的行號,而非數據字段值。 各部分之間如下劃線字符 (_) 分隔。
以上是MSDN的說法,可是通過試驗,我發現Predictable的特性更應該是用這麼個式子來表達:
Inherit[+"_"+ClientIDRowSuffix]
意思就是說,若是一個控件的ClientIDMode選中爲Predictable,那麼在ASP.NET生成該控件的ClientID時首先會去看該控件所屬的父容器控件的ClientIDMode是什麼值,而後先用該控件父容器控件的ClientIDMode規則生成該控件自己的ClientID,最後若是該控件所屬的父容器控件或祖先容器控件是顯示多個數據行的容器控件,還會根據父容器控件或祖先容器控件的ClientIDRowSuffix屬性的值在該控件已生成的ClientID後面加上一個後綴字符串。
下面將幾種狀況逐一列出來單獨解釋:
<1>若是父容器控件的ClientIDMode值爲AutoID
- 若是父容器控件或祖先容器控件爲顯示多個數據行的容器控件,那麼該控件的ClientID格式爲:[父/祖先容器控件的ID+"_"+[行號格式+"_"]]+該控件自身ID+"_"+[ClientIDRowSuffix],其中ClientIDRowSuffix部分是什麼後面會單獨說明,其中:[父/祖先容器控件的ID+"_"+[行號格式+"_"]],就是該控件自身ClientIDMode值繼承父容器控件ClientIDMode值AutoID生成的ClientID結果,其中的[行號格式+"_"]部分是否存在依賴於[父/祖先容器控件]部分是不是顯示多個數據行的容器控件(這裏不明白請看前面的AutoID部分)。
- 若是父容器控件或祖先容器控件都不是顯示多個數據行的容器控件,那麼該控件的ClientID格式爲:[父/祖先容器控件的ID+"_"]+該控件自身ID,可見這個格式就是該控件自身ClientIDMode值繼承父容器控件ClientIDMode值AutoID生成的ClientID結果(這裏不明白請看前面的AutoID部分)。
下面我就舉一個父容器控件是多數據行容器控件且其ClientIDMode爲AutoID的例子,將上面的代碼再作更改,將Label1的ClientIDMode屬性值改成Predictable,而且設置其父容器控件grd_Account的ClientIDMode爲AutoID:
<
asp:GridView
ID
="grd_Account"
runat
="server"
AllowPaging
="True"
AutoGenerateColumns
="False"
DataKeyNames
="Account Number"
DataSourceID
="sds_account"
Height
="63px"
Width
="676px"
PageSize
="5"
ClientIDMode
="AutoID"
>
<
Columns
>
<
asp:TemplateField
HeaderText
="Account Number"
SortExpression
="Account Number"
>
<
ItemTemplate
>
<
asp:Label
ID
="Label1"
runat
="server"
Text
="Logged"
ClientIDMode
="Predictable"
></
asp:Label
>
</
ItemTemplate
>
</
asp:TemplateField
>
</
Columns
>
</
asp:GridView
>
其生成的HTML代碼爲:
<
table
cellspacing
="0"
rules
="all"
border
="1"
id
="grd_Account"
style
="height:63px;width:676px;border-collapse:collapse;"
>
<
tr
>
<
th
scope
="col"
>
Account Number
</
th
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_ctl02_Label1_0"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_ctl03_Label1_1"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_ctl04_Label1_2"
>
Logged
</
span
>
</
td
>
</
tr
>
</
table
>
能夠看到生成的Label的控件的ID諸如:grd_Account_ctl02_Label1_0正是上面所述格式:父容器控件的ID(grd_Account)+"_"+行號格式(ctl02)+"_"+該控件自身ID(Label1)+"_"+[ClientIDRowSuffix](0)。
<2>若是父容器控件的ClientIDMode值爲Static
- 若是父容器控件或祖先容器控件爲顯示多個數據行的容器控件,那麼該控件的ClientID格式爲:該控件自身ID+"_"+[ClientIDRowSuffix],其中ClientIDRowSuffix部分是什麼後面會單獨說明,其中:該控件自身ID,就是該控件自身ClientIDMode值繼承父容器控件ClientIDMode值Static生成的ClientID結果(這裏不明白請看前面的Static部分)。
- 若是父容器控件或祖先容器控件都不是顯示多個數據行的容器控件,那麼該控件的ClientID格式爲:該控件自身ID,可見這個格式就是該控件自身ClientIDMode值繼承父容器控件ClientIDMode值Static生成的ClientID結果(這裏不明白請看前面的Static部分)。
下面我就舉一個父容器控件是多數據行容器控件且其ClientIDMode爲Static的例子,將上面的代碼再作更改,將Label1的ClientIDMode屬性值改成Predictable,而且設置其父容器控件grd_Account的ClientIDMode爲Static:
<
asp:GridView
ID
="grd_Account"
runat
="server"
AllowPaging
="True"
AutoGenerateColumns
="False"
DataKeyNames
="Account Number"
DataSourceID
="sds_account"
Height
="63px"
Width
="676px"
PageSize
="5"
ClientIDMode
="Static"
>
<
Columns
>
<
asp:TemplateField
HeaderText
="Account Number"
SortExpression
="Account Number"
>
<
ItemTemplate
>
<
asp:Label
ID
="Label1"
runat
="server"
Text
="Logged"
ClientIDMode
="Predictable"
></
asp:Label
>
</
ItemTemplate
>
</
asp:TemplateField
>
</
Columns
>
</
asp:GridView
>
其生成的HTML代碼爲:
<
table
cellspacing
="0"
rules
="all"
border
="1"
id
="grd_Account"
style
="height:63px;width:676px;border-collapse:collapse;"
>
<
tr
>
<
th
scope
="col"
>
Account Number
</
th
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="Label1_0"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="Label1_1"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="Label1_2"
>
Logged
</
span
>
</
td
>
</
tr
>
</
table
>
能夠看到生成的Label的控件的ID諸如:Label1_0正是上面所述格式:該控件自身ID(Label1)+"_"+[ClientIDRowSuffix](0)。
<3>若是父容器控件的ClientIDMode值爲Predictable
- 若是父容器控件或祖先容器控件爲顯示多個數據行的容器控件,那麼該控件的ClientID格式爲:父容器控件的ClientID+"_"+該控件自身ID+"_"+[ClientIDRowSuffix],其中ClientIDRowSuffix部分是什麼後面會單獨說明,可見這種狀況才屬於MSDN上所說的格式。
- 若是父容器控件或祖先容器控件都不是顯示多個數據行的容器控件,那麼該控件的ClientID格式爲:父容器控件的ClientID+"_"+該控件自身ID,可見這種狀況纔是MSDN上所說的格式。
下面我就舉一個父容器控件是多數據行容器控件且其ClientIDMode爲Predictable的例子,將上面的代碼再作更改,將Label1的ClientIDMode屬性值改成Predictable,而且設置其父容器控件grd_Account的ClientIDMode也爲Predictable:
<
asp:GridView
ID
="grd_Account"
runat
="server"
AllowPaging
="True"
AutoGenerateColumns
="False"
DataKeyNames
="Account Number"
DataSourceID
="sds_account"
Height
="63px"
Width
="676px"
PageSize
="5"
ClientIDMode
="Predictable"
>
<
Columns
>
<
asp:TemplateField
HeaderText
="Account Number"
SortExpression
="Account Number"
>
<
ItemTemplate
>
<
asp:Label
ID
="Label1"
runat
="server"
Text
="Logged"
ClientIDMode
="Predictable"
></
asp:Label
>
</
ItemTemplate
>
</
asp:TemplateField
>
</
Columns
>
</
asp:GridView
>
其生成的HTML代碼爲:
<
table
cellspacing
="0"
rules
="all"
border
="1"
id
="grd_Account"
style
="height:63px;width:676px;border-collapse:collapse;"
>
<
tr
>
<
th
scope
="col"
>
Account Number
</
th
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_Label1_0"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_Label1_1"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_Label1_2"
>
Logged
</
span
>
</
td
>
</
tr
>
</
table
>
能夠看到生成的Label的控件的ID諸如:grd_Account_Label1_0正是上面所述格式:父容器控件的ClientID(grd_Account)+"_"+該控件自身ID(Label1)+"_"+[ClientIDRowSuffix](0)。
<4>若是父容器控件的ClientIDMode值爲Inherit
這種狀況沒什麼好說的,由於父容器控件的ClientIDMode值會繼承其所在更上層的祖先容器控件的ClientIDMode值,繼承後也屬於上面三種狀況之一。
最後來講說ClientIDRowSuffix部分是什麼,若是父容器控件或祖先容器控件是顯示多數據行的容器控件(後面會討論到若是控件的ClientIDMode爲Predictable,在判斷該控件是否在顯示多數據行的容器控件中時,會有一種特殊的穿透現象),那麼父容器控件或祖先容器控件會有個屬性叫ClientIDRowSuffix,好比本例中的GridView的ClientIDRowSuffix屬性,這個屬性的做用是爲設定ClientIDMode值爲Predictable的子控件生成ClientID的後綴字符串(就是上面那些ClientID格式中的ClientIDRowSuffix部分):
若是 ClientIDRowSuffix 屬性爲空白,則在已生成的子控件ClientID末尾添加遞增的行號並在行號前面加上下劃線字符 (_) 分隔,好比上面的例子中因爲都沒有在GridView上設置ClientIDRowSuffix屬性,因此ClientIDRowSuffix爲空白,那麼生成的子控件ClientID最末位都有諸如_0、_一、_2等的遞增行號。
此外還能夠設置ClientIDRowSuffix 屬性值爲父容器控件或祖先容器控件中DataSource數據源中的字段,這樣生成子控件ClientID的後綴字符串爲ClientIDRowSuffix 指定字段在該行的數據值,而且ClientIDRowSuffix 屬性可指定多個DataSource數據源中的數據字段,那麼在生成子控件ClientID時會將每一個數據字段在該行的值用下劃線字符 (_) 進行分隔而後做爲ClientID後綴字符串。
如今就舉個例子,將上面的代碼再作更改將Label1的ClientIDMode屬性值改成Predictable,而且設置其父容器控件grd_Account的ClientIDMode也爲Predictable,而且將grd_Account的ClientIDRowSuffix設置爲數據源sds_account的Account Number字段:
<
asp:GridView
ID
="grd_Account"
runat
="server"
AllowPaging
="True"
AutoGenerateColumns
="False"
DataKeyNames
="Account Number"
DataSourceID
="sds_account"
Height
="63px"
Width
="676px"
PageSize
="5"
ClientIDMode
="Predictable"
ClientIDRowSuffix
="Account Number"
>
<
Columns
>
<
asp:TemplateField
HeaderText
="Account Number"
SortExpression
="Account Number"
>
<
ItemTemplate
>
<
asp:Label
ID
="Label1"
runat
="server"
Text
="Logged"
ClientIDMode
="Predictable"
></
asp:Label
>
</
ItemTemplate
>
</
asp:TemplateField
>
</
Columns
>
</
asp:GridView
>
其生成的HTML代碼爲:
<
table
cellspacing
="0"
rules
="all"
border
="1"
id
="grd_Account"
style
="height:63px;width:676px;border-collapse:collapse;"
>
<
tr
>
<
th
scope
="col"
>
Account Number
</
th
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_Label1_1060"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_Label1_1200"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="grd_Account_Label1_1510"
>
Logged
</
span
>
</
td
>
</
tr
>
</
table
>
能夠看到生成子控件的ClientID的後綴字符串爲Account Number字段在GridView上每行的值:1060、1200、1500,其再也不是遞增的行號.
EX:最後Predictable還有一個很特別的特性:
當控件的ClientIDMode爲Predictable且該控件在多個嵌套的容器控件中時,判斷該控件是否在顯示多數據行的容器控件中時,會具備層次穿透性,它不但會考察父容器控件,還會考察祖先容器控件。
下面就舉個例子來講明這種狀況,首先grd_Account爲顯示多數據行的容器控件,它的ClientIDMode設置爲Static,在它內部有一個ID爲LoginView1的LoginView,咱們知道LoginView也是容器性控件,只不過它不是顯示多數據行的容器控件,這裏設置LoginView1的ClientIDMode屬性爲Predictable,在LoginView1裏面再放置一個ID爲Label1的Label,它的ClientIDMode沒有設置,因此其值也默認繼承爲Predictable,下面是代碼:
<
asp:GridView
ID
="grd_Account"
runat
="server"
AllowPaging
="True"
AutoGenerateColumns
="False"
DataKeyNames
="Account Number"
DataSourceID
="sds_account"
Height
="63px"
Width
="676px"
PageSize
="5"
ClientIDMode
="Static"
>
<
Columns
>
<
asp:TemplateField
HeaderText
="Account Number"
SortExpression
="Account Number"
>
<
ItemTemplate
>
<
asp:LoginView
ID
="LoginView1"
runat
="server"
ClientIDMode
="Predictable"
>
<
LoggedInTemplate
>
<
asp:Label
ID
="Label1"
runat
="server"
Text
="Logged"
></
asp:Label
>
</
LoggedInTemplate
>
</
asp:LoginView
>
</
ItemTemplate
>
</
asp:TemplateField
>
</
Columns
>
</
asp:GridView
>
能夠看到在嵌套層次結構中,因爲LoginView1所屬的父容器控件grd_Account是顯示多數據行的容器控件,因此LoginView1的ClientID應該是諸如:LoginView1_0、LoginView1_1等有ClientIDRowSuffix部分的格式,這沒有問題,可是按道理來講Label1所在的父容器控件LoginView1不是顯示多數據行的容器控件,那麼Label1生成的ClientID 應該是諸如LoginView1_0_Label一、LoginView1_1_Label1等這樣的沒有ClientIDRowSuffix部分的格式,可是爲咱們來看一下生成的HTML代碼:
<
table
cellspacing
="0"
rules
="all"
border
="1"
id
="grd_Account"
style
="height:63px;width:676px;border-collapse:collapse;"
>
<
tr
>
<
th
scope
="col"
>
Account Number
</
th
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="LoginView1_0_Label1_0"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="LoginView1_1_Label1_1"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="LoginView1_2_Label1_2"
>
Logged
</
span
>
</
td
>
</
tr
>
</
table
>
能夠看到生成的Label1的ClientID都帶表示遞增行號的後綴字符串0、一、2等(此外請注意LoginView不會產生HTML代碼),也就是有ClientIDRowSuffix部分。
咱們再將grd_Account的ClientIDRowSuffix屬性更改成數據源中的Account Number字段後再來看看生成的HTML代碼:
<
table
cellspacing
="0"
rules
="all"
border
="1"
id
="grd_Account"
style
="height:63px;width:676px;border-collapse:collapse;"
>
<
tr
>
<
th
scope
="col"
>
Account Number
</
th
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="LoginView1_1060_Label1_1060"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="LoginView1_1200_Label1_1200"
>
Logged
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
<
span
id
="LoginView1_1510_Label1_1510"
>
Logged
</
span
>
</
td
>
</
tr
>
</
table
>
能夠看到生成的Label1的ClientID也都帶ClientIDRowSuffix部分,只不過ClientIDRowSuffix部分如今是數據源中Account Number字段的值。
因而可知在判斷Label1是否在顯示多數據行的容器控件中時,斷定機制進行了穿透,即假若有這麼一組嵌套的容器控件:Control_1<-Control_2<-Control_3<-.......<-Control1_n-1<-Control_n,其中Control_n的ClientIDMode設置或繼承爲Predictable,且其中Control_2到Control1_n-1都不是顯示多數據行的容器控件,只有最外層的Control_1是顯示多數據行的容器控件,那麼在斷定最裏面的Control_n是否在顯示多數據行的容器控件時,參考的是從裏到外第一個是顯示多數據行的容器控件Control_1(即從裏到外只要任意一個容器控件是顯示多數據行的容器控件,就認爲Control_n是在顯示多數據行的容器控件中),而且Control_n的後綴字符串部分參考的也是Control_1的ClientIDRowSuffix屬性,從而忽略中間那些不是顯示多數據行的容器控件(Control_2到Control1_n-1),因此控件的ClientIDMode屬性爲Predictable時,就是用這種穿透斷定來判斷該控件是否在顯示多數據行的容器控件中的。