Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Populate TreeView and maintain its state

0.00/5 (No votes)
19 Dec 2012CPOL1 min read 14.4K   341  
The article shows how to populate a TreeView and maintain its state.

Introduction 

This article presents a simple way to populate a Web Form TreeView control and maintain its state using a recursive query, list of types, and Session object.

Background

A working knowledge of web development with ASP.NET, C#, and SQL Server is helpful to understand the code.

Using the code

I'm using SQL Server 2008 Express and Visual Web Developer 2010 Express.

First I created a database ItemsDb with a table 'items' holding hierarchical data and a stored procedure SP_ItemTreePopulate. The procedure uses a recursive query to select data from the 'item' table and sort it in hierarchical order:

SQL
with item_tree (item_id, parent_item_id, item_name, hierarchy_id)
    as
    (
    select item_id, parent_item_id, item_name,         
    REPLICATE('0',7-len(convert(varchar(50), item_id))) + convert(varchar(50), item_id) as hierarchy_id
    from dbo.items where parent_item_id is null
    UNION ALL           
    select i.item_id, i.parent_item_id, i.item_name, tv.hierarchy_id + 
    REPLICATE('0',7-len(convert(varchar(50), i.item_id))) + convert(varchar(50), i.item_id) 
    from dbo.items as i inner join item_tree as tv on i.parent_item_id = tv.item_id
    where i.parent_item_id is not null
    )
select * from item_tree order by hierarchy_id asc

When you download the source you can restore the database from the BAK file.

Then I created a new Web Site, added a TreeView control to the Default.aspx page, and wrote the following code in the Page Load event:

C#
protected void Page_Load(object sender, EventArgs e)
{
    DataTable table = null;

    MyTreeView.RootNodeStyle.BackColor = System.Drawing.Color.Transparent;
    MyTreeView.NodeStyle.BackColor = System.Drawing.Color.Transparent;
    MyTreeView.SelectedNodeStyle.BackColor = System.Drawing.Color.Yellow;

    MyTreeView.Nodes.Clear();

    TreeNode rootNode = new TreeNode("MyItems");
    rootNode.Value = "MyItems";
    MyTreeView.Nodes.Add(rootNode);        

    string spname = SPName.SP_ItemTreePopulate.ToString();
    try
    {
        table = SPManager.GetItemTree(spname);
    }
    catch
    {
        table = null;
    }
    //Class SPManager is kept in a DataProcessor namespace. Add reference to the namespace

    //in the code-behind file Default.aspx.cs

    if (table != null)
    {
        int rowCnt1 = table.Rows.Count;
        if (rowCnt1 > 0)
        {
            List<TreeNode> nodeList = new List<TreeNode>();
            for (int j = 0; j < rowCnt1; j++)
            {
                string item_id = table.Rows[j]["item_id"].ToString();
                string parent_item_id = table.Rows[j]["parent_item_id"].ToString();
                string item_name = table.Rows[j]["item_name"].ToString();

                TreeNode nodeparentitem = null;
                TreeNode nodeitem = null;
                if (parent_item_id == "")
                {
                    nodeparentitem = new TreeNode("Item: " + item_name);
                    nodeparentitem.Value = item_id;
                    rootNode.ChildNodes.Add(nodeparentitem);
                    nodeList.Add(nodeparentitem);                        
                }
                else if (parent_item_id != "")
                {
                    nodeitem = new TreeNode("Item: " + item_name);
                    nodeitem.Value = item_id;

                    for (int i = 0; i < nodeList.Count; i++)
                    {
                        if (nodeList[i].Value == parent_item_id)
                        {
                            nodeList[i].ChildNodes.Add(nodeitem);
                            nodeList.Add(nodeitem);
                        }                           
                    }
                }
            }
        }
    }
    MyTreeView.Nodes[0].Expand();
}

The code loops through the query result, creates nodes, and adds them to List<TreeNode>. When creating a child node, the code searches List<TreeNode> for an appropriate parent because at the current moment the parent is already in the list. When you run the page you see that TreeView is populated correctly but on every postback it returns to its original state.

To resolve this problem I used the Session object.

I added the following code to the Page Load event (in bold):

C#
protected void Page_Load(object sender, EventArgs e)
{
    DataTable table = null;

    MyTreeView.RootNodeStyle.BackColor = System.Drawing.Color.Transparent;
    MyTreeView.NodeStyle.BackColor = System.Drawing.Color.Transparent;
    MyTreeView.SelectedNodeStyle.BackColor = System.Drawing.Color.Yellow;

    MyTreeView.Nodes.Clear();

    TreeNode rootNode = new TreeNode("MyItems");
    rootNode.Value = "MyItems";
    MyTreeView.Nodes.Add(rootNode);

    string spname = SPName.SP_ItemTreePopulate.ToString();
    try
    {
        table = SPManager.GetItemTree(spname);
    }
    catch
    {
        table = null;
    }

    if (table != null)
    {
        int rowCnt1 = table.Rows.Count;
        if (rowCnt1 > 0)
        {
            List<TreeNode> nodeList = new List<TreeNode>();
            for (int j = 0; j < rowCnt1; j++)
            {
                string item_id = table.Rows[j]["item_id"].ToString();
                string parent_item_id = table.Rows[j]["parent_item_id"].ToString();
                string item_name = table.Rows[j]["item_name"].ToString();

                TreeNode nodeparentitem = null;
                TreeNode nodeitem = null;
                if (parent_item_id == "")
                {
                    nodeparentitem = new TreeNode("Item: " + item_name);
                    nodeparentitem.Value = item_id;
                    rootNode.ChildNodes.Add(nodeparentitem);
                    nodeList.Add(nodeparentitem);

                    
                    string itemExp = nodeparentitem.Text + nodeparentitem.Value;
                    if (Session[itemExp] != null)
                    {
                        if ((string)(Session[itemExp]) == "expended")
                            nodeparentitem.Expanded = true;
                        else if ((string)(Session[itemExp]) == "collapsed")
                            nodeparentitem.Expanded = false;
                    }

                    string itemColor = nodeparentitem.Text + "--" + nodeparentitem.Value;
                    if (Session[itemColor] != null)
                    {
                        if ((string)(Session[itemColor]) == "selected")
                        {
                            nodeparentitem.Select();
                        }
                    }
                    
                }
                else if (parent_item_id != "")
                {
                    nodeitem = new TreeNode("Item: " + item_name);
                    nodeitem.Value = item_id;

                    for (int i = 0; i < nodeList.Count; i++)
                    {
                        if (nodeList[i].Value == parent_item_id)
                        {
                            nodeList[i].ChildNodes.Add(nodeitem);
                            nodeList.Add(nodeitem);
                        }
                    }
                    
                    string itemExp = nodeitem.Text + nodeitem.Value;
                    if (Session[itemExp] != null)
                    {
                        if ((string)(Session[itemExp]) == "expended")
                            nodeitem.Expanded = true;
                        else if ((string)(Session[itemExp]) == "collapsed")
                            nodeitem.Expanded = false;
                    }

                    string itemColor = nodeitem.Text + "--" + nodeitem.Value;
                    if (Session[itemColor] != null)
                    {
                        if ((string)(Session[itemColor]) == "selected")
                        {
                            nodeitem.Select();
                        }
                    }
                    
                }
            }
        }
    }
    MyTreeView.Nodes[0].Expand();
}

Then I added three more event handlers to the TreeView events:

C#
protected void MyTreeView_SelectedNodeChanged(object sender, EventArgs e)
{
    string nodeVal = "";
    string nodeText = "";

    nodeVal = MyTreeView.SelectedNode.Value;
    nodeText = MyTreeView.SelectedNode.Text;

    for (int k = 0; k < Session.Keys.Count; k++)
    {
        if (Session.Keys[k].IndexOf("--") != -1)
        {
            Session[Session.Keys[k]] = null;
        }
    }

    if (!MyTreeView.Nodes[0].Selected)
        MyTreeView.RootNodeStyle.BackColor = System.Drawing.Color.Transparent;

    string colSess = nodeText + "--" + nodeVal;
    Session[colSess] = "selected"; 
}

protected void MyTreeView_NodeExpand(object sender, TreeNodeEventArgs e)
{
    string nodeTxt = e.Node.Text;
    string nodeVal = e.Node.Value;
    string expSess = nodeTxt + nodeVal;
    Session[expSess] = "expended";
}
protected void MyTreeView_NodeCollapse(object sender, TreeNodeEventArgs e)
{
    string nodeTxt = e.Node.Text;
    string nodeVal = e.Node.Value;
    string expSess = nodeTxt + nodeVal;
    Session[expSess] = "collapsed";
}

In these events I save Selected, Expanded, and Collapsed nodes in the Session object and restore them when the TreeView is being recreated.

And do not forget to add:

onselectednodechanged="MyTreeView_SelectedNodeChanged"
OnTreeNodeExpanded="MyTreeView_NodeExpand"
OnTreeNodeCollapsed="MyTreeView_NodeCollapse"

to the TreeView control in the Default.aspx page.

Now running the page you see that the TreeView maintains its state.

History

  • December 15th, 2012: Initial post.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)