2014-02-08 20 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); 
} 
相关问题