Question

I'm developing a set of custom controls, one of them are the "parent", any others must be added inside an ITemplate property of this parent.

But in the page, at design time, I can see, by intellisense, the other controls at page level, and I can theorically add them everywhere.

I want to emulate the behaviour of the asp:Table and the asp:TableRow, you can't directly add an asp:TableRow outside an asp:Table...

Is there a way to reach this? Many thanks!

edit: I've partially solved with the KP suggestion, but if you read at the comment it's not the "real" way to do this (I think). No one knows how to do that? :(

Was it helpful?

Solution

I've edited the entire answer based on our discussion. Here's a working and tested example. We have below two controls - ParentControl and ChildControl. ParentControl is visible via Intellisense, where ChildControl is only visible as a child of ParentControl as you wanted. For simple rendering purposes, the children render as li tags and output their 'text' property. The parent control ensures each child is asked to render during its own RenderContents event.

Child Control:

using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace TestControls
{
    [ToolboxItem(false), Bindable(false)]
    public class ChildControl : WebControl
    {
        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            base.Render(writer);

            //render the text property as a list item for example's sake
            writer.RenderBeginTag(HtmlTextWriterTag.Li);
            writer.Write(this.Text);
            writer.RenderEndTag();
        }

        [Browsable(true)]
        public string Text { get; set; }
    }
}

Parent Control:

using System.Collections.Generic;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace TestControls
{
    [ToolboxData("<{0}:ParentControl runat=\"server\"></{0}:ParentControl>")]
    [DefaultProperty("Children"), ParseChildren(true, "Children")]
    public class ParentControl : WebControl
    {
        private List<ChildControl> _children;

        protected override void RenderContents(HtmlTextWriter writer)
        {
            base.RenderContents(writer);

            //create a div, and write some sample text
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            writer.Write("Parent Control. Children:");

            //create a ul, and ask each child control to render
            writer.RenderBeginTag(HtmlTextWriterTag.Ul);

            foreach (ChildControl child in _children)
            {
                child.RenderControl(writer);
            }

            //close all tags
            writer.RenderEndTag();
            writer.RenderEndTag();
        }

        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public virtual List<ChildControl> Children
        {
            get
            {
                if (_children == null)
                    _children = new List<ChildControl>();

                return _children;
            }
        }
    }
}

In my markup, I registered the controls via namespace:

<%@ Register TagPrefix="test" Namespace="TestControls" %>

And then added some markup:

<test:ParentControl ID="test" runat="server">
    <test:ChildControl ID="child" Text="Hello World from Child 1" runat="server" />
    <test:ChildControl ID="child2" Text="Hello World from Child 2" runat="server" />
</test:ParentControl>

In the above markup, Intellisense picks up on the outer parent control, but does not see the child control. Once the cursor is inside the parent control, Intellisense picks up on the ChildControl tag as desired.

The final output is:

Parent Control. Children:

* Hello World from Child 1
* Hello World from Child 2

Also , here's a good article on how the whole intellisence creation works, which I followed to create the above.

I hope this helps. You'd still have to deal with rendering at the child control level in the way you see fit for your specific controls, however the above gets you started and does meet the need of a working Intellisense model.

OTHER TIPS

I've done this before but I don't have the code available. I can tell you that I figured it out by using Reflector on the built-in ASP.NET Datagrid control. I was able to reverse engineer the relationship between the "contained" ("row") and "container" ("grid"). You have to arrange the classes together in a very specific way using attributes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top