我目前正在ASP.net中编写自定义服务器控件。我的想法是创建某种可折叠的面板控件(顺便说一句,它是我正在编写的第一个服务器控件)。所以我想要的是用户在页面中以这种方式定义我的控件:
<myCtrl:CollapsiblePanel id="myCollapsiblePanel" runat="server">
<asp:Label id="lblTest" runat="server" Text="Test Label:"/>
<asp:TextBox id="txbTest" runat="server"/>
</myCtrl:CollapsiblePanel>
然后控件应在最后一页上呈现如下:
<div id="myCollapsiblepanel">
<input type="submit" name="btnExpand" value="Expand/Collapse" id="btnExpand" />
<div id="pnlContent">
<span id="lblTest">Title:</span>
<input name="txbTest" type="text" id="txbTest" />
</div>
</div>
提交按钮“btnExpand”然后在控件上有一个事件处理程序,它隐藏/显示面板“pnlContent”。 我的第一种方法是创建一个继承自Panel的服务器控件。在CollapsiblePanel控件内部,我创建了另一个Panel“pnlContent”。在OnInit中,我做了类似以下的事情:
protected override void OnInit(EventArgs e)
{
for (int i = 0; i < base.Controls.Count; i++)
{
Control control = base.Controls[i];
content.Controls.Add(control);
}
base.Controls.Clear();
base.Controls.Add(button);
base.Controls.Add(content);
}
我基本上接受了控件中当前存在的所有控件,并将它们添加到内部创建的Panel pnlContent的控件列表中。此外,我添加了按钮,然后进行扩展/折叠然后pnlContent。 我的问题是,某种程度上渲染无法正常工作。按钮和pnlContent正确呈现,只显示pnlContent内的控件。输出类似于以下
<div id="myCollapsiblepanel">
<input type="submit" name="btnExpand" value="Expand/Collapse" id="btnExpand" />
<div id="pnlContent"> </div>
</div>
pnlContent为空... 有人可以建议我解决这个和/或我做错了渲染。我没有覆盖我继承的Panel的默认渲染,因为我认为这不应该是必要的,因为我在OnInit中修改了它的控制结构,所以它应该渲染我的修改..
目前我找到了解决方案。我基本上这样做的方式与Microsoft Ajax Control工具包中最常用的方式相同。我创造了一个所谓的“扩展器”,它不会自我渲染而只是增加了行为。所以现在我添加面板的id以折叠到我的CollapsiblePanelExtender控件,然后它完成折叠/扩展的工作。也许这也是更好更清洁的解决方案,但我仍然想知道我之前描述的第一种方法出了什么问题。
感谢您的帮助!
答案 0 :(得分:3)
这是因为在任何给定时刻控件只能属于一个父级。将控件从一个集合添加到另一个集合时,它会自动从第一个集合中删除。在你的情况下:
for (int i = 0; i < Controls.Count; i++)
{
//obtain a reference
Control control = Controls[i];
//The control is removed from Controls collection
//and added to content.Controls. All remaining controls get shifted left.
//This means that next control will get skipped.
content.Controls.Add(control);
}
考虑做以下事情(顺便说一下,在这种情况下你可能不需要使用 base ):
Control[] controls = new Control[Controls.Count];
Controls.CopyTo(controls, 0); //create a snapshot of controls
for (int i = 0; i < controls.Length; i++)
content.Controls.Add(controls[i]);
或使用反向添加:
Control[] controls = new Control[Controls.Count];
for (int i = Controls.Count - 1; i >= 0; i--) //walk backwards
controls[i] = Controls[i];
for (int i = 0; i < controls.Length; i++)
content.Controls.Add(controls[i]);
答案 1 :(得分:0)
使用Controls集合进行混乱,特别是在Control容器之间删除和交换控件并不总是有效。
您是否特别不希望在回发之前呈现内容?为什么不渲染控件并将它们包装在CSS上的display:none
div中,然后可以使用像jQuery这样容易的东西在客户端交换?
e.g。
<myCtrl:CollapsiblePanel id="myCollapsiblePanel" runat="server" Text="Panel title">
<asp:Label id="lblTest" runat="server" Text="Test Label:"/>
<asp:TextBox id="txbTest" runat="server"/>
</myCtrl:CollapsiblePanel>
您应该在CreateChildControls重载中创建应用它:
protected override CraeteChildControls()
{
Controls.Add(new LiteralControl("<"+"h1>"+Text+"<a onclick=\"expandPanel\">[+]</a></h1>"));
Panel panel=new Panel();
// add controls in collapsable panel to panel here
Controls.Add(panel);
}
翻译为:
<div id="ctl00_etc_myCollapsiblePanel">
<h1>Panel title <a onclick="expandPanel">[+]</a></h1>
<div id="childPanel" style="display:none">
<label id="ctl_00_etc">Test Label</label>
<input ... />
</asp:Panel>
</div>
和一些jQuery(或类似的)附加到<a>
:
function expandPanel() {
$('<%=childPanel.ClientID %>').css('display','block');
}
当然,所有这些都是伪代码。