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 文档中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。

4

2 回答 2

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

在查看了MultiView控件的反编译源代码(类似)之后,我意识到我必须使代码更加复杂才能使其按需要工作。如果我什至可以使该控件正常工作,那么仅使用控件就可以使我获得的唯一好处MultiView是可以使用重复的 ID 。我决定最好只满足于使用内置控件。MultiView

于 2013-08-09T14:50:40.913 回答
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>");

        }
于 2014-08-11T13:00:58.973 回答