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);
}
我无法理解谁在哪里更改了 this.Controls
?
最佳答案
问题在于c
,是Control
,有一个 Parent 当控件添加到 ControlCollection
时,就会建立双向关系。 。相同的控件不会存在于多个ControlCollection中,除非某些东西被破坏。
因此,当将 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 标记并创建声明性控制树。
关于c# - 为什么 foreach on PlaceHolder.Controls 抛出 "Collection was modified"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21640644/