2014-02-08 22 views
2
protected override void Render(HtmlTextWriter writer) 
{ 
    HtmlGenericControl div1 = new HtmlGenericControl("div"); 
    div1.Attributes.Add("class", "modalbox"); 
    if (!ClientVisible) 
     div1.Attributes.CssStyle.Add("display", "none"); 
    HtmlGenericControl div2 = new HtmlGenericControl("div"); 
    div2.Attributes.Add("class", "modalbox-m1"); 
    HtmlGenericControl div3 = new HtmlGenericControl("div"); 
    div3.Attributes.Add("class", "modalbox-m2"); 
    foreach (Control c in this.Controls) 
     div3.Controls.Add(c); // exception here 
    div2.Controls.Add(div3); 
    div1.Controls.Add(div2); 
    div1.RenderControl(writer); 
} 

我無法理解WHO和WHERE變更this.Controls爲什麼在PlaceHolder.Controls上拋出「Collection被修改」?

+1

倍率的'CreateChildControls'方法和放置上面的代碼,並檢查 – Damith

回答

2

的問題是,c,作爲一個Control有一個Parent和雙向關係將控件添加到ControlCollection時建立。在多個ControlCollections中存在相同的控件不會存在,除非出現問題。

所以當c加到div3,它首先removes itself從以前的父容器 - 在這種情況下也就是c.Parent.Controls中,相同的對象作爲this.Controls,正被重複。最終結果是變形例外,添加控制到一個不同的收集從所述迭代收集刪除..

避免此異常的簡單的方法是創建集合的副本,與IEnumerable.ToList 。新的列表獨立於ControlCollection,在刪除控件時不會被修改。

// Note the "ToList()", from LINQ/Enumerable 
foreach (Control c in this.Controls.ToList()) { 
    div3.Controls.Add(c); // exception here 
} 

然而,添加控件這樣的裏面Render「相當可疑」而且有很多情況下,它會突破在回發控制。


如果我只是想換控件集合代碼和標記不會影響控件樹,然後我會使用類似如下的不創造新的控制產生渲染輸出合適或以其他方式修改控制樹。 HtmlTextWriter提供了生成相關標記的所有基本方法,即使它可能很乏味。

protected override void Render(HtmlTextWriter writer) 
{ 
    writer.AddAttribute(HtmlTextWriterAttribute.Class, "modalbox"); 
    if (!ClientVisible) { 
     writer.AddAttribute(HtmlTextWriterAttribute.Style, "display: none"); 
    } 
    writer.RenderBeginTag(HtmlTextWriterTag.Div); // div1 

    writer.AddAttribute(HtmlTextWriterAttribute.Class, "modalbox-m1"); 
    writer.RenderBeginTag(HtmlTextWriterTag.Div); // div2 

    writer.AddAttribute(HtmlTextWriterAttribute.Class, "modalbox-m2"); 
    writer.RenderBeginTag(HtmlTextWriterTag.Div); // div3 

    // Render children. 
    // None of the control collections are altered in this process. 
    // (This may render other content if sub-classing an 
    // existing control; in that case, loop and render the 
    // the children individually, if such is appropriate.) 
    base.Render(writer); 

    writer.RenderEndTag(); // div3 
    writer.RenderEndTag(); // div2 
    writer.RenderEndTag(); // div1 
} 

提示:RenderBeginTag/RenderEndTag工作作爲堆疊,並通過屬性設置的addAttribute總是適用於未來RenderBeginTag。


但在實踐中,我強烈建議(和經常使用)User Controls允許ASCX標記和創建控件樹聲明。

+0

很好的答案!我只需要用3個div來包裝基於PlaceHolder的自定義控件的內容。也許我做錯了地方? – Denis

+0

@Denis只需將它添加到佔位符控件(或直接使用一些合適的衍生品) - OnPreRender是* latest *,可以可靠地修改控件集合,但它應該經常發生(在OnLoad或甚至是PostBack事件中)。 – user2864740

+0

@Denis我建議使用UserControls(以便大部分控件可以用Markup編寫),但創建自定義的ServerControl也是合適的。然後在控制的渲染中:'/ *在HTML *// *之前寫入呈現子控件,但不要移動它們*// *寫入結束HTML * /'(使用UserControl和ASCX,HTML當然「寫在」標記這就是爲什麼我更喜歡它)。 – user2864740

1

您的代碼是通過將this.controls中的控件添加到div3集合來改變它。

我不知道確切的機制,但我懷疑這個動作導致c重新註冊,這會影響this.controls

的解決方案是將其更改爲一個for循環,通過this.controls倒過來:

 for (int i = this.Controls.Count - 1; i >= 0; i--) { 
      Control c = this.Controls[i]; 
      div3.Controls.Add(c); 
     } 
+0

'div3'是'HtmlGenericControl'創建以上幾行。據我所知,它與'this'完全隔離。我填充它的'Controls'列表 - 爲什麼'this.Controls'改變?無法理解。 – Denis

+1

@denis:問題是,當你從this.Controls集合中添加一個項目到div3.Controls集合時,可能會有一個內部操作隱式地從this.Controls中刪除控件並將其添加到div3.Controls。在web中,Controls集合都是分層的,所以this.Controls並不代表頁面中的每一個控件,只有那些在頂層的控件。 –

1

我的最終溶液:

protected override void CreateChildControls() 
{ 
    HtmlGenericControl div1 = new HtmlGenericControl("div"); 
    div1.Attributes.Add("class", "modalbox"); 
    if (!ClientVisible) 
     div1.Attributes.CssStyle.Add("display", "none"); 
    HtmlGenericControl div2 = new HtmlGenericControl("div"); 
    div2.Attributes.Add("class", "modalbox-m1"); 
    HtmlGenericControl div3 = new HtmlGenericControl("div"); 
    div3.Attributes.Add("class", "modalbox-m2"); 
    div1.Controls.Add(div2); 
    div2.Controls.Add(div3); 
    while (Controls.Count != 0) 
     div3.Controls.Add(Controls[0]); 
    Controls.Add(div1); 
} 
相關問題