Tree structures in ASP.NET and SQL Server

Previewing Sub-levels

One other common feature of some web directories is to not only list the direct children of the node we're looking at - but also revealing the direct children of those nodes (ie two depths below the current node). Thanks to our lineage and depth columns, with a bit of thought it's easy to return a list of child nodes up to a particular depth.

CREATE PROCEDURE dfTreeGetSubChildren ( @id INT, @depth INT ) AS
SELECT t.* FROM dfTree AS children INNER JOIN dfTree AS actualNode
    ON children.lineage LIKE actualNode.lineage + '_%'
    WHERE actualNode.id = @id AND children.depth - actualNode.depth <= @depth
    ORDER BY children.lineage, children.name

The inner join condition (ON children.lineage LIKE actualNode.lineage + '_%') ensures that any nodes we are returning is a child of the actualNode. The underscore (which matches at least one character) ensures that we don't return the node itself. The WHERE clause specifies that our actualNode should match the @id parameter we were passed, and the difference between the depth of that node, and any node we return should be at most @depth.

In order to add this operation to our provider, we just need the following:

public ArrayList GetSubChildren(int uniqueID, int depth)
{
    return ProcessList("dfTreeGetSubChildren",
        new SqlParameter("@id",uniqueID),
        new SqlParameter("@depth",depth));
}

In our ASP.NET page, we modify the categoryList repeater so that it includes a call to a new function RepeatString that will repeat the string "--" according to the difference between the depth of the node we're displaying, and the actual node we're viewing (currentNode).

<asp:Repeater Runat="server" ID="categoryList">
    <ItemTemplate>
        <%# RepeatString("--",(int)DataBinder.Eval(Container.DataItem,"Depth") - currentNode.Depth - 1) %>
        <a href='<%# "TreeDemoC.aspx?id=" + DataBinder.Eval(Container.DataItem,"UniqueID") %>'
            ><%# DataBinder.Eval(Container.DataItem,"Name") %></a>
    </ItemTemplate>
    <SeparatorTemplate> <br /> </SeparatorTemplate>
</asp:Repeater>

The currentNode variable is declared as

protected TreeNode currentNode;

In the code-behind class. Until now we haven't actually needed a direct reference to the "current node" - but we can do this easily enough, as our call to GetPath already returns us the node we want - at the end of the list:

ArrayList pathList = treeProvider.GetPath(treeID);
// fetch the current node
currentNode = (TreeNode)pathList[pathList.Count - 1];
// continue the binding as we did before
breadcrumb.DataSource = pathList;
breadcrumb.DataBind();
// now bind the list (we need a ref to currentNode first)
categoryList.DataSource = treeProvider.GetSubChildren(treeID,2);
categoryList.DataBind();

After adding a few more rows to the database, your page should look something like this:

A diagram displaying the structure of the table, with these fields: id (int, primary key), parentId (int), name (varchar), depth (int), lineage (varchar).

This could easily be modified to display the entire tree below the current category by simply removing the depth restriction on the data we return. To do this, you can simply comment out the AND t.depth - m.depth <= @depth part of the dfTreeGetSubChildren stored procedure. Then you'd get something that looks like this:

A diagram displaying the structure of the table, with these fields: id (int, primary key), parentId (int), name (varchar), depth (int), lineage (varchar).

Cool, eh?

We've only scratched the surface here of the sorts of queries you can perform over the tree now that we've got the self-maintaining lineage and depth fields. I'm not sure how much interest there will be in this article - but if you find some other useful queries you'd like to share, then just let me know.

You might also like...

Comments

About the author

James Crowley

James Crowley United Kingdom

James first started this website when learning Visual Basic back in 1999 whilst studying his GCSEs. The site grew steadily over the years while being run as a hobby - to a regular monthly audien...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Weeks of coding can save you hours of planning.”