“用過的控制項”就是指曾經加入過Controls(子控制項集合)中的控制項。這些“用過的控制項”再一次被加入Controls中時,可能造成控制項ID衝突,癥狀為:
中文版的錯誤資訊:
[HttpException (0x80004005): 找到多個具有相同 ID“c1:_ctl1:_ctl0:_ctl0”的控制項。Trace 要求控制項具有唯一的 ID。]
另附上英文版的錯誤資訊(便於使用google查詢英文資料):
[HttpException (0x80004005): Multiple controls with the same ID“c1:_ctl1:_ctl0:_ctl0”. Trace requires that controls have unique IDs. ]
由於出錯點位於.Net架構的基類中,調試跟蹤都不太方便,費了不少功夫才發現問題。這個錯誤的原因與ASP.NET中的控制項命名處理方式有關:
ASP.NET中的所有控制項都有Controls(子控制項集合)這個屬性,在將子控制項加入Controls中時,ASP.NET會對加入的控制項進行命名操作。如果該控制項已經指定了ID(即ID不為空白),則沿用該控制項已有的名字;如果控制項沒有指定ID(即ID為空白),則系統會為該控制項自動分配一個名字(自動命名的規則尚不清楚)。問題就出在這個自動分配的名字上,這個名字可能會與非自動命名控制項的名字相同(這個不知道算不算系統Bug?),於是就會出現上面的錯誤資訊。
系統自動指定的ID名字都以底線開頭,所以只要非自動命名的控制項名字都不以底線開頭,就可以有效避免發生重複。同一次進行自動命名的控制項之間是不會發生名字重複的,但在回傳情況下,控制項可能會建立兩次,這時候也容易發生名字重複。例如:
編寫這樣一個容器型控制項MyPanel,有兩個Panel為工具列和內容欄,其中都可以加入若干控制項。
用兩個ArrayList來存放加入的控制項。並在CreateChildControls()中將ArrayList中的控價加入Controls中,如下:
foreach (control c in m_List1)
{
Controls.Add(c);
}
上述寫法實際上是有隱患的,如果在PostBack情況下,MyPanel可能被建立兩次,在第一次建立MyPanel時,系統為m_List1中的控制項分配了自動命名的ID,然後MyPanel又被建立了一次,而m_List1中的控制項來源於父類,可能並沒有重新建立,依然是那些已被自動命名的控制項(以底線開頭的ID),這時系統不會為m_List1中的控制項自動命名,這些控制項的名稱就很容易與系統中的自動命名控制項重複,發生系統錯誤。
上面說的這個問題比較隱蔽,但也比較容易避免,比如採用如下寫法就可以:
foreach (control c in m_List1)
{
c.ID = String.Empty;
Controls.Add(c);
}
將控制項的ID置為空白,系統就會為控制項分配自動命名的ID了。但這個方法會清空非自動命名控制項的ID,所以還可以加上判斷,當控制項ID以底線開頭時,才進行清空。這裡也要提醒大家,對控制項指定ID時,不要指定以底線開頭的名稱,以免重複!