2013-07-30 95 views
1

我想创建一个自定义服务器控件VersionedContentControl,这将允许我指定最终标记的不同变体。使用嵌套可选控件创建自定义服务器控件

实例:

<custom:VersionedContentControl runat="server" VersionToUse="2"> 
    <ContentVersions> 
     <Content Version="1"> 
      <asp:HyperLink runat="server" ID="HomeLink" NavigateUrl="~/Default.aspx">Home</asp:HyperLink> 
     </Content> 
     <Content Version="2"> 
      <asp:LinkButton runat="server" ID="HomeLink" OnClick="GoHome">Home</asp:LinkButton> 
     </Content> 
     <Content Version="3"> 
      <custom:HomeLink runat="server" ID="HomeLink" /> 
     </Content> 
    </ContentVersions> 
</custom:VersionedContentControl> 

使用上述标记,我希望在页面上被利用的唯一LinkButton控制。

长篇

我有很大的困难试图确定这种自定义控件。我甚至没有能够找到使用这样的嵌套控件的MSDN上的一个很好的例子。相反,我不得不求助于以下博客文章作为例子:

不幸的是,一切我都试过了悲惨地失败了。以下是我目前有:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web.UI; 

namespace CustomControls 
{ 
    [ParseChildren(true)] 
    [PersistChildren(false)] 
    public class VersionedContentControl : Control, INamingContainer 
    { 
     public string VersionToUse { get; set; } 

     [PersistenceMode(PersistenceMode.InnerProperty)] 
     public IList<Content> ContentVersions { get; set; } 

     protected override void OnInit(EventArgs e) 
     { 
      base.OnInit(e); 
      var controlToUse = ContentVersions.Single(x => x.Version == VersionToUse); 
      Controls.Clear(); 
      controlToUse.InstantiateIn(this); 
     } 
    } 

    public class Content : ITemplate 
    { 
     public string Version { get; set; } 

     public void InstantiateIn(Control container) 
     { 
      // I don't know what this method should do 
     } 
    } 

    public class ContentVersionsList : List<Content> {} 
} 

虽然我还没有实现InstantiateIn,我的内容的所有3个版本出现在页面上;它显示3个链接。

此外,我实际上不能使用该控件,除非我为每个嵌套控件指定不同的ID属性值;他们都不能使用"HomeLink"。我希望能够重新使用ID,以便我可以从后面的代码访问控制。

我意识到,通常禁止为页面上的多个控件指定重复的ID值。但是,在MSDN documentation for System.Web.UI.MobileControls.DeviceSpecific中,示例对嵌套控件使用重复的ID值。事实上,这个例子非常接近我想要做的事情;它基于移动设备兼容性过滤器而不同。

<mobile:Form id="Form1" runat="server"> 
    <mobile:DeviceSpecific Runat="server"> 
     <Choice Filter="isHTML32"> 
      <HeaderTemplate> 
       <mobile:Label ID="Label1" Runat="server"> 
        Header Template - HTML32</mobile:Label> 
       <mobile:Command Runat="server"> 
        Submit</mobile:Command> 
      </HeaderTemplate> 
      <FooterTemplate> 
       <mobile:Label ID="Label2" Runat="server"> 
        Footer Template</mobile:Label> 
      </FooterTemplate> 
     </Choice> 
     <Choice> 
      <HeaderTemplate> 
       <mobile:Label ID="Label1" Runat="server"> 
        Header Template - Default</mobile:Label> 
       <mobile:Command ID="Command1" Runat="server"> 
        Submit</mobile:Command> 
      </HeaderTemplate> 
      <FooterTemplate> 
       <mobile:Label ID="Label2" Runat="server"> 
        Footer Template</mobile:Label> 
      </FooterTemplate> 
     </Choice> 
    </mobile:DeviceSpecific> 
</mobile:Form> 

这将是很好看这些控件的源代码,看看他们是如何做到这一点,但不幸的是,它不是开源。

我的问题

我怎样才能创建一个包含嵌套控件列表的自定义服务器控件,仅呈现基于属性的嵌套控件一个?理想情况下,在单独的嵌套控件中重新使用ID。

+0

我希望的功能实际上是(大部分)由内置控件提供的:['MultiView'](http://msdn.microsoft.com/zh-cn/library/system.web.ui .webcontrols.multiview%28V = VS.100%29.aspx)。我甚至不知道这个控制存在!不幸的是,它不允许重用ID [例如'ITemplate'实例](http://stackoverflow.com/a/3671788/346561)。我会继续尝试。 –

回答

1

TL;博士

我结束了使用MultiView控制。它不允许重复的ID,但它满足我所有的其他要求,它可以避免必须维护任何自定义代码。

我试过

我终于能够得到的东西与此代码工作

using System.Collections.Generic; 
using System.Linq; 
using System.Web.UI; 

namespace CustomControls 
{ 
    [ParseChildren(true)] 
    [PersistChildren(false)] 
    public class VersionedContent : Control 
    { 
     public string VersionToUse { get; set; } 

     [PersistenceMode(PersistenceMode.InnerProperty)] 
     [TemplateContainer(typeof(ContentContainer))] 
     [TemplateInstance(TemplateInstance.Multiple)] 
     public List<Content> ContentVersions { get; set; } 

     public override ControlCollection Controls 
     { 
      get 
      { 
       EnsureChildControls(); 
       return base.Controls; 
      } 
     } 

     public ContentContainer ContentContainer 
     { 
      get 
      { 
       EnsureChildControls(); 
       return _contentContainer; 
      } 
     } private ContentContainer _contentContainer; 

     protected override void CreateChildControls() 
     { 
      var controlToUse = ContentVersions.Single(x => x.Version == VersionToUse); 
      Controls.Clear(); 
      _contentContainer = new ContentContainer(); 
      controlToUse.InstantiateIn(_contentContainer); 
      Controls.Add(_contentContainer); 
     } 
    } 

    public class Content : Control, ITemplate 
    { 
     public string Version { get; set; } 

     public void InstantiateIn(Control container) 
     { 
      container.Controls.Add(this); 
     } 
    } 

    public class ContentContainer : Control { } 
} 

这允许我使用像这样的控制:

<custom:VersionedContent ID="VersionedContentControl" runat="server" VersionToUse="1"> 
    <ContentVersions> 
     <custom:Content Version="1"> 
      <custom:MyControlV1 runat="server" /> 
     </custom:Content> 
     <custom:Content Version="2"> 
      <custom:MyControlV2 runat="server" CustomProperty="Foo" /> 
     </custom:Content> 
    </ContentVersions> 
</custom:VersionedContent> 

不幸,这个解决方案有几个缺点...

  • 即使我用Template情况下,我是不允许使用重复的ID在不同的Content部分
  • 的ViewState没有被妥善处理
    • 回发不能正常工作
    • 验证不在所有

工作着眼于MultiView控制的反编译的源代码之后(其为Si我意识到我不得不让代码变得复杂得多,才能使其工作正常。我只通过使用MultiView控件获得的唯一一个可能是能够使用重复的ID,如果我甚至可以得到这个工作。我决定最好只使用内置的MultiView控件。

0
protected override void RenderContents(HtmlTextWriter output) 
     { 

      output.Write("<div><div class=\"UserSectionHead\">"); 

      Label l = new Label() { Text = Label }; 
      TextBox t = new TextBox() { Text = Text }; 
      l.AssociatedControlID = t.ID; 
      l.RenderControl(output); 

      output.Write("</div><div class=\"UserSectionBody\"><div class=\"UserControlGroup\"><nobr>"); 

      t.RenderControl(output); 

      output.Write("</nobr></div></div><div style=\"width:100%\" class=\"UserDottedLine\"></div>"); 

     } 
相关问题