Display Hierarchical Data with TreeView in ASP.NET 2.0
Posted by ramanisandeep on April 18, 2009
I’m going to provide a simple example how to display hierarchical data from SQL Server database in the TreeView. A requirement is that the implementation should not be dependant on the hierarchy level in the database. It means that the TreeView implementation should be capable of displaying data from any level, no matter how deep.
Listing #1 : TreeviewEx.aspx
<asp:treeview id="tvCategoryList" runat="server" imageset="BulletedList3" width="100%"
showexpandcollapse="True" nodewrap="True" ontreenodepopulate="tvCategoryList_TreeNodePopulate"
expanddepth="2" populatenodesfromclient="False">
<ParentNodeStyle Font-Bold="True" />
<RootNodeStyle Font-Bold="True" CssClass="Table_Title_Label_Black12" />
<HoverNodeStyle Font-Underline="True" ForeColor="#5555DD" />
<SelectedNodeStyle Font-Underline="True" HorizontalPadding="0px" VerticalPadding="0px" ForeColor="#5555DD" />
<NodeStyle Font-Names="Verdana" Font-Size="8pt" ForeColor="Black" HorizontalPadding="0px" NodeSpacing="0px" VerticalPadding="0px" />
</asp:treeview>
Next, we start looking at the code in the TreeviewEx.aspx,cs file. We want to populate the root level. Here’s the code.
Listing #2
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
PopulateRootLevel();
}
}
This happens by connecting to the database, querying the first set of nodes (having null as the parent id), and creating TreeNode objects with the PopulateNodes routine, which follows next.
Listing #3
private void PopulateRootLevel()
{
DataTable dt = BAL_Category.GetParentCategory();// Get Parent CategoryList From Database
PopulateNodes(dt, tvCategoryList.Nodes);
}
Next, we want to create the routine to populate the child nodes of a given node. This happens with the PopulateSubLevel method.
Listing #4
private void PopulateSubLevel(int parentid, TreeNode parentNode)
{
// Get CategoryList by ParentID From Database
DataTable dt = BAL_Category.GetChildCategory(parentid);
PopulateNodes(dt, parentNode.ChildNodes);
}
Here, the idea is the same as with the root level, but with the distinction that only child nodes of the given node are queried and populated with the PopulateNodes method (described earlier). The trick to triggering the populating of the child nodes is as follows.
Listing #5
protected void tvCategoryList_TreeNodePopulate(object sender, TreeNodeEventArgs e)
{
PopulateSubLevel(Int32.Parse(e.Node.Value), e.Node);
}
TreeNodePopulate is raised for a TreeNode which is expanded by the user for the first time. Due to PopulateNodesFromClient (TreeView) and PopulateOnDemand (TreeNode) settings, this happens with the client-side callback mechanism which is handled by the ASP.NET Page framework. This means that populating does not involve a postback. And, due to better cross-browser support in ASP.NET 2.0, this also works for other browsers such as Firefox. Note that clicking on the node does cause a postback because I haven’t modified the select action of the populated tree nodes from the defaults.
Listing #6
private void PopulateNodes(DataTable dt, TreeNodeCollection nodes)
{
foreach (DataRow dr in dt.Rows)
{
TreeNode tn = new TreeNode();
tn.Text = dr["Category"].ToString();
tn.Value = dr["CategoryID"].ToString();
int NoofChild = Convert.ToInt32(dr["childnodecount"].ToString());
if (NoofChild != 0 )
{
tn.PopulateOnDemand = true;
tn.SelectAction = TreeNodeSelectAction.None;
}
else
{
tn.PopulateOnDemand = false;
if (dr["TotalDoc"].ToString().Equals("0") == false)
{
tn.NavigateUrl = "~/CategoryWiseProductList.aspx?ID=" + dr["CategoryID"].ToString();
tn.Text = tn.Text + " (" + dr["TotalProduct"].ToString() + ")";
}
else
{
tn.NavigateUrl = "javascript:alert('Product is not Available Under this Category.')";
}
}
nodes.Add(tn);
}
}
Hope this will Help you !!!
Aneesh said
hi , can you post the sample code here please?
ramanisandeep said
hi aneesh, i have already explained the example with code & i have also stated all the things that are required but still if u need any clarification than please specify at which area you got problem so that i can give solution regarding that.
Aneesh said
Thanks Sandeep.
Let me explain you mu issue. I have a project where users can subscribe to specific subjects so that the application can send newsletters to them on that specific subject.
So I have to make an userinterface as simple as possible but displaying all the categories and its n level subcategories.
After some R&D i believe Tree view is the only way ahead.[Please guide me If i am wrong].
I asked you the entire sample because
1) I wanted to know how the styles and skins will work Treeview which needed the CSS you have used.
2) I wantd to know the definition for
BAL_Category.GetChildCategory(parentid); and
BAL_Category.GetChildCategory(parentid);
since I would like to see the SQL queries used .
3) I am interested to see how the database table is designed and how the relation is set.
Thanks for your Time!
ramanisandeep said
Hi Aneesh ,
1 ) Css :
.Table_Title_Label_Black12
{
font-size: 10pt;
font-family: ‘Verdana’;
color: Black;
font-weight: bold;
text-decoration: none;
}
2 )
BAL_LegalDocList.GetParentCategory() & BAL_Category.GetChildCategory(parentid)
——————————————————————————-
using System;
using System.Data.Common;
using System.Data;
public class BAL_LegalDocList
{
public BAL_LegalDocList()
{
}
public static DataTable GetParentCategory()
{
// get a configured DbCommand object
GenericDataAccess gda = new GenericDataAccess();
DbCommand comm = gda.CreateCommand();
// set the stored procedure name
comm.CommandText = “GetParentCategory”;
// return the result table
DataTable table = gda.ExecuteSelectCommand(comm);
return table;
}
public static DataTable GetChildCategory(int ParentID)
{
// get a configured DbCommand object
GenericDataAccess gda = new GenericDataAccess();
DbCommand comm = gda.CreateCommand();
// set the stored procedure name
comm.CommandText = “GetChildCategory”;
// create a new parameter
DbParameter param = comm.CreateParameter();
param.ParameterName = “@ParentID”;
param.Value = ParentID;
param.DbType = DbType.Int32;
comm.Parameters.Add(param);
// return the result table
DataTable table = gda.ExecuteSelectCommand(comm);
return table;
}
}
3 ) Database :
———————-
Stored Procedure 1:
——————-
CREATE PROCEDURE [dbo].[GetParentCategory]
AS
BEGIN
SELECT
CategoryID,
Category,
(SELECT count(*) FROM Category WHERE ParentID=C.CategoryID AND SoftStatus=1) as childnodecount ,
(SELECT count(*) FROM LegalDocument WHERE CategoryID = C.CategoryID AND Status=0) as TotalDoc
FROM
Category C
WHERE
ParentID =0 AND
SoftStatus = 1
ORDER BY Category
END
Stored Procedure 2:
——————–
CREATE PROCEDURE [dbo].[GetChildCategory]
@ParentID int
AS
BEGIN
SELECT
CategoryID,
Category,
(SELECT count(*) FROM Category WHERE ParentID=C.CategoryID AND SoftStatus=1) as childnodecount ,
(SELECT count(*) FROM LegalDocument WHERE CategoryID = C.CategoryID AND Status=0) as TotalDoc
FROM
Category C
WHERE
ParentID = @ParentID AND
SoftStatus=1
ORDER BY Category
END
Table :
————
CREATE TABLE [dbo].[Category](
[CategoryID] [int] IDENTITY(1,1) NOT NULL,
[Category] [nvarchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[ParentID] [int] NULL,
[SoftStatus] [bit] NULL,
CONSTRAINT [PK_Category_1] PRIMARY KEY CLUSTERED
(
[CategoryID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
Hope this will help you
Aneesh said
Hi , Sandeep
Thank you very much for your help.
I really appreciate your gesture and the time you spent to do this.
Thank you very much !
Aneesh said
Hi ,
I could successfully populate treeview from database. i have added checkboxes to all the nodes so that user can select and unselect.Those values is updated on the DB , using
TreeNodeCollection checkedNodes = TreeView1.CheckedNodes;
This make sure the DB is updated with the values selected by users.
But when I repopulate the tree view for the second time how can we show the checkboxes with CHECKED state those have selected by the user already?
Aneesh said
Well , I could solve it.
The code used to given below.Hope this helps someone who reaches Mr.Sandeep`s post till this end.
We need to call this function twice.
First) after populating the Main categories Second) After populating the Sublevels.
private void DisplaySelected(TreeNodeCollection treeNodeCollection)
{
if (treeNodeCollection == null)
return;
foreach (TreeNode tn in treeNodeCollection)
{
// Assumes that you have the Userselection as a Arraylist here which was stored in a / //ViewState during PageLoad.
ArrayList categoryList=(ArrayList) ViewState["categories"];
if (categoryList.Contains(tn.Text))
{
tn.Checked=true;
}
}
}
THAnks again Sandeep…
ramanisandeep said
you are welcome !!!
wiki said
was very helpful!
Thanks!