Populate a TreeView Control C#

Populating a TreeView control in a windows application using SQL and C# is quite straightforward.

Create a self referencing table, lets call it myTable, quite orginal don't you think? ;)

 
CREATE TABLE [dbo].[myTable](
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[title] [varchar](255) NOT NULL,
	[parentID] [int] NULL,
 CONSTRAINT [PK_myTable] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
 

Insert some hierarchal data.

 
SET IDENTITY_INSERT myTable ON
GO
 
INSERT INTO myTable(ID, title, parentID) VALUES(1,'Microsoft', NULL)
INSERT INTO myTable(ID, title, parentID) VALUES(2,'C#', 1)
INSERT INTO myTable(ID, title, parentID) VALUES(3,'VB.net', 1)
INSERT INTO myTable(ID, title, parentID) VALUES(4,'Open Source', NULL)
INSERT INTO myTable(ID, title, parentID) VALUES(5,'Python',	4)
INSERT INTO myTable(ID, title, parentID) VALUES(6,'Ruby', 4)
INSERT INTO myTable(ID, title, parentID) VALUES(7,'PHP', 4)
INSERT INTO myTable(ID, title, parentID) VALUES(8,'Perl', 4)
INSERT INTO myTable(ID, title, parentID) VALUES(9,'Java', 4)
INSERT INTO myTable(ID, title, parentID) VALUES(10,'LinQ', 2)
INSERT INTO myTable(ID, title, parentID) VALUES(11,'5.2', 7)
INSERT INTO myTable(ID, title, parentID) VALUES(12,'4.4', 7)
 
GO
SET IDENTITY_INSERT myTable OFF
GO
 

I assume in my code example that the root parent node equals 0, hence when returning results from SQL like in the stored procedure, I use the ISNULL method to return 0 from a NULL.

You can just define a root value as well...

 
CREATE PROCEDURE viewMyTable
AS
BEGIN
	SELECT ID, title, ISNULL(parentID, 0) AS parentID
	FROM myTable
END
 

Add a same table key contraint.

 
ALTER TABLE [dbo].[myTable]  WITH CHECK ADD  CONSTRAINT [FK_myTable_myTable] FOREIGN KEY([parentID])
REFERENCES [dbo].[myTable] ([ID])
GO
ALTER TABLE [dbo].[myTable] CHECK CONSTRAINT [FK_myTable_myTable]
 


  • In your windows application, drag and drop a treeview control onto your form.
  • Populate a DataTable with your stored procedure.
Pass your TreeNodeCollection, the parentID you wish to start with and DataTable to the following method: eg. PopulateTreeView(SomeTreeView.Nodes, 0, SomeDataTable);

 
protected void PopulateTreeView (TreeNodeCollection parentNode, int parentID, DataTable folders)
{   
    foreach (DataRow folder in folders.Rows)
    {
        if (Convert.ToInt32(folder["parentID"]) == parentID)
        {
            String key = folder["ID"].ToString();
            String text = folder["title"].ToString();
            TreeNodeCollection newParentNode = parentNode.Add(key, text).Nodes;
            PopulateTreeView(newParentNode, Convert.ToInt32(folder["ID"]), folders);                    
        }
    }
}
 

If everything went according to plan, you'll end up with something like this:


Post/View comments
 

Coding horrors: Apparently 2 == "2abc"

This one is for the PHP enthusiasts, most of you are hopefully probably aware of this, but if you're not (and for people that adhere to other perhaps more sane languages), the following condition will evaluate to true and output the text, "How dodgy is this?"

 
if (2 == "2abc") {
	echo "How dodgy is this?";
}
 


The reason for this can be found in the way that PHP handles conversion/casting of its types. PHP will automatically convert the string ("2abc") to an integer to allow comparison of the values (the joys of loosely typeness).

Which brings me to the next expression which gives us a clue of what's really happening.

 
$a = (int) "2abc"; 
echo $a;
 


The non numeric part of the variable will be stripped away, leaving us with an integer value (if the string begins with a numeric value that is) - personally I believe this is horrible functionality - a variable containing non numeric values must NOT be allowed to be converted to an integer in this manner, conversion must fail.

Getting back to our if condition at the top, PHP introduced "identity" operators into their codebase in version 4 (I believe), observe:

 
if (2 === "2abc") {
	echo "How dodgy is this?";
}
 


This condition will evaluate to false and won't output anything, the reason being that instead of converting the variables we're attempting to compare, it compares the inferred types of each variable and only then the actual content of the variables.

In my opinion this is more credible (robust/safe) behaviour for an equality operator, not to mention to all the other operators in PHP that work in the same fashion (which might be too strict for a lot of PHP devs, something more familiar to strongly typed languages like C#).

One could argue that since PHP is a loosely typed language, one should rather implement some sane behaviour in the way PHP handles certain conversions and still allow PHP to convert values automatically to make them compareable.

This is exactly how javascript (another loosely typed language) handles scenarios like this. The following condition will evaluate to false like expected.

 
if (2 == "2abc")
{
	alert('True');
}
 


Unlike the identity operators in PHP, the following will evaluate to true, but since type conversion seems to be more sane in javascript, it doesn't matter, they might not be equal in type, but they are definitely equal in value.

 
if (2 == "2")
{
	alert('True');
}
 

Post/View comments
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27