Есть ли способ поместить внутренние элементы управления в пользовательский элемент управления ASP.NET?

Я хочу сделать что-то вроде (обновленный пример):

<uc:Tabs>
  <Tab Name="A handy tab">
    <Node Url="~/Default.aspx" />
    <Node Url="~/Node2.aspx" />
  </Tab>      
  <Tab Name="Another handy tab">
    <Node Url="~/Neato.aspx" />
    <Node Url="~/Node3.aspx" />
    <Node Url="~/Node4.aspx" />
  </Tab>
<uc:Tabs>

Возможный? Любые учебники или инструкции? Я не уверен, что даже искать или как это называется, поэтому пока ничего не нашел. Внутренний контроль? Внутренняя коллекция что-то что-то...?


person rball    schedule 03.03.2009    source источник


Ответы (2)


Используйте ParseChildrenAttribute и PersistChildrenAttribute:

[ParseChildren(false)]
[PersistChildren(true)]
public class MyControl : UserControl { }

Это приведет к тому, что любые элементы управления, которые вы поместите внутри ссылки:

<uc:MyControl runat="server">
  <asp:TextBox runat="server" />
<uc:MyControl>

Добавляется в конец коллекции Controls содержимого UserControl.

Однако, если вы хотите иметь набор элементов управления, вам, вероятно, следует использовать серверный элемент управления, а не пользовательский элемент управления. Для элемента управления, который работает следующим образом:

<foo:TabControl runat="server">
    <Tabs>
        <foo:Tab CssClass="myclass" Title="Hello World" />
    </Tabs>
</foo:TabControl>

Вам нужен класс Control со свойством Tabs; свойство Tabs должно быть коллекцией; и он должен содержать объекты типа Tab. Я создал три класса здесь:

[ParseChildren(true, "Tabs")]
public class TabControl: WebControl, INamingContainer
{
    private TabCollection _tabs;

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
    public TabCollection Tabs
    {
        get
        {
            if (_tabs == null)
            {
                _tabs = new TabCollection();
            }
            return _tabs;
        }
    }

    protected override void Render(HtmlTextWriter writer)
    {
        foreach (Tab tab in Tabs)
        {
            writer.WriteBeginTag("div");
            writer.WriteAttribute("class", tab.CssClass);
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.Write("this is a tab called " + tab.Title);
            writer.WriteEndTag("div");
        }
    }
}

И класс вкладки:

public class Tab
{
    public string CssClass { get; set; }
    public string Title { get; set; }
}

И коллекция вкладок:

public class TabCollection : Collection<Tab> { }
person Rex M    schedule 03.03.2009
comment
Я попробую. Сейчас я использую пользовательский (серверный?) элемент управления; Я думаю, что UC мог заставить вас поверить, что это пользовательский элемент управления. - person rball; 03.03.2009

Продолжение

Рекс М предоставил ответ, но просто хотел продолжить то, что я также нашел для потомков.

Кажется, вы можете сделать либо:

<ui:Tabs runat="server">
    <ui:Tab TabText="Blah">
        <ui:Node Url="~/Default.aspx" />
        <ui:Node Url="~/More.aspx" />
        <ui:Node Url="~/Another.aspx" />
    </ui:Tab>
    <ui:Tab TabText="Nanner">
        <ui:Node Url="~/Default.aspx" />
        <ui:Node Url="~/More.aspx" />
        <ui:Node Url="~/Another.aspx" />
    </ui:Tab>
    <ui:Tab TabText="High There">
        <ui:Node Url="~/Default.aspx" />
        <ui:Node Url="~/More.aspx" />
        <ui:Node Url="~/Another.aspx" />
    </ui:Tab>
</ui:Tabs>

OR

<ui:Tabs runat="server">
    <TabItems>
        <ui:Tab InnerHeading="Big Huge Heading" TabText="Big">
            <NodeItems>
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
            </NodeItems>
        </ui:Tab>
    </TabItems>
    <TabItems>
        <ui:Tab InnerHeading="Hi ya" TabText="Hi">
            <NodeItems>
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
                <ui:Node Url="~/Default.aspx" />
            </NodeItems>
        </ui:Tab>
    </TabItems>
</ui:Tabs>

Затем в коде:

namespace Controls
{
    [ToolboxData("<{0}:Tabs runat=server></{0}:Tabs>"), ParseChildren(true, "TabItems")]
    public class Tabs : BaseControl, INamingContainer
    {
        private TabCollection tabItems;
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public TabCollection TabItems
        {
            get
            {
                if (tabItems == null) 
                { 
                    tabItems = new TabCollection(); 
                } 
                return tabItems;
            }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            writer.Write("<div id=\"tabs\" class=\"pane\" style=\"display: none\"><ul>");
            int tabNumber = 1;
            foreach (Tab tab in TabItems)
            {
                string li = string.Format("<li id=\"tab_{0}\"><a href=\"#tab{0}\"><span>{1}</span></a></li>", tabNumber, tab.TabText);
                tabNumber++;
                writer.Write(li);
            }
            writer.Write("</ul>");
            tabNumber = 1;
            foreach (Tab tab in TabItems)
            {
                string div = string.Format("<div id=\"tab{0}\" class=\"pane\"><h1>{1}</h1>", tabNumber, tab.InnerHeading);
                tabNumber++;
                writer.Write(div);
                foreach (Node node in tab.NodeItems)
                {
                    string a = string.Format("<a href='{0}'>{1}</a>", node.Url, "Text holder");
                    writer.Write(a);
                }
                writer.Write("</div>");
            }
            writer.Write("</div>");
        }
    }

    public class TabCollection : List<Tab> { }

    [ParseChildren(true, "NodeItems")]
    public class Tab
    {
        private string tabText = string.Empty;
        private string innerHeading = string.Empty;
        private string showOn = string.Empty;
        public string TabText
        {
            get { return tabText; }
            set { tabText = value; }
        }
        public string InnerHeading
        {
            get { return innerHeading; }
            set { innerHeading = value; }
        }

        public string ShowOn
        {
            get { return showOn; }
            set { showOn = value; }
        }

        private NodeCollection nodeItems;
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public NodeCollection NodeItems
        {
            get
            {
                if (nodeItems == null)
                {
                    nodeItems = new NodeCollection();
                }
                return nodeItems;
            }
        }
    }

    public class NodeCollection : List<Node> { }

    public class Node
    {
        private string url = string.Empty;
        public string Url
        {
            get { return url; }
            set { url = value; }
        }
    }
}

Это, очевидно, будет меняться (моя будет читать из web.sitemap среди других изменений), но это должно помочь всем, у кого такие же потребности.

person rball    schedule 03.03.2009