自定义ASP.net服务器控件 - 渲染

时间:2009-03-21 07:48:19

标签: asp.net controls

我目前正在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控件,然后它完成折叠/扩展的工作。也许这也是更好更清洁的解决方案,但我仍然想知道我之前描述的第一种方法出了什么问题。

感谢您的帮助!

2 个答案:

答案 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');
}

当然,所有这些都是伪代码。