Search code examples
c#asp.netuser-controlssharepoint-2007

Recursive ASP user control


I am trying to make a user controls for multi-level menu. I have created first-level control which works. Its iterating with repeater and instantiating my own MenuButton class. Each MenuButton object has children of same type.

The question is: How to create MenuButton control inside MenuButton.aspx file?

I am using repeater like this

<%@ Control ClassName="MenuButton" Language="C#" AutoEventWireup="true" CodeBehind="MenuButton.ascx.cs"
    Inherits="MenuSolution._12.TEMPLATE.CONTROLTEMPLATES.MenuButton, MenuSolution, Version=1.0.0.0, Culture=neutral, PublicKeyToken=284eb573cd58385d" %>
<%@ Register TagPrefix="a" Namespace="MenuSolution._12.TEMPLATE.CONTROLTEMPLATES"
    Assembly="MenuSolution, Version=1.0.0.0, Culture=neutral, PublicKeyToken=284eb573cd58385d" %>
<li runat="server">
    <% if (Children.Count == 0)
       { %>
            <a href="<%# Url %>"><%# Description %></a>
    <% }
       else
       {
    %>
    <a href="<%# Url %>" class="dropdown-toggle" data-toggle="dropdown">
        <%# Description %><b class="caret"></b></a>
    <ul class="dropdown-menu multi-level">
        <asp:Repeater ID="repDynamicRows" runat="server">
            <ItemTemplate>
                <a:MenuButton runat="server" id="button" url='<%# DataBinder.Eval(Container.DataItem, "Url") %>'
                    children='<%# DataBinder.Eval(Container.DataItem, "ChildItems") %>' description='<%# DataBinder.Eval(Container.DataItem, "Description") %>' />
            </ItemTemplate>
        </asp:Repeater>
    </ul>
    <%
       }
    %>
</li>

and this code does not place MenuButton code inside final HTML. I was trying to register this control like:

<%@ Register TagPrefix="a" TagName="MenuButton" Src="~/_controltemplates/MenuButton.ascx" %>

But this leads to circural reference.

How should i do that?


Solution

  • You need to Load the control from code instead of the markup. That is due to how the asp.net compiler creates the assemblies from your mark up.

    I made the following changes to load the control from the codebehind. I had to change the databinding as well by changing the # to = (and have properties on the control).

    Control MenuButton.ascx

    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MenuButton.ascx.cs" Inherits="WebApplication1.MenuButton" %>
    <li id="Li1" runat="server">
        <% if (Children.Count == 0)
           { %>
                <a href="<%= Url %>"><%= Description %></a>
        <% }
           else
           {
        %>
        <a href="<%= Url %>" class="dropdown-toggle" data-toggle="dropdown">
            <%= Description %><b class="caret"></b></a>
        <!-- is now a server control -->
        <ul class="dropdown-menu multi-level" runat="server" ID="ul1">
           <!-- the page_load adds MenuButtons here -->       
        </ul>
    
        <%
           }
        %>
    </li>
    

    Codebehind MenuButton.ascx.cs

    public partial class MenuButton : System.Web.UI.UserControl
    {
        public String Url { get; set; }
        public string Description { get; set; }
        public List<MenuItem> Children { get; set; }
    
        protected void Page_Load(object sender, EventArgs e)
        {
            if (Children != null)
            {
                foreach (var menuItem in Children)
                {
                    // Create a button from the ascx file
                    var but = (MenuButton)LoadControl("/MenuItems/MenuButton.ascx");
                    // bind!
                    but.Children = menuItem.Children;
                    but.Description = menuItem.Description;
                    but.Url = menuItem.Url;
    
                    if (ul1 != null)
                    {
                        // add our button
                        ul1.Controls.Add(but);
                    }
                }
            }
        }
    }
    

    ViewModel

     public class MenuItem
     {
         public List<MenuItem> Children { get; set; }
         public String Url { get; set; }
         public string Description { get; set; }
     }