I'm in the process of building a new menu editor for someone at work. I don't know if I've been at this too long or just can't see the forest for the trees....

The database structure's menu items like
id, title, parentid, listorder, link, etc

I'm trying to write this to the asp.net page like so:

<ul>
    <li>menu 1</li>
    <li>menu 2
        <ul>
             <li>submenu 1</li>
        </ul>
    </li>
</ul>

Of course, there may be any number of sub menus, sub submenus, etc...

If there was only 1 nested submenu it would be easy, but what is the best way to handle unlimited submenus and a simple code example of such?

FYI, I'm using VS 2008 targeting .Net 2.0

You may ask in ASP.NET forum, it'd be better.

This is the ASP.net forum?


And the best way to achieve this is definately a recursive function. I dont quite understand your database structure could you ellaborate. A simple example of something would be.

Database Table Structure

MenuItem
--ID
--Name
--ParentID
' MenuItem class, to hold an instance of an item
' Not the best of ideas to hold public instance variables but for this example it will suffice
Private Class MenuItem

    Public Name As String = Nothing

    Public ID As String = Nothing

    Public Sub New(ByVal name As String, ByVal id As String)
        Me.Name = name
        Me.ID = id
    End Sub

End Class

The above code is untested and leaves out your database requests to however you are implementing this. The only trouble with thei recursive method may be if you had say 50+ levels, you may find that requesting from the database for each node will slow you down. Another way to get around this would be to gather all the nodes at the begginning in a list then select them from this list at runtime using their ID's.

Hope it helps

Private Function OutputFiles(ByVal list As System.Collections.Generic.ArrayList(Of MenuItem)) As String
    Dim html As String = "<ul>"

    For Each item As MenuItem In list
        Dim sql As String = String.Format("SELECT ID, name FROM MenuItem WHERE parentID = '{0}'", item.ParentID)
        Dim innerList As System.Collections.Generic.ArrayList(Of MenuItem) ' Fire the above SQL statement and populate an ArrayList of type MenuItem using the returned ID's and Names

        html &= String.Format("<li>{0}{1}</li>", item.Name, OutputFiles(innerList)) ' Append the inner list to the current node, if there are no items in the list then the recursive function wont move any further.
    End For

    html &= "</ul>"
    Return html
End Sub
Comments
very nice help
great help, thanks

Thanks Fungus1487 (rep++).

I couldn't think of how to structure a recursive loop. I did manage to do the job with a loop structure, but this will definitely come in handy as I migrate from classic asp to asp.net.

I still find myself doing these procedurally instead of OOP, although I never lost any time in dropping inline asp tags... :)

you could achieve the same thing using a custom sitemap provider,
there is an article at MSDN that describes how to do this:

http://msdn.microsoft.com/en-us/magazine/cc163657.aspx

we use this extensively on our sites for menus, navigation etc. The benefit of this is that if you add the provider to your config file it gets automatically cached too and you dont need multiple hits to the database. The example uses a SQL server DB but you can easily rewrite it to use anything you want.

And the best way to achieve this is definitely a recursive function.

As a bit of an afterthought, the loop that "was" working broke once I started adding more menu items.

I came back and implemented this recursive function which required very little modification.

Basically it was System.Collections.Generic.List(Of MenuItem) as opposed to ArrayList.

As this function is only called when the menu is viewed in edit mode and is pulled via AJAX, I'm not too concerned about speed here.

even more +++ rep. Thanks again.

This article has been dead for over six months. Start a new discussion instead.