TheSwamp

Code Red => .NET => Topic started by: Keith™ on March 19, 2009, 10:24:51 AM

Title: Building a treeview with recursion
Post by: Keith™ on March 19, 2009, 10:24:51 AM
I'll admit that recursion has never been one of my strongest points, and I only use it except for the simplest of tasks. But I find myself once again in a situation that I "think" requires more than a case, while or for next loop.

I have a file (many actually) that must be read into an application and displayed on a treeview control, each line represents a single node and each line has a variable number of preceeding tabs that determine the heirarchy of the items in the file. Try as I might, I am not getting my head wrapped around this ... or maybe my last project fried my brain ... take your pick.

The file will look something like this:
Code: [Select]
Top Node1
        Child Node1_1
        Child Node1_2
                Child Node1_2_1
Top Node2
Top Node3
        Child Node3_1
        Child Node3_2
                Child Node3_2_1
                        Child Node3_2_1_1
                                Child Node3_2_1_1_1

... and I need it to look like this:

Title: Re: Building a treeview with recursion
Post by: Tuoni on March 19, 2009, 11:04:48 AM
I did something similar with recursion in PHP a couple or three years back... http://www.theswamp.org/~tuoni/FileManagement/ (sorry about the roughness, it was a WIP which never got even close to being finished :( )

I can post the code if you'd like, but like I say it's all in PHP
Title: Re: Building a treeview with recursion
Post by: Keith™ on March 19, 2009, 11:18:39 AM
I could translate the php to .net, but I think this is more about concept than actual code. I am having a tough time wrapping my mind around it, not so much coding it.

I guess, what I need to do is understand the requirements a little better.

Currently, I have the entire contents of the file in a single variable that I can split
i.e.
Code: [Select]
Dim objLines As Object = Split(strFilecontents, vbCrLf)

I can also create a global var to hold the index (which line we are reading) then compare the current line to the previous one to determine if it is indeed a child. The best way I can figure to do that is to split the line by tabs

Code: [Select]
Dim curNode As Object = Split(objLines(N),vbTab)
Dim prvNode As Object = Split(objLines(N - 1), vbTab)
If Ubound(curNode) > Ubound(prvNode) Then
IsChild = True
End If

Now if it is a child, call the recursion function with the current node as the parent to add to.

At least that seems to be the concept. If the current node isn't a child, it backs up the line to the previous parent... I think

I'd appreciate any insight
Title: Re: Building a treeview with recursion
Post by: T.Willey on March 19, 2009, 11:24:11 AM
Couldn't you count the tabs, and then get the ' Parent ' property of the node just added?  In your example
Top Node1 ( no tab, new main node. )
        Child Node1_1 ( one tab more than previous, so it is owned by previous )
        Child Node1_2 ( same amount of tabs, owned by previous parent )
                Child Node1_2_1 ( one more tab than previous, so it is owned by previous )
Top Node2 ( no tab, new main node. )
Top Node3 ( no tab, new main node. )
        Child Node3_1 ( one tab more than previous, so it is owned by previous )
        Child Node3_2 ( same amount of tabs, owned by previous parent )
                Child Node3_2_1 ( one tab more than previous, so it is owned by previous )
                        Child Node3_2_1_1 ( one tab more than previous, so it is owned by previous )
                                Child Node3_2_1_1_1 ( one tab more than previous, so it is owned by previous )
                        Child Node3_2_1_2 ( one tab less than previous, so it is owned by previous parent's parent )

There is also a ' PrevNode ' property that may be of help also.
Title: Re: Building a treeview with recursion
Post by: Tuoni on March 19, 2009, 11:27:12 AM
I could translate the php to .net, but I think this is more about concept than actual code. I am having a tough time wrapping my mind around it, not so much coding it.
Well that was kinda my thought.

So... here goes.  Please excuse dodgy code, like I say it was a WIP which never got finished

index.php
Code: [Select]
<?php

require "settings.inc.php";
require 
"filefunctions.inc.php";

$ParentArray = array();

if (!isset(
$_GET['CurrentFolder']) && !isset($_SESSION['CurrentFolder'])){
$_SESSION['CurrentFolder'] = $UserBaseDir;
} elseif (isset(
$_GET['id'])) {

$i 0;

$FolderId $_GET['id'];
$oldParentArray $_SESSION['parentArray'];
unset($_SESSION['parentArray']);
while ($oldParentArray[$i][3] != $FolderId && $i count($oldParentArray)){ $i++; }
$_SESSION['CurrentFolder'] = $oldParentArray[$i][1];
unset($oldParentArray);
}

if(isset(
$_GET['Reset']))
{
$_SESSION['CurrentFolder'] = $UserBaseDir;
header('location:./index.php');
}

$Folder $_SESSION['CurrentFolder'];

$found false;

$dir opendir($Folder);  //Open the resulting folder for inspection

while(false !== ($file readdir($dir))) //While there are still files in there...
{
    
$type filetype($Folder $file); //Get the file type (file/dir)
    
if($type == "dir" && $file != "." && $file != ".."//While it's a dir and isn't "." or ".."...
    
{
        
$found true//There are sub-directories, let's display one
        
$headhurts=TraverseToCurrent($Folder.$file."/");
    }
}
if(
$found == false)
{
    
$headhurts=TraverseToCurrent($Folder);
}

sort($ParentArray); //Make it alphabetical

$_SESSION['parentArray'] = $ParentArray//Assign ParentArray to a session so we can access it

printTree();
ListDirContents();


?>

settings.inc.php
Code: [Select]
<?php

session_start
(); //Sessions hold the current folder for updating columns, leave this value alone :)

// *** Edittable stuff starts ***

//Where are the files stored? (With trailing /)
$FilesDirectory "./files/";

// Do you want to show "." and ".." directories on file pane? (does not affect sub folders)
$listDirectories false// "true" or "false" with no quote marks

$ChMod="0777"//Permissions directories are created with

$username "tuoni"//Temp hard-value for username

// *** Edittable stuff stops ***

// *** Session setup stuff for current base directory ***

if (!isset($_SESSION['UserBaseDir']))
{
    
$_SESSION['UserBaseDir'] = $FilesDirectory.$username."/";
}
if (!isset(
$_SESSION['BaseDir']))
{
    
$_SESSION['BaseDir'] = $FilesDirectory;
}

$UserBaseDir $_SESSION['UserBaseDir'];
$BaseDir $_SESSION['BaseDir'];

if(
is_dir($UserBaseDir) == false)
{
    
mkdir($UserBaseDir$ChMod);

    
$indexFile fopen($UserBaseDir.'/index.html''w');
    
fwrite($indexFile"w00t w00t");
    
fclose($indexFile);
}

?>

fileFunctions.inc.php (where the recursion actually happens)
Code: [Select]
<?php

/*****************************************************************************************************************
* This is a set of functions which handle traversing to a folder given in $_GET['id'] and
* then print them out to the page.  It is designed to be included in another file as finished
* functions, rather than to be used on their own.  It requires that session values are set,
* so try not to use these functions unless you can understand what they do :)
*
* Author: Ed Geraghty
* Version: 2007-10-14
*****************************************************************************************************************/

//This is the function which does the physical traversing through the directory structure
Function traverseToCurrent($GivenFolder){

global $PushPopArray//Present the array in a way the recursive fn can use it
global $BaseDir;
global $ParentArray;

$found false;

$filepath=explode("/"$GivenFolder); //Split the string into an array
$poppedentity=array_pop($filepath); //Take off the last two elements
$poppedentity2=array_pop($filepath);

$subfilepath=implode("/",$filepath); //Put the string back together

$subfilepath.="/"//Add a trailing /

$dir opendir($subfilepath);  //Open the resulting folder for inspection

while(false !== ($file readdir($dir))) //While there are still files in there...
{
$type filetype($subfilepath $file); //Get the file type (file/dir)
if($type == "dir" && $file != "." && $file != ".." && $subfilepath != $BaseDir//While it's a dir and isn't "." or ".."... or the user's base dir
{
for($i 0$i < (count($ParentArray)); $i++) //Quick loop through the array to check this file isn't already listed
{
if (count($ParentArray) != 0//If there are entities in the array
{
if($ParentArray[$i][0] == $subfilepath && $ParentArray[$i][1] == $subfilepath.$file."/"//If it is already there
{
$found true//We found it, don't add it again
break; //Break out of the loop
//If
//If
//For
if (!$found || count($ParentArray) == 0//If we haven't found it or the array isn't populated
{
if (count($ParentArray) == 0//If the array isn't populated
{
$i 0//Set the index to the first record
//If
$ParentArray[$i][0] = $subfilepath//Set [0] to the parent folder
$ParentArray[$i][1] = $subfilepath.$file."/"//Set [1] to the current folder
$ParentArray[$i][2] = $file//Set [2] to what we want the folder name to appear as
$ParentArray[$i][3] = $i//Set [3] to a folder id - was originally folder name
//If
traverseToCurrent($subfilepath); //Otherwise recurse
//If
//While
//Function

//All recursion and no play mAkES ed a dull boy
//allRecRUSIon and no play makes ed a dull boy
//All recursion and No play mkaes Ed a dull hboy

//This function prints the directory tree out as a div to the page
Function printTree()
{

global $ParentArray;
global $UserBaseDir;

$PushPopArray = array();
$LevelsDeep=1;

array_push($PushPopArray$UserBaseDir);
$LevelsDeep++;

print "<div id='foldertree'>";

print "<li><a href='index.php?Reset=True'>Home</a></li>";

while (count($ParentArray) > 0)
{
$PreviousFolder array_pop($PushPopArray);
$LevelsDeep--;
$found false;
for ($i=0$i count($ParentArray); $i++)
{
if($PreviousFolder == $ParentArray[$i][0]) //If they share the same parent
{
$aHrefString=ltrim($ParentArray[$i][1],$UserBaseDir);
print "<li>";
for ($foo=0$foo < ($LevelsDeep); $foo++)
{
print "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
}
print "<a href='index.php?id=".$ParentArray[$i][3]."'>".$ParentArray[$i][2]."</a></li>";
array_push($PushPopArray,$PreviousFolder);
$LevelsDeep++;
array_push($PushPopArray,$ParentArray[$i][1]);
$LevelsDeep++;

array_splice($ParentArray$i1);
$found true;
break;
//If
//For
If ($found != true//If its not been found
{
array_push($PushPopArray,$PreviousFolder);
$tempvar array_pop($PushPopArray);
if($tempvar==$UserBaseDir)
{
$aHrefString=ltrim($ParentArray[0][1],$UserBaseDir);
array_push($PushPopArray,$PreviousFolder);
print "<li><a href='index.php?CurrentFolder=".$aHrefString."'>".$ParentArray[0][2]."</a></li>";
array_splice($ParentArray01);
}
//If
//While

print "</div>";
}

//Lists the contents of a directory in a div
Function ListDirContents()
{
$filesInDirectory = array();
$ListMeDirectory $_SESSION['CurrentFolder'];
$foo 0;

echo "<div id='foldercontents'>";
if(is_dir($ListMeDirectory)) //If directory exists, show its contents
{
echo "<table>
<tr>
<td width='25px'></td>
<td width='100%'><b><u>Name</u></b></td>
<td width='100px'><b><u>Type</u></b></td>
<td width='100px'><b><u>Size</u></b></td>
</tr>"
;
$dir opendir($ListMeDirectory);
while(false !== ($file readdir($dir)))
{
$type filetype($ListMeDirectory ."/"$file);
if($file != "." && $file != "..")
{
if ($type == "file")
{
echo "<tr>
<td><input type='checkbox' name='file|
$foo' />
<td><a href='index.php?action=showfile&id=
$foo'>" $file "</a></td>";
$filesInDirectory[$foo] = $file;
$foo++;
} elseif ($type == "dir") {
$folderId getIdForFolder($file);
echo "<tr>
<td><input type='checkbox' name='folder|
$folderId' />
<td><a href='index.php?id=
$folderId'>" $file "</a></td>";
}
echo "<td>" $type "</td>";
echo "<td>";
if($type == "file"){ echo filesize($ListMeDirectory ."/".$file); }
echo "</td></tr>";
//If
//While
closedir($dir);
echo "</table>";
echo "</div>";

$_SESSION['fileArray'] = $filesInDirectory;
//If
//Fn

//Retrieves the folder ID for a given foldername
Function getIdForFolder($folderName)
{
$ParentArray $_SESSION['parentArray'];
$i 0;

while ($ParentArray[$i][2] != $folderName){ $i++; }

return $ParentArray[$i][3];
}

?>

It was originally intended to be a file management script to use with the lilly pad here, it reads structure in a directory based on the user's login name...

It got abandoned when I found better, OSS, completed solutions.
Title: Re: Building a treeview with recursion
Post by: Keith™ on March 19, 2009, 11:31:34 AM
Couldn't you count the tabs, and then get the ' Parent ' property of the node just added?  In your example
Top Node1 ( no tab, new main node. )
        Child Node1_1 ( one tab more than previous, so it is owned by previous )
        Child Node1_2 ( same amount of tabs, owned by previous parent )
                Child Node1_2_1 ( one more tab than previous, so it is owned by previous )
Top Node2 ( no tab, new main node. )
Top Node3 ( no tab, new main node. )
        Child Node3_1 ( one tab more than previous, so it is owned by previous )
        Child Node3_2 ( same amount of tabs, owned by previous parent )
                Child Node3_2_1 ( one tab more than previous, so it is owned by previous )
                        Child Node3_2_1_1 ( one tab more than previous, so it is owned by previous )
                                Child Node3_2_1_1_1 ( one tab more than previous, so it is owned by previous )
                        Child Node3_2_1_2 ( one tab less than previous, so it is owned by previous parent's parent )

There is also a ' PrevNode ' property that may be of help also.

Essentially that is what I am trying to do, except if a node has less tabs than the previous child, it may not necessarily be a top level node. In that case it may be node.parent.parent.parent.parent adinfinitum ... and I don't see how coding that would be very efficient or easy to accomplish.

Tuoni, I'll look at that code in depth and see if I can garner what I need from it.

Thanks
Title: Re: Building a treeview with recursion
Post by: Tuoni on March 19, 2009, 11:40:49 AM
Tuoni, I'll look at that code in depth and see if I can garner what I need from it.
I hope it helps :)  Most of it is setting up sessions and variables to be honest.

Basically the way it works is with a stack, there are almost definitely better ways of doing it, but hopefully this will help you get your head around at least some of the logic.  Though as you'll probably work out from the comments, I ended up frying my own mind :)
Title: Re: Building a treeview with recursion
Post by: T.Willey on March 19, 2009, 11:45:41 AM
Couldn't you count the tabs, and then get the ' Parent ' property of the node just added?  In your example
Top Node1 ( no tab, new main node. )
        Child Node1_1 ( one tab more than previous, so it is owned by previous )
        Child Node1_2 ( same amount of tabs, owned by previous parent )
                Child Node1_2_1 ( one more tab than previous, so it is owned by previous )
Top Node2 ( no tab, new main node. )
Top Node3 ( no tab, new main node. )
        Child Node3_1 ( one tab more than previous, so it is owned by previous )
        Child Node3_2 ( same amount of tabs, owned by previous parent )
                Child Node3_2_1 ( one tab more than previous, so it is owned by previous )
                        Child Node3_2_1_1 ( one tab more than previous, so it is owned by previous )
                                Child Node3_2_1_1_1 ( one tab more than previous, so it is owned by previous )
                        Child Node3_2_1_2 ( one tab less than previous, so it is owned by previous parent's parent )

There is also a ' PrevNode ' property that may be of help also.

Essentially that is what I am trying to do, except if a node has less tabs than the previous child, it may not necessarily be a top level node. In that case it may be node.parent.parent.parent.parent adinfinitum ... and I don't see how coding that would be very efficient or easy to accomplish.

You would just need to setup a repeat for the amount of tabs less than.  I showed that on the one extra node I added ( the last node ).  You would repeat 1 ( since there is one less tab ) on the previous nodes parent.  That should work for what you are trying to do.
Title: Re: Building a treeview with recursion
Post by: Glenn R on March 19, 2009, 12:18:27 PM
Got a sample text file to help people think with?
Title: Re: Building a treeview with recursion
Post by: Keith™ on March 19, 2009, 01:52:32 PM
Attached is a sample ...
Title: Re: Building a treeview with recursion
Post by: John Kaul (Se7en) on March 19, 2009, 02:43:52 PM
I think a recursive procedure would be extremely expensive in this instance; use a loop.
Title: Re: Building a treeview with recursion
Post by: Keith™ on March 19, 2009, 02:47:26 PM
yeah I tried that too ... I just can't seem to loop my mind around it hahahahaha .. sometimes I crack me up

seriously ... I can't seem to get it ironed out ... and I am losing my hair fast enough as it is ...
Title: Re: Building a treeview with recursion
Post by: John Kaul (Se7en) on March 19, 2009, 02:58:05 PM
A recursive procedure is nothing more then an expensive loop. Sometimes its simpler to build so that is the trade off with using a recursive procedure (spend time coding or waste some time on execution).

I have some drafting to do so I can think a bit.
Title: Re: Building a treeview with recursion
Post by: John Kaul (Se7en) on March 19, 2009, 03:24:55 PM
so-far i cant think of anything better then parsing that string and counting tabs like T.Willey suggested. I mean that sounds like the most straight forward and efficient way.
Title: Re: Building a treeview with recursion
Post by: T.Willey on March 19, 2009, 03:38:18 PM
Here is proof of concept.  Real simple, but works here.

Code: [Select]
void MainFormLoad(object sender, EventArgs e)
{
TreeNode PrevNode = new TreeNode();
TreeNode CurNode;
int OldTabCnt = -1;
using ( StreamReader sr = new StreamReader( @"C:\MyCustom\material list.txt" ) ) {
while ( sr.Peek() != -1 ) {
string tempStr = sr.ReadLine();
if ( string.IsNullOrEmpty( tempStr ) )
continue;
int CurTabCnt = tempStr.LastIndexOf( '\t' );
string[] tempAr = tempStr.Split( '\t' );
CurNode = new TreeNode( tempAr[ tempAr.Length - 1 ] );
if ( CurTabCnt == -1 ) {
Tview.Nodes.Add( CurNode );
}
else if ( CurTabCnt == OldTabCnt ) {
PrevNode.Parent.Nodes.Add( CurNode );
}
else if ( CurTabCnt > OldTabCnt ) {
PrevNode.Nodes.Add( CurNode );
}
else if ( CurTabCnt < OldTabCnt ) {
for ( int i = 0; i <= OldTabCnt - CurTabCnt; ++i ) {
PrevNode = PrevNode.Parent;
}
PrevNode.Nodes.Add( CurNode );
}
OldTabCnt = CurTabCnt;
PrevNode = CurNode;
}
}
}
Title: Re: Building a treeview with recursion
Post by: Mark on March 19, 2009, 03:43:57 PM
I was thinking of using regex myself.
Title: Re: Building a treeview with recursion
Post by: Mark on March 19, 2009, 03:48:02 PM
I was thinking of using regex myself.
Never mind ...
Title: Re: Building a treeview with recursion
Post by: Keith™ on March 19, 2009, 04:26:55 PM
Tim I appreciate your time, I was able toactually understand what you were doing and believe it or not, I was pretty close already .. my incrementing wasn't done correctly and a couple of other minor things. Thanks for helping me get over the hump ...

Incidently, I decided to use the streamreader as opposed to reading the whole file up front ... this will prevent future problems should the files get very big.
Title: Re: Building a treeview with recursion
Post by: T.Willey on March 19, 2009, 04:28:49 PM
You're welcome Keith.  I haven't gotten a chance to think C# in a while, and it was fun to get my feet in again.
Title: Re: Building a treeview with recursion
Post by: Keith™ on March 19, 2009, 04:36:07 PM
Lately I have been thinking in all sorts of languages ... there hasn't been much to draw .. no houses are selling, so my boss has me automating much of the daily tasks around the office ... I've had to work in just about everything from html to C++ ... depending upon the needs at the time .. not much AutoCAD though ;)
Title: Re: Building a treeview with recursion
Post by: T.Willey on March 19, 2009, 04:48:48 PM
Sometimes I'm thankful I'm not in the private sector of drawings, so I've been pretty consistently busy, which is nice, but doesn't leave much time for programming, as I'm just a monkey.
Title: Re: Building a treeview with recursion
Post by: Maverick® on March 19, 2009, 04:49:25 PM
Lately I have been thinking in all sorts of languages ...

Klingon?
Title: Re: Building a treeview with recursion
Post by: Keith™ on March 19, 2009, 04:54:08 PM
Lately I have been thinking in all sorts of languages ...

Klingon?

Funny you should mention that .. I actually found out that I could study Klingon at the local university ... evidently there is a huge demand now for people who can speak it to deal with inmates at any one of the myriad of mental health facilities.

I did look into it ... but for me it would be a novelty ... so I didn't pursue it ;)
Title: Re: Building a treeview with recursion
Post by: tjr on March 19, 2009, 10:58:51 PM
Keith:

I did something like this a while ago as a test of sorts. This should get you started (just ignore the filehandler stuff, that was C++ code)

Code: [Select]
       private void btnLoad_Click(object sender, EventArgs e)
        {
            try
            {
                // Create an OpenFileDialogBox to select the text file to read.
                this.ofdFilePicker.InitialDirectory = Environment.CurrentDirectory;
                this.ofdFilePicker.Filter = "text files (*.txt)|*.txt";
                if (this.ofdFilePicker.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        // Create a new XML document.
                        XmlDocument doc = new XmlDocument();
                        // Read the contents of a file using the C++ file reader
                        // and load it into the XMLDoc.
                        string xmlDoc = fileHandler.readFile(this.ofdFilePicker.FileName);
                        doc.LoadXml(xmlDoc);
                        // Clear the treeview nodes and then start adding nodes too
                        // it from the file.
                        this.tvXMLViewer.Nodes.Clear();
                        TreeNode rootNode = this.tvXMLViewer.Nodes.Add(doc.DocumentElement.Name);
                        // Call our TreeClimber function to traverse the XML nodes and
                        // populate the TreeView
                        this.xmlTreeClimber(doc.DocumentElement, rootNode);
                        this.tvXMLViewer.ExpandAll();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Error: Could not generate a treeview from the specified XML document. Original error: " + ex.Message);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: Could not read XML file. Original error: " + ex.Message);
            }
        }

        private void xmlTreeClimber(System.Xml.XmlNode node, TreeNode treeNode)
        {
            // Process the child nodes if available.
            if (node.HasChildNodes)
            {
                // Loop through all the child nodes.
                XmlNodeList nodeList = node.ChildNodes;
                for (int count = 0; count <= nodeList.Count - 1; count++)
                {
                    XmlNode newXNode = node.ChildNodes[count];
                    treeNode.Nodes.Add(new TreeNode(newXNode.Name));
                    TreeNode newTNode = treeNode.Nodes[count];
                    // recursively call xmlTreeClimber.
                    xmlTreeClimber(newXNode, newTNode);
                }
            }
            else
            {
                // If no children use trimmed OuterXML.
                treeNode.Text = node.OuterXml.Trim();
            }
        }
        private void XmlTreeBuilder(TreeNodeCollection tnCollection)
        {
            // Loop through all nodes in the collection.
            foreach (TreeNode node in tnCollection)
            {
                // If the node has children recursivley call XmlTreeBuilder.
                if (node.Nodes.Count > 0)
                {
                    xmlWriter.WriteStartElement(node.Text);
                    XmlTreeBuilder(node.Nodes);
                    xmlWriter.WriteEndElement();
                }
                else
                {
                    // If no children just write the text.
                    xmlWriter.WriteString(node.Text);
                }
            }
        }
Title: Re: Building a treeview with recursion
Post by: MikeTuersley on March 20, 2009, 05:18:34 PM
You guys are sure going about this the long way. This is something relatively easy to do with only a minor amount of programming required. Here's how to approach it:

1. Use an xml file instead of a text file. Take an existing text file and convert it by adding the xml tags you want:
    <Level1>Floor System (main)</Level1>
   <Level2>Floor truss system</Level2>
      <etc.>
2. Use xsd.exe [in the Framework folders] and generate an xsd file from your xml file
3. Use xsd.exe again to generate a c# class from the new xsd file
4. Attach the class to your project
5. Now you can use serilization to open and save the data [assuming you are using base data types]
6. Once it is opened and populated your object, your tree code becomes a simple nested For loop structure:
    pseudo code:
   For each Level1 item
      For each level2 item
         for each levelX item
             
Title: Re: Building a treeview with recursion
Post by: TonyT on March 20, 2009, 10:28:38 PM
Very good Mike.  I see you've learned something.

XSD is not really needed in this case, unless the data being persisted is represented by classes in code, and there's additional information associated with each node.

You guys are sure going about this the long way. This is something relatively easy to do with only a minor amount of programming required. Here's how to approach it:

1. Use an xml file instead of a text file. Take an existing text file and convert it by adding the xml tags you want:
    <Level1>Floor System (main)</Level1>
   <Level2>Floor truss system</Level2>
      <etc.>
2. Use xsd.exe [in the Framework folders] and generate an xsd file from your xml file
3. Use xsd.exe again to generate a c# class from the new xsd file
4. Attach the class to your project
5. Now you can use serilization to open and save the data [assuming you are using base data types]
6. Once it is opened and populated your object, your tree code becomes a simple nested For loop structure:
    pseudo code:
   For each Level1 item
      For each level2 item
         for each levelX item
             
Title: Re: Building a treeview with recursion
Post by: MikeTuersley on March 21, 2009, 11:28:02 AM
Thanks, Tony. You'd be surprised at what I know :kewl:
Title: Re: Building a treeview with recursion
Post by: John Kaul (Se7en) on March 21, 2009, 02:49:41 PM
> I have a file (many actually)...


> 1. Use an xml file instead of a text file. Take an existing text file and convert it by adding the xml tags you want:




Title: Re: Building a treeview with recursion
Post by: MikeTuersley on March 21, 2009, 11:33:47 PM
Ok...so what are you trying to say???
Title: Re: Building a treeview with recursion
Post by: John Kaul (Se7en) on March 22, 2009, 01:47:53 AM
1. Whom, or what, will 'add the tags' to the 'many' text files (couldn't--wouldn't--that process put us back to square one)?

2. "Why" are there so many text files (program output, client data, etc.)?

3. Overhead for change in procedure, or conversions?

...

Wouldn't it be easier to model the application for the need not alter the resources for "the" application?
Title: Re: Building a treeview with recursion
Post by: MP on March 22, 2009, 02:10:09 AM
It's a good point John. I perused the thread but I don't see if Keith spec'd how the files are being generated or where they're coming from. Nonetheless, going the xml route is likely the best route, so regardless where they originate, the main proggy should expect xml.

In other words the files would have to be pre-processed (cleaned/converted to xml) if they originate from a 3rd party, formatted as proper xml if they are generated from a seperate module within the app greater.
Title: Re: Building a treeview with recursion
Post by: MikeTuersley on March 22, 2009, 09:55:23 AM
Exactly. The major point is how to build the treeview which is best accomplished by the route I've explained. Processing the text files, or changing them at output if it's Keith's program, then becomes the next step. We may be stepping backward but then moving forward with a stronger solution and a shift in the problem.

If it's Keith's program that outputs the information, then using the class object that xsd would spit out becomes a simple process - instantiate it, add the values where needed, then serialize it to the xml file. Again, there is no need to write custom code to populate an xml document.

If he has no control over the text files' generation, then he'd need some code to "wash" it as MP explained.

I came into this when R started explaining how to do it with xml and he was way off the mark on how to use xml to populate a treeview. While his approach may work, it was not the simplest or easiest approach. It's about working smarter, not harder.

If Keith comes back saying the files are not his and he needs help "washing" them, then I'll gladly pitch in with that question  :-)
Title: Re: Building a treeview with recursion
Post by: tjr on March 22, 2009, 11:44:34 AM
I came into this when R started explaining how to do it with xml and he was way off the mark on how to use xml to populate a treeview. While his approach may work, it was not the simplest or easiest approach. It's about working smarter, not harder.
If your way is smarter then I'm glad I'm a dope. You're passing off nested for loops as recursion, which is just plain wrong. My xmlTreeClimber function will recursively build a TreeView regardless if it has 1 node or 10000, how will nested for loops do that? My xmlTreeBuilder function will generate the XML code in the exact same format recursively.

You can't just say, hey I'm going to "clean" my input into the way I want it as you don't know what the output will be used for. If he is pulling an XML file from a 3rd party app, changing some stuff and feeding it back in the same format the 3rd party expects cleaning input fails.
Title: Re: Building a treeview with recursion
Post by: MikeTuersley on March 22, 2009, 10:58:16 PM
I'm not passing off for loops as recursion - I'm saying you don't need recursion especially in your scenario with an xml file. The simpler the code, the easier it is to maintain by you or whomever inherits it.

Quote
You can't just say, hey I'm going to "clean" my input into the way I want it as you don't know what the output will be used for. If he is pulling an XML file from a 3rd party app, changing some stuff and feeding it back in the same format the 3rd party expects cleaning input fails.

Sure I can! What do you think xslt is for? Here's where the smarter comes into play - if the data is formatted such that it's a pain to deal with, convert it to something that's easier to handle. If the file is to be consumed by some other 3rd party app downstream, then obviously you need to convert it back.
Title: Re: Building a treeview with recursion
Post by: tjr on March 22, 2009, 11:55:04 PM
I see what you're saying, but I can't just bring myself to agree with it. Generated code is not maintainable, one off code compared to a generic function doesn't seem logical and converting data to a format that is simpler for a developer to understand just to modify it and convert it back seems like a recipe for failure. I'd rather take the extra 20 minutes or so and code up something myself for something as simple as this than deal with generated code.
Title: Re: Building a treeview with recursion
Post by: MikeTuersley on March 23, 2009, 02:14:53 AM
Well, if you've never tried it, give it a shot once and see what you think. I suggested the use of xsd to generate the class just because it was faster than writing all the properties and a cool little tool a lot of people are unaware of. I agree with generated code being unmaintainable but not here - it's just property declarations for the most part. To serialize/deserialize is 4 or 5 lines of standard code where you just change the object you are serializing. This can all be done in about 2 minutes assuming you have the xml file completed and are comfortable with xsd. Then its just handling the For loops. For the average cad programmer, it's nice and simple.

As for the washing, I agree, it could be tedious and may not be the wisest choice. In this specific case, it appears he could use search and replace on the tabs or a regular expression. His focus would be on something simpler (manipulating strings) than the recursion of the treeview structure. A lot of times the programmers on my team (and even I) get too focused on trying to code around the data when the easiest solution is to adjust it and then the main app programming becomes easier. It is on a case by case basis and, if it's xml, xslts make it a fairly straightforward process.

BTW, I didn't mean to infer that you were not smart!
Title: Re: Building a treeview with recursion
Post by: pkohut on March 23, 2009, 03:23:46 AM
I've been keeping my eye on this thread and Seven had it correct way back in the
beginning with counting tabs.  1) you can't infer any extra meaning from the sample
text file that's not already there.  2) The user provided the criteria of the input file.
3) I don't agree though that recursion is expensive.  If I had a concern I'd implement it
both as a recursive function and as a non-recursive function.  That's the only way to
know the true story.  4) Converting to XML and then using XSLT "IS WAY MORE EXPENSIVE"
then processing the data raw in the first place.  5) This function could probably be done in
10 to 15 lines of recursive code, without using any libraries.


Paul