File System TreeView - c#

Im working with file systems and I have a List<> of file objects that have the file path as a property. Basically I need to create a treeview in .NET but im struggling to think of the best way to go about doing this as I need to create a tree structure from a list like:
C:/WINDOWS/Temp/ErrorLog.txt
C:/Program Files/FileZilla/GPL.html
C:/Documents and Settings/Administrator/ntuser.dat.LOG
etc....
The list is not structured at all and I cant make any changes to the current object structure.
I'm working in C#.
Many thanks for all who contribute

If you wanted to stick with the strings something like this would work...
TreeNode root = new TreeNode();
TreeNode node = root;
treeView1.Nodes.Add(root);
foreach (string filePath in myList) // myList is your list of paths
{
node = root;
foreach (string pathBits in filePath.Split('/'))
{
node = AddNode(node, pathBits);
}
}
private TreeNode AddNode(TreeNode node, string key)
{
if (node.Nodes.ContainsKey(key))
{
return node.Nodes[key];
}
else
{
return node.Nodes.Add(key, key);
}
}

I would turn the string into a FileInfo.
Once you have the FileInfo object, you can use the Directory property to retrieve the DirectoryInfo for each path.
Once you have the DirectoryInfo for the path, it's easy to "walk up" the Parent reference in DirectoryInfo to turn each path into a list of directories + filename - ie:
{"C:","Windows","Temp","ErrorLog.txt"}
This should be fairly straightforward to insert into your treeview. Just look for each section of the path in turn, and if it doesn't exist, add it....

give recursion a try.
private void AddFiles()
{
// Iterate your list with FileInfos here
foreach( var fileInfo in new Collection<FileInfo>() )
{
GetOrCreateTreeNode( fileInfo.Directory ).Nodes.Add( new TreeNode( fileInfo.Name ) );
}
}
private TreeNode GetOrCreateTreeNode( DirectoryInfo directory )
{
if( directory.Parent == null )
{
// Access your TreeView control here:
var rootNode = <TreeView>.Nodes[directory.Name];
if( rootNode == null )
{
rootNode = new TreeNode(directory.Name);
// Access your TreeView control here:
<TreeView>.Nodes.Add( rootNode );
}
return rootNode;
}
var parent = GetOrCreateTreeNode( directory.Parent );
var node = parent.Nodes[directory.Name];
if( node == null )
{
node = new DirectoryNode( directory );
parent.Nodes.Add( node );
}
return node;
}
This code should give you only an idea - I have to admit that I did not test it before posting here.

private void Form1_Load(object sender, EventArgs e)
{
var paths = new List<string>
{
#"C:\WINDOWS\AppPatch\MUI\040C",
#"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727",
#"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MUI",
#"C:\WINDOWS\addins",
#"C:\WINDOWS\AppPatch",
#"C:\WINDOWS\AppPatch\MUI",
#"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MUI\0409"
};
treeView1.PathSeparator = #"\";
PopulateTreeView(treeView1, paths, '\\');
}
private static void PopulateTreeView(TreeView treeView, IEnumerable<string> paths, char pathSeparator)
{
TreeNode lastNode = null;
string subPathAgg;
foreach (string path in paths)
{
subPathAgg = string.Empty;
foreach (string subPath in path.Split(pathSeparator))
{
subPathAgg += subPath + pathSeparator;
TreeNode[] nodes = treeView.Nodes.Find(subPathAgg, true);
if (nodes.Length == 0)
if (lastNode == null)
lastNode = treeView.Nodes.Add(subPathAgg, subPath);
else
lastNode = lastNode.Nodes.Add(subPathAgg, subPath);
else
lastNode = nodes[0];
}
}
}

EHosca's piece worked for me perfectly, with one change - I had to set lastnode to nothing after the foreach path in paths area.
This is eHosca's code above, ported to VB.
Private Sub PopulateTreeView(tv As TreeView, paths As List(Of String), pathSeparator As Char)
Dim lastnode As TreeNode = Nothing
Dim subPathAgg As String
For Each path In paths
subPathAgg = String.Empty
lastnode = Nothing
For Each subPath In path.Split(pathSeparator)
subPathAgg += subPath + pathSeparator
Dim nodes() As TreeNode = tv.Nodes.Find(subPathAgg, True)
If nodes.Length = 0 Then
If IsNothing(lastnode) Then
lastnode = tv.Nodes.Add(subPathAgg, subPath)
Else
lastnode = lastnode.Nodes.Add(subPathAgg, subPath)
End If
Else
lastnode = nodes(0)
End If
Next
Next
End Sub

Related

Populate TreeView with non-Empty Folders

private void ListDirectory(TreeView treeView, string path)
{
treeView.Nodes.Clear();
var rootDirectoryInfo = new DirectoryInfo(path);
treeView.Nodes.Add(CreateDirectoryNode(rootDirectoryInfo));
}
private static TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeNode(directoryInfo.Name);
foreach (var directory in directoryInfo.GetDirectories())
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
foreach (var file in directoryInfo.GetFiles().Where(s => s.Extension == ".jpg"))
directoryNode.Nodes.Add(new TreeNode(file.Name));
return directoryNode;
}
using this code I can list all jpg files and folders from a given directory. But it will list empty folders of jpg files too. How can I avoid it?
The solution is to check whether there are any files on the return from the recursion, then add it if there are.
foreach (var directory in directoryInfo.GetDirectories())
{
TreeNode subNode = CreateDirectoryNode(directory);
if (subNode.Nodes.Count > 0)
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
}
Since you're doing a depth first search the nodes will effectively be culled from the leaf up to the root if there aren't any files.
You need to check if the number of sub directories is zero and the number of files in the directory is zero. If this is the case, you should not create the node.
One way to do this is to return null from the CreateDirectoryNode method if the directory does not meet the specification. And then only add sub nodes that are not null.
Consider this code:
private void ListDirectory(TreeView treeView, string path)
{
treeView.Nodes.Clear();
var rootDirectoryInfo = new DirectoryInfo(path);
var tree_node = CreateDirectoryNode(rootDirectoryInfo);
if (tree_node != null)
treeView.Nodes.Add(tree_node);
}
//This method will return null of the specified directory does not have sub folders or JPG files
private static TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
//Obtain all sub directories
var sub_directories = directoryInfo.GetDirectories();
//Obtain all JPG files
var jpeg_files =
directoryInfo.GetFiles()
.Where(s => s.Extension.Equals(".jpg", StringComparison.OrdinalIgnoreCase))
.ToArray();
//If the above arrays are empty, return null
if (sub_directories.Length == 0 && jpeg_files.Length == 0)
return null;
var directoryNode = new TreeNode(directoryInfo.Name);
foreach (var directory in sub_directories)
{
var sub_node = CreateDirectoryNode(directory);
if(sub_node != null) //Only add sub nodes if they are not null
directoryNode.Nodes.Add(sub_node);
}
foreach (var file in jpeg_files)
directoryNode.Nodes.Add(new TreeNode(file.Name));
return directoryNode;
}

Recursive tree search return wrong results

I have this implementation of a tree recursive function. It gets all possible paths from A to D.
The recursive function is:
private static List<Path> GetPath(int depth, Path path)
{
if (depth == nodes.Length)
{
return new List<Path> { path };
}
else
{
var result = new List<Path>();
foreach(var link in nodes[depth].Links)
{
Node node = new Node { Name = nodes[depth].Name, Links = new[] { link } };
path.Add(node);
result.AddRange(
GetPath(
depth+1, path));
}
return result;
}
}
The expected results should be:
A1-B2->C3->D4
A1-B5->C3->D4
However, the paths returned are the same and they include all possible nodes twice.
What's wrong with the function?
foreach(var link in nodes[depth].Links)
{
Node node = new Node { Name = nodes[depth].Name, Links = new[] { link } };
path.Add(node);
You probably intend to make a new path (which is a copy of path) for each node that you find here, before appending the next node.
As #moreON's suggest, I add clone function into class Path, and modify the loop, in loop i copy to new instance of path:
public class Path : List<Node>
{
public override string ToString()
{
String s = "";
foreach (var node in this)
{
s += node.Name + node.Links[0] + "->";
}
return s;
}
public Path Clone()
{
var newPath = new Path();
ForEach(x => newPath.Add(new Node {Name = x.Name, Links = new int[] {x.Links[0]}}));
return newPath;
}
}
private static List<Path> GetPath(int depth, Path path)
{
if (depth == nodes.Length)
{
return new List<Path> { path };
}
else
{
var result = new List<Path>();
foreach (var link in nodes[depth].Links)
{
Node node = new Node { Name = nodes[depth].Name, Links = new[] { link } };
var currentPath = path.Clone();
currentPath.Add(node);
result.AddRange(GetPath(depth + 1, currentPath));
}
return result;
}
}
Hope this help.

Files in Array, in Array to Tree?

Using C#, I have created a program that executes a command line perl script and captures the output to a text file. The output is from ClearCase and was a huge pain to figure out, there isnt much in the way of documentation of the ClearCase plugin.
Anyhow now what I would like to do is skip the file and only use the output from the console...
The output appears like
"\Directory\subDirectory\subsubDir\etc\dir\##main\branch\version\4"
"\Directory\subDirectory\subsubDir\etc\dir\somefile.txt##main\branch\version\3"
"\Directory\subDirectory\subsubDir\etc\dir\somefile.txt##\branch\version\1"
I then want to basically load that into a tree view. So that the tree appears as a directory listing...that could be sortable so it is easy to tell the latest version of any particular file or directory.
One problem is there are multiple instances of the same directory and files as each version of a particular file is counted and there may be different branches and versions...
I have trouble because I am only slightly experienced with C# and I can't quite comprehend how to load arrays in arrays then have it neatly go to a dynamic tree view (keeping its associations).
Most online examples for tree views I find have hard coded strings, not dynamic strings[].
Does anyone have any experience doing this? Or know of any tricks? I cant decide if Visual Studio's line editing is best to use to split the directories or to use a regex...I will at a later point (once I get this figured out) want to Re-send this data into clearcase via command prompt to auto checkout these associated files...but that part seems easier from this point of view...
I can't post the code that I have, closed loop lan only.
The example of treeview that I've been scratching my head on is from DotNetPerls :
private void Form1_Load(object sender, EventArgs e)
{
//
// This is the first node in the view.
//
TreeNode treeNode = new TreeNode("Windows");
treeView1.Nodes.Add(treeNode);
//
// Another node following the first node.
//
treeNode = new TreeNode("Linux");
treeView1.Nodes.Add(treeNode);
//
// Create two child nodes and put them in an array.
// ... Add the third node, and specify these as its children.
//
TreeNode node2 = new TreeNode("C#");
TreeNode node3 = new TreeNode("VB.NET");
TreeNode[] array = new TreeNode[] { node2, node3 };
//
// Final node.
//
treeNode = new TreeNode("Dot Net Perls", array);
treeView1.Nodes.Add(treeNode);
and for arrays to tree from D.Morton # MSDN
public void AddTreeViewItem(string[] item)
{
TreeNodeCollection nodes = treeView1.Nodes;
for (int i = 0; i < item.Length; i++)
nodes = nodes.Find(item[i], false).Length > 0 ? nodes.Find(item[i], false)
[0]. Nodes : nodes.Add(item[i], item[i]).Nodes;
}
The TreeBuilder class below will build a proper tree for you. I've tested it with your examples and it works. Where there are gaps in the tree it will recursively trim the path to create the missing parent nodes and then unwind to add the item itself. The TreeBuilder breaks the problem into handling file paths and version paths by splitting your lines at the ##. It treats the version path as a child of the file path. This allows the built in Path.GetFileName and Path.GetDirectoryName functions to be used to label the nodes and trim the paths to find parents.
using System;
using System.Windows.Forms;
using System.IO;
public class TreeBuilder
{
public TreeBuilder()
{
TreeNode rootNode = new TreeNode(#"\");
rootNode.Name = #"\";
RootNode = rootNode;
}
public TreeNode RootNode
{
get;
set;
}
public void AddItems(string[] items)
{
Array.Sort(items);
if (string.IsNullOrEmpty(RootNode.Name))
{
RootNode.Name = #"\";
}
foreach (string item in items)
{
string[] itemParts = item.Split(new string[] { "##" }, StringSplitOptions.None);
string filePath = itemParts[0].TrimEnd('\\');
string versionPath = itemParts[1];
TreeNode fileNode = AddNode(RootNode, filePath);
TreeNode versionNode = AddNode(fileNode, filePath + "##", versionPath);
}
}
public TreeNode AddNode(TreeNode topNode, string path)
{
return AddNode(topNode, null, path);
}
public TreeNode AddNode(TreeNode topNode, string pathPrefix, string path)
{
pathPrefix = pathPrefix ?? string.Empty;
TreeNode node = null;
if (!string.IsNullOrEmpty(path) && topNode.Name != path)
{
string parentPath = Path.GetDirectoryName(path);
TreeNode[] matchingNodes = topNode.Nodes.Find(pathPrefix + path, true);
if (matchingNodes == null || matchingNodes.Length == 0)
{
string nodeLabel = Path.GetFileName(path);
nodeLabel = string.IsNullOrEmpty(nodeLabel) ? #"\" : nodeLabel;
node = new TreeNode(nodeLabel);
node.Name = pathPrefix + path;
TreeNode[] parentNodes = topNode.Nodes.Find(pathPrefix + parentPath, true);
TreeNode parentNode = null;
if (parentNodes != null && parentNodes.Length > 0)
{
parentNode = parentNodes[0];
parentNode.Nodes.Add(node);
}
else
{
parentNode = AddNode(topNode, pathPrefix, parentPath);
parentNode.Nodes.Add(node);
}
}
else
{
node = matchingNodes[0];
}
}
else
{
node = topNode;
}
return node;
}
}
Here's an example of how you'd use it in a form with a TreeView on it:
private void Form1_Load(object sender, EventArgs e)
{
string[] fileStrings = new String[]
{
#"\Directory\subDirectory\subsubDir\etc\dir\##main\branch\version\4",
#"\Directory\subDirectory\subsubDir\etc\dir\somefile.txt##main\branch\version\3",
#"\Directory\subDirectory\subsubDir\etc\dir\somefile.txt##\branch\version\1"
};
TreeBuilder treeBuilder = new TreeBuilder();
treeBuilder.AddItems(fileStrings);
treeView1.Nodes.Add(treeBuilder.RootNode);
treeView1.ExpandAll();
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
TreeNode selectedNode = treeView1.SelectedNode;
MessageBox.Show(string.Format("Label: {0}\nClearCase path: {1}\nTree path: {2}", selectedNode.Text, selectedNode.Name, selectedNode.FullPath));
}
One of possible solutions could be split the string by directories:
string[] directories = "\Directory\subDirectory\subsubDir\etc\dir\##main\branch\version\4".Split(Path.DirectorySeparatorChar);
after have a Dictionary<string, List<string>> tree, where key is a Directory path, and List<string> a list of directories or files of "key" directory.
If it's not something you're searching for, please clarify.

Object reference not set to an instance of an object. (C#)

If the logic within this method is run from an event handler such as Button_Click it works perfectly, but, when running this from a method such as below I get the error:
hostView.SelectedNode.Nodes.Add(newNode);
Object reference not set to an instance of an object.
Here is my code:
private void SetupHostTree()
{
// Set internal host names
using (var reader = File.OpenText("Configuration.ini"))
{
List<string> hostnames = ParseInternalHosts(reader).ToList();
foreach (string s in hostnames)
{
TreeNode newNode = new TreeNode(s);
hostView.SelectedNode.Nodes.Add(newNode);
string title = s;
TabPage myTabPage = new TabPage(title);
myTabPage.Name = s;
tabControl1.TabPages.Add(myTabPage);
}
}
}
Maybe there are no Selected Nodes :)
Probably because no node is currently selected in the hostView TreeView.
The documentation says that the TreeView.SelectedNode property will return null when no node is currently selected. And since you've combined it into an expression, the entire expression is failing because there is no Nodes collection on a null object!
Try this code:
private void SetupHostTree()
{
// Set internal host names
using (var reader = File.OpenText("Configuration.ini"))
{
List<string> hostnames = ParseInternalHosts(reader).ToList();
foreach (string s in hostnames)
{
// Ensure that a node is currently selected
TreeNode selectedNode = hostView.SelectedNode;
if (selectedNode != null)
{
TreeNode newNode = new TreeNode(s);
selectedNode.Nodes.Add(newNode);
}
else
{
// maybe do nothing, or maybe add the new node to the root
}
string title = s;
TabPage myTabPage = new TabPage(title);
myTabPage.Name = s;
tabControl1.TabPages.Add(myTabPage);
}
}
}

Transfer stringlist into treeview

I have a String List with items like this
"Root"
"Root/Item1"
"Root/Item2"
"Root/Item3/SubItem1"
"Root/Item3/SubItem2"
"Root/Item4/SubItem1"
"AnotherRoot"
How do I transfer this stringlist into a treeview ?
You can split each item into it's substrings. Then via recursion look for each item, if the parent exists add to it, and if the parent doesn't exists create it.
If you can't see how to do it, i`ll post you a sample code
Sample Code
public void AddItem(TreeView treeControl, TreeNode parent, string item)
{
TreeNodeCollection nodesRef = (parent != null) ? parent.Nodes : treeControl.Nodes;
string currentNodeName;
if (-1 == item.IndexOf('/')) currentNodeName = item;
else currentNodeName = item.Substring(0, item.IndexOf('/'));
if (nodesRef.ContainsKey(currentNodeName))
{
AddItem(treeControl, nodesRef[currentNodeName], item.Substring(currentNodeName.Length+1));
}
else
{
TreeNode newItem = nodesRef.Add(currentNodeName, currentNodeName);
if (item.Length > currentNodeName.Length)
{
AddItem(treeControl, newItem, item.Substring(item.IndexOf('/', currentNodeName.Length) + 1));
}
}
}
And the caller example:
string[] stringArr = {
"Root",
"Root/Item1",
"Root/Item2",
"Root/Item3/SubItem1",
"Root/Item3/SubItem2",
"Root/Item4/SubItem1",
"AnotherRoot"
};
foreach (string item in stringArr)
{
AddItem(treeView1, null, item);
}
One way is to iterate the items split the item and push them on a list and if the parent doesn't match pop an item from the list until the stack is empty or you have a match.
You can use this code:
private void button1_Click(object sender, EventArgs e) {
List<String> paths = new List<String> {
"Root", "Root/Item1", "Root/Item2", "Root/Item3/SubItem1",
"Root/Item3/SubItem2", "Root/Item4/SubItem1", "AnotherRoot"
};
List<TreeNode> nodeCollection = new List<TreeNode>();
foreach (var path in paths) {
AddPath(nodeCollection, path);
}
treeView1.Nodes.Clear();
treeView1.Nodes.AddRange(nodeCollection.ToArray());
}
public void AddPath(List<TreeNode> collection, String path) {
LinkedList<String> pathToBeAdded = new LinkedList<String>(path.Split(new String[] { #"/" }, StringSplitOptions.RemoveEmptyEntries));
if (pathToBeAdded.Count == 0) {
return;
}
String rootPath = pathToBeAdded.First.Value;
TreeNode root = collection.FirstOrDefault(n => n.Text.Equals(rootPath));
if (root == null) {
root = new TreeNode(rootPath);
collection.Add(root);
}
pathToBeAdded.RemoveFirst();
AddPath(root, pathToBeAdded);
}
public void AddPath(TreeNode rootNode, LinkedList<String> pathToBeAdded) {
if (pathToBeAdded.Count == 0) {
return;
}
String part = pathToBeAdded.First.Value;
TreeNode subNode = null;
if (!rootNode.Nodes.ContainsKey(part)) {
subNode = rootNode.Nodes.Add(part, part);
} else {
subNode = rootNode.Nodes[part];
}
pathToBeAdded.RemoveFirst();
AddPath(subNode, pathToBeAdded);
}
Hope this helps.
Ricardo Lacerda Castelo Branco

Categories

Resources