【问题标题】:Create a custom server control with nested optional controls使用嵌套的可选控件创建自定义服务器控件
【发布时间】:2013-07-31 06:48:49
【问题描述】:

我想创建一个自定义服务器控件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。

【问题讨论】:

  • 我想要的功能实际上(大部分)由内置控件提供:MultiView。我什至不知道这个控件的存在!不幸的是,它不允许重复使用 ID like ITemplate instances do。我会继续努力。

标签: c# asp.net webforms custom-controls


【解决方案1】:
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>");

        }

【讨论】:

    【解决方案2】:

    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 实例,也不允许在单独的 Content 部分中使用重复的 ID
    • ViewState 未正确处理
      • 回发无法正常工作
      • 验证根本不起作用

    查看MultiView 控件的反编译源代码(类似)后,我意识到我必须使代码更加复杂才能使其按需要工作。我唯一能比使用MultiView 控件获得的好处是可能能够使用重复的ID,如果我什至可以让它工作。我决定最好还是满足于使用内置的MultiView 控件。

    【讨论】:

      猜你喜欢
      • 2011-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-08
      • 2010-12-12
      • 2011-04-13
      • 2010-09-23
      相关资源
      最近更新 更多