一区二区久久-一区二区三区www-一区二区三区久久-一区二区三区久久精品-麻豆国产一区二区在线观看-麻豆国产视频

UniqueID和ClientID的來源

在《漫話ID》一文中,作者提出了一個問題:為什么在ItemCreated事件中訪問ClientID會導(dǎo)致MyButton無法響應(yīng)事件,事實上MyButton無法響應(yīng)事件是因為他在客戶端的ID被改變了,而此文從UniqueID和ClientID入手,進行較為深入的探討,展示UniqueID和ClientID是如何生成的,在何時生成,并同時解答《漫話ID》一文中作者的疑問。

為什么有UniqueID和ClientID

這個我想多數(shù)使用WebForm的人已經(jīng)知道了,很大原因上是因為WebForm中存在著數(shù)據(jù)綁定控件以及自定義控件、View控件之類可以擁有子控件的控件,這就導(dǎo)致在控件樹中很有可能存在著2個ID完全相同的對象,但是由于HTML并不允許ID的重復(fù),于是WebForm發(fā)明出了一個叫NamingContainer的東西,并給控件提供了UniqueID和ClientID這兩個屬性,用于區(qū)分彼此。

雖然這兩個屬性經(jīng)常給我們?nèi)莵磉@樣那樣的麻煩,但從總體上來說,這是一個良好的設(shè)計,無可厚非。

UniqueID和ClientID如何計算

首先需要明白的一點是,在WebForm中所有的控件最終形成一個樹,每個控件是樹中的一個節(jié)點。

在樹型的結(jié)構(gòu)中,每一個節(jié)點都擁有0個或1個父節(jié)點,而UniqueID和ClientID正是基于“從樹的根部到當前節(jié)點的路徑”來計算UniqueID的,我們看一下以下的示例:

<ASP:Repeater ID="MyRepeater" runat="server">
<
ItemTemplate>
<
ASP:Button ID="MyButton" runat="server" Text="My Button" />ItemTemplate>
ASP:Repeater>

我們在MyRepeater中放置了MyButton,而這樣簡單的內(nèi)容生成出的這個MyButton的最終HTML如下:

請注意看,按鈕的ID變?yōu)榱薓yRepeater_ctl00_MyButton,Name則變?yōu)榱薓yRepeater$ctl00$MyButton,根據(jù)《漫話ID》一文中所述,這個ID正好是服務(wù)器端控件的ClientID,而Name又正好是服務(wù)器端控件的UniqueID。
我們現(xiàn)在要探索的,是為什么ID會變成這個樣子,因此我們先從字面上進行理解,不能看出:

  1. 在這個ID中,有“MyRepeater”字樣,而這正是MyButton的父節(jié)點元素Repeater的服務(wù)器端ID
  2. 中間有“ctl00”字樣,根據(jù)我們編程的習慣,ctl應(yīng)該表示著control的意思,而00自然就是索引了,這里正好表示“此MyButton是Repeater產(chǎn)生的列表中的第1個元素(下標為0)”
  3. 最后有“MyButton”字樣,正好是MyButton的服務(wù)器端ID

好了,將ID分解以后我們就能非常清晰地理解ClientID的生成策略了:

生成ClientID的方法是,在控件本身的ID前加上此控件在數(shù)據(jù)綁定(如果有)中的索引再加上父控件的ClientID

那么這是怎么做到的呢,這里就提出了NamingContainer的概念,對于NamingContainer,只需要實現(xiàn)一個標記接口INamingContainer。
當控件在計算ClientID時,會查找當前控件的NamingContainer屬性是否為null,如果不是則調(diào)用該NamingContainer的一個叫GetUniqueIDPrefix的方法,當然這個方法是遞歸的,在方法體內(nèi)又會去調(diào)用另一個GetUniqueIDPrefix方法。
至于NamingContainer這個屬性是怎么來的,事實上是在調(diào)用Controls.Add方法時加上去的,大家可以使用反編譯看一下Control類一個叫AddedControl方法的實現(xiàn),在此就不作贅述。

回到問題

現(xiàn)在我們回到《漫話ID》一文中的問題,為什么在DataGrid控件的ItemCreated事件中去調(diào)用ClientID會影響以后的執(zhí)行。

我想令作者感到困惑的是,他僅僅獲取了ClientID,而沒有對其作任何的修改。作者認為他并沒有影響到Button的狀態(tài),因此Button應(yīng)當按其正常的方式進行渲染。

在這里就不得不說一個問題,非常多的人認為屬性這東西的讀取僅僅是一個簡單的return,他們沒有想到在屬性的get體中可以進行非常多的邏輯,而這些邏輯又很有可能影響到對象的狀態(tài),而我想正是這種“想當然”致使作者沒有仔細地去看ClientID的get過程中,Button到底發(fā)生了什么。那么就由我來大致地梳理一下這個思路吧。

首先我反編譯了ClientID屬性,可以看到他的get體的內(nèi)容,其代碼如下:

public virtual string ClientID
{
get
{
this.EnsureID();
string uniqueID = this.UniqueID;
if ((uniqueID != null) && (uniqueID.IndexOf(this.IdSeparator) >= 0))
{
return uniqueID.Replace(this.IdSeparator, '_');
}
return uniqueID;
}
}

可以看到,ClientID的get遠不止return this._clientID;這么簡單,事實是,控件根本不保存當前的ClientID,而是在每一次獲取時都從UniqueID去計算,并將UniqueID中的分隔符(也就是$)替換為下劃線(_)并返回。

再仔細地看代碼,我們會發(fā)現(xiàn)首先調(diào)用了EnsureID方法,這就像我們在自定義控件的時候會調(diào)用EnsureChildControls方法一個,EnsureID會檢測當前控件的ID是否已經(jīng)生成,如果沒有生成則會使用一定的策略進行生成,下面就是EnsureID方法的代碼:

protected void EnsureID()
{
if (this._namingContainer != null)
{
if (this._id == null)
{
this.GenerateAutomaticID();
}
this.flags.Set(0x800);
}
}
我們看到,在EnsureID方法中就用到了NamingContainer,當且僅當NamingContainer不為null的時候,該方法才有作用。
什么嘛,結(jié)果在EnsureID方法中根本就沒有涉及到UnqiueID的計算問題,呵呵是不是有被騙的感覺?
但是這么一來,又是什么影響著ClientID呢,再仔細地回看ClientID的get方法體,最后也只能說是UniqueID這個屬性在搞鬼了。
對,UniqueID和ClientID一樣,并不是一個簡單的屬性,他在get體內(nèi)也包含了大量的邏輯,以下就是這些邏輯:
public virtual string UniqueID
{
get
{
if (this._cachedUniqueID == null)
{
Control namingContainer = this.NamingContainer;
if (namingContainer == null)
{
return this._id;
}
if (this._id == null)
{
this.GenerateAutomaticID();
}
if (this.Page == namingContainer)
{
this._cachedUniqueID = this._id;
}
else
{
string uniqueIDPrefix = namingContainer.GetUniqueIDPrefix();
if (uniqueIDPrefix.Length == 0)
{
return this._id;
}
this._cachedUniqueID = uniqueIDPrefix + this._id;
}
}
return this._cachedUniqueID;
}
}
我想代碼已經(jīng)非常清楚了,如果NamingContainer為null,則UniqueID只會返回當前控件的ID,否則將會通過NamingContainer的GetUniqueIDPrefix方法去計算本文一開始說的那種格式的ID并返回。
好了,至此我們已經(jīng)把ClientID的生成過程分析清楚了,這里總結(jié)一下:
  1. ClientID是由UniqueID經(jīng)過簡單的字符串替換形成的
  2. UniqueID是通過NamingContainer形成的

看到這里我想《漫話ID》一文的作者已經(jīng)明白了吧,為什么在ItemCreated事件中訪問ClientID會導(dǎo)致最終HTML頁面上的2個Button都叫“MyButton”,如果還不明白,請去斷點調(diào)試ItemCreated事件,看看MyButton的NamingContainer是什么。
呵呵,你以為MyButton的NamingContainer是null?那么你肯定沒調(diào)試過哦~~

在ItemCreated事件中,MyButton的NamingContainer是DataGridItem,但問題在于DataGridItem此時
并沒有加入到DataGrid中(在ItemDataBound事件中才被加入到DataGrid中),因此DataGridItem的
NamingContainer是null。
而DataGridItem的GetUniqueIDPrefix方法則需要知道自己在DataGrid中的索引,以便返回類似ctl00這樣的
格式,既然沒有加入到DataGrid中,DataGridItem就只能返回一個空字符串。
MyButton將DataGridItem返回的空字符串和自己的ID一拼接,就形成了UniqueID,此時的UniqueID正好是
自己的ID。
并且MyButton又將這個UniqueID給保存了起來,在今后的訪問中不會再一次重新計算,于是這個不規(guī)范的
UniqueID也將一直被使用到成為HTML。

總結(jié)

發(fā)現(xiàn)自己的表達能力實在很差,不知道有沒有將這個問題說清楚,總之:

  1. 對ClientID屬性的訪問將直接導(dǎo)致控件計算UniqueID
  2. 在不恰當?shù)臅r候計算UniqueID,將可能導(dǎo)致錯誤的結(jié)果
  3. 要保證控件與當前頁面的控件樹的根連通的情況下才訪問ClientID
  4. .NET中有很多屬性并不是簡單的return,他們會改變對象的狀態(tài),往往這就是解決問題的切入點

NET技術(shù)UniqueID和ClientID的來源,轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 美女被日视频 | 97人人模人人爽视频一区二区 | 在线小视频国产 | 国产a久久精品一区二区三区 | 日韩一区二区三区视频 | 欧美经典成人在观看线视频 | 91久久网| 亚洲欧洲综合 | 欧美五月激情 | 青青草国产97免久久费观看 | 国产极品白嫩美女在线观看看 | 久久九色综合九色99伊人 | 成人在线视频国产 | 激情小说专区 | 野外一男一女一级毛片 | 视频在线欧美 | 亚洲清色 | 欧美精品第56页在线视频观看 | 日韩一区二区免费看 | 久久99精品国产麻豆 | 香蕉视频成人在线观看 | 国产免费观看视频 | 日韩精品视频观看 | 伊人色在线观看 | 日本在线视频www色 日本在线视频免费观看 | 久久精品国产亚洲网站 | 欧美一级淫片a免费播放口aaa | 国产综合久久久久久 | 日本道精品一区二区三区 | 国产精品99久久 | 亚洲国产91 | 青青草国产青春综合久久 | 四虎影视亚洲精品 | 亚洲一区日韩一区欧美一区a | 久久精品国产一区二区三区肥胖 | 国产成人亚洲精品2020 | 色综合五月 | 色播.com| 国产福利在线永久视频 | 成人福利网| 欧美三级视频 |