views:

617

answers:

3

I am trying to add a template to a simplified composite control containing a Label and a TextBox. I want my mark up to look something like this:

<test:FormItem ID="fi" runat="server" Title="MyTitle" Text="My Text!">
    <TestTemplate>
        <i>
            <%# Container.Title) %></i>
        <br />
        <%# Container.Text %>
    </TestTemplate>
</test:FormItem>

I have a templateContainer class that has properties for the TextBox and Label.

public class TemplateContainer : WebControl, INamingContainer
{

    public TextBox Text { get { return m_item.Text; } }
    public Label Title { get { return m_item.Title; } }

    private FormItem m_item;

    public TemplateContainer(FormItem item)
    {
        m_item = item;
    }

}

In the main FormItem class I have a CreateControlHierarchy() method that is being called from CreateChildControls():

protected virtual void CreateControlHierarchy()
    {

        m_itemTemplateContainer = new TemplateContainer(this);
        TestTemplate.InstantiateIn(m_itemTemplateContainer);

        Controls.Add(m_itemTemplateContainer);

    }

What I WANT is for the Template to render the actual control. Instead, it's calling ToString() on the control and displaying System.Web.UI.WebControls.Label and System.Web.UI.WebControls.TextBox. Is there a way to make the template add the controls to it's collection instead of just calling ToString() on them?

Note: I've also tried adding the textbox and label to the controls collection of the container which does the same thing.

A: 

You cannot render using binding expressions. Instead, you'd better to create an ITemplate implementation which will add required controls to a container.

amartynov
is there another way, in mark up, to have the template add controls? there must be a way since asp .net internally does it.
bmwbzz
To be honest, I have no idea where asp.net does it internally. Anyway, you may use the Reflector (http://www.red-gate.com/products/reflector/) to examine their code :)
amartynov
+1  A: 

Ok. So I tried a few things and I came up with an OK solution.

First, I tried to use methods in the data binding expression and then keep track of where in the container's Control collection the textbox or label would go. However, the CompiledTemplateBuilder (which is what .Net internally builds for ITemplates specified in mark up) put all of the markup before and after both binding expressions into one DataBoundLiteral control and the Control collection was already built when the method was called.

What did work was to create a new WebControl which serves as a place holder for the controls within the composite control. It has one property Control and when set, it add the control to it's Controls Collection.

public class FormItemPlaceHolder : WebControl, INamingContainer
{
    public WebControl Control
    {
        get
        {
            if(Controls.Count == 0)
                return null;
            return Controls[0] as WebControl;
        }
        set
        {
            if (Controls.Count != 0)
                Controls.Clear();
            Controls.Add(value);
        }
    }

}

Then in the mark up, I create a control of this type and bind it's Control property to the correct property in the container.

<test:FormItem ID="fi" runat="server" Title="MyTitle" Text="My Text!">
    <TestTemplate>
        <i>
        <test:FormItemPlaceHolder ID="ph" runat="server" 
            Control='<%# Container.Title %>' />
        </i>
        <br />
         <test:FormItemPlaceHolder ID="ph2" runat="server" 
             Control='<%# Container.Text %>' />
    </TestTemplate>
</test:FormItem>

Does anyone have a better solution?

bmwbzz
A: 

The container should not define the controls, just the data.

It is in the markup that you should define the actual controls of the data, and assign them the values in from the container.

E.g.

public class TemplateContainer : UserControl
{
    public string Text { get { return m_text; } }
    public string Title { get { return m_title; } }

    private string m_text;
    private string m_title;
    private FormItem m_item;

    public TemplateContainer(FormItem item)
    {
        m_item = item;
    }

}

And in the markup:

   <test:FormItem ID="fi" runat="server" Title="MyTitle" Text="My Text!">
        <TestTemplate>
            <i><asp:Label runat="server" Text='<%# Container.Title) %>' /></i>
            <br />
            <asp:TextBox runat="server" Text='<%# Container.Text %>' />
    </TestTemplate>
</test:FormItem>

If you are trying to create a composite control that does not require controls to be added in the markup, then why are you using a Template? If it is just for styling then perhaps creating your own Style object may be more effective?

codemonkeh
I want to create a Composite Control (control of controls) and allow the user to add markup around the controls. Note: I do not want to create a UserControl. I have custom logic for the controls in the Composite Control and I want it used across applications.
bmwbzz
Do you users have to be able to move the actual controls themselves, or simply be able to place markup before and after them?
codemonkeh