I didn't think this would be that hard to do but I have been stuck on this for the last 45 minutes or so. I am trying to make a recursive function that finds files in a directory and add them to the TreeView.
This is my method so far:
private void RecursiveAddToTree(string path, TreeNode parent)
{
var directories = Directory.GetDirectories(path);
var files = Directory.GetFiles(path);
foreach (var directory in directories)
{
var node = new TreeNode(getItemOrDirectoryName(directory)) {ImageIndex = 0, SelectedImageIndex = 1};
//this is where I need to add the child node to the parent node
RecursiveAddToTree(directory,node);
}
foreach (var file in files)
{
var node = new TreeNode(getItemOrDirectoryName(file)) {ImageIndex = 0, SelectedImageIndex = 0};
//this is where I need to add the child node to the parent node
}
}
I'm looking for a way to add a child node to a parent node given the parent node, but I can't seem to figure out how to do that.
Related
Taking suggestion from this SO question, I'm populating a list view with paths array by converting it to NodeEntryCollection by
NodeEntryCollection nodes = new NodeEntryCollection();
foreach (string line in lines)
{
nodes.AddEntry(line, 0);
}
Now on double click list view item I'm using this.
private void filesList_DoubleClick(object sender, EventArgs e)
{
if (filesList.SelectedItems.Count == 0) return;
if (filesList.SelectedItems[0].Tag.ToString() == "Folder")
{
string key = filesList.SelectedItems[0].Text;
filesList.Clear();
foreach (var item in nodes[key].Children) //Exception thrown here!
{
string fileName = Path.GetFileName(item.Key);
string extension = Path.GetExtension(fileName);
if (item.Value.Children.Count > 0)
{
ListViewItem itmNew = new ListViewItem(item.Key, 0);
itmNew.Tag = "Folder";
filesList.Items.Add(itmNew);
}
else
{
ListViewItem itmNew = new ListViewItem(item.Key, objIconListManager.AddFileIcon(fileName));
itmNew.Tag = "File";
filesList.Items.Add(itmNew);
}
}
}
}
It works fine on first directory and I see files from it but when I double click again on a subdirectory it throws:
[KeyNotFoundException was unhandled]
The given key was not present in the dictionary.
Assuming that according to the question path data to tree like data structure your NodeEntryCollection is just a Dictionary<string, NodeEntry> with a bit of custom item addition logic, where NodeEntry itself is the pair of string Key and NodeEntryCollection Children.
NodeEntryCollection nodes is your root node. For example, fill it this way:
nodes.AddEntry("root", 0);
nodes.AddEntry("root/main", 0);
nodes.AddEntry("root/main/dev", 0);
At the moment nodes has only single element "root". Children of "root" is also NodeEntryCollection with only single element "main" and so on.
Also you are probably filling the filesList by iterating nodes. So only root is displayed in the list. When you double-click it, in this line:
foreach (var item in nodes[key].Children)
key value is "root" and you successfully got a first (and single) item in the collection. After iterating through the Children the main is displayed in the list. Everything are okay.
However, when you doble-click main, you again go in this foreach and key value is "main", but nodes has only single "root" key, and obviously KeyNotFoundException will be thrown. Actually you need to iterate through the Children of the current selected node, which is "main" now, not your root node.
One way to do this is to track your current selected node.
NodeEntryCollection viewRoot;
Initially, for example at nodes filling, set it to you actual tree root node:
viewRoot = nodes;
Then in your event handler:
if (filesList.SelectedItems[0].Tag.ToString() == "Folder")
{
string key = filesList.SelectedItems[0].Text;
filesList.Clear();
viewRoot = viewRoot[key].Children; // Set new view root
//foreach (var item in nodes[key].Children) //Exception thrown here!
foreach (var item in viewRoot) // iterate throught it
{
string fileName = Path.GetFileName(item.Key);
string extension = Path.GetExtension(fileName);
if (item.Value.Children.Count > 0)
{
ListViewItem itmNew = new ListViewItem(item.Key, 0);
itmNew.Tag = "Folder";
filesList.Items.Add(itmNew);
}
else
{
ListViewItem itmNew = new ListViewItem(item.Key, objIconListManager.AddFileIcon(fileName));
itmNew.Tag = "File";
filesList.Items.Add(itmNew);
}
}
}
I have a method that is used as a threadsafe callback to update a treeview. It takes two string parameters. The first is the passed data and the second is the IP of the host it checked.
I am trying to check whether or not the treeview currently contains the string containing the input string and if it doesn't than it is supposed to add it to the treeview as a parent node, then add the ip string underneath as a child. Although if it does already contain that input string as a parent node than it should add only the Ip address underneath the parent that the data string matches. So it basically sorts the ips. Each parent node will have multiple ips underneath.
My issue is that my method is adding each string as it's own parent regardless if it is a duplicate, which also means that it is not adding the IP of the duplicate input underneath the parent. Can anyone take a look and see where I am going wrong?
public void UpdateScan(string input, string ip)
{
lock (outputTree)
{
outputTree.BeginUpdate();
if (!(outputTree.Nodes.ContainsKey(input)))
{
TreeNode treeNode = new TreeNode(input);
//Add our parent node
outputTree.Nodes.Add(treeNode);
//Add our child node
treeNode.Nodes.Add(ip);
}
else
{
TreeNode[] treeNode = outputTree.Nodes.Find(input, true);
//Add only child node
foreach (var node in treeNode)
{
node.Nodes.Add(ip);
}
}
outputTree.EndUpdate();
}
}
I was able to get it working. By dynamically adding a key containing the data to the parent node, so I can then use that key to find the parents to add the correct children to them.
public void UpdateScan(string input, string ip)
{
lock (outputTree)
{
outputTree.BeginUpdate();
if (! outputTree.Nodes.ContainsKey(input))
{
TreeNode treeNode = new TreeNode(input);
treeNode.Name = input;
//Add our parent node
outputTree.Nodes.Add(treeNode);
//Add our child node
treeNode.Nodes.Add(ip);
}
else
{
TreeNode[] found = outputTree.Nodes.Find(input, true);
TreeNode newChild = new TreeNode(ip);
//Add only child node
found[0].Nodes.Add(newChild);
}
outputTree.EndUpdate();
}
}
I've created a directory and file browser TreeView on the left side. I want the users to be able to browse the tree and check the files and directories that they would like to move to another treeview.
The other TreeView is a user control I found online called TreeViewColumn. I will be using that control to allow the user to add other data (categories, attributes) to the files and folders selected.
The trouble I'm running into is two-fold, I need to recursively add all children (I can figure this out) but I need to add unchecked parents to checked children (to preserve the hierarchy).
private void IterateTreeNodes(TreeNode originalNode, TreeNode rootNode)
{
//Take the node passed through and loop through all children
foreach (TreeNode childNode in originalNode.Nodes)
{
// Create a new instance of the node, will need to add it to the recursion as a root item
// AND if checked it needs to get added to the new TreeView.
TreeNode newNode = new TreeNode(childNode.Text);
newNode.Tag = childNode.Tag;
newNode.Name = childNode.Name;
newNode.Checked = childNode.Checked;
if (childNode.Checked)
{
// Now we know this is checked, but what if the parent of this item was NOT checked.
//We need to head back up the tree to find the first parent that exists in the tree and add the hierarchy.
if (tvSelectedItems.TreeView.Nodes.ContainsKey(rootNode.Name)) // Means the parent exist?
{
tvSelectedItems.TreeView.SelectedNode = rootNode;
tvSelectedItems.TreeView.SelectedNode.Nodes.Add(newNode);
}
else
{
AddParents(childNode);
// Find the parent(s) and add them to the tree with their CheckState matching the original node's state
// When all parents have been added, add the current item.
}
}
IterateTreeNodes(childNode, newNode);
}
}
private TreeNode AddParents(TreeNode node)
{
if (node.Parent != null)
{
//tvDirectory.Nodes.Find(node.Name, false);
}
return null;
}
Could anyone help with this code so that it recursively adds checked nodes (and their parent, regardless of checked state). I need to maintain directory hierarchy.
Thanks for any help!
A rather (not-so-clean and not-preferred) solution could be to clone the tree first, and then remove unchecked branches.
Else, when you are adding a node, write a recursive method to traverse through node's parent node until you hit the root. And simply optimize it by checking if childNode.parent already exists, just ignore the branch and move on. i.e. Backtrack to root node.
I got it working. I was already aware of what #Yahya was saying, I was hoping for an easier way / better approach.
The code below is certainly not optimal, I will continue improving it on my end but at this point it is looking through a treeview on the left and copying all of the checked items (and their parents - regardless of CheckedState) to a treeview on the right.
Hopefully this helps someone and thanks for answering #Yahya.
I'm open to improvements but keep in mind this is a one-time use utility.
private void cmdMoveRight_Click(object sender, EventArgs e)
{
tvSelectedItems.TreeView.Nodes.Clear();
// Traverse first level Tree Nodes
foreach (TreeNode originalNode in tvDirectory.Nodes)
{
TreeNode newNode = new TreeNode(originalNode.Text);
newNode.Name = originalNode.Name;
newNode.Tag = originalNode.Tag;
newNode.Checked = originalNode.Checked;
//Only add to the new treeview if the node is checked
if (originalNode.Checked)
{
tvSelectedItems.TreeView.Nodes.Find(originalNode.Parent.Name,true)[0].Nodes.Add(newNode);
}
//Start recursion - this will be called for each first level node - there should technically only be 1 "ROOT" node.
IterateTreeNodes(originalNode, newNode);
}
}
private void IterateTreeNodes(TreeNode originalNode, TreeNode rootNode)
{
//Take the node passed through and loop through all children
foreach (TreeNode childNode in originalNode.Nodes)
{
// Create a new instance of the node, will need to add it to the recursion as a root item
// AND if checked it needs to get added to the new TreeView.
TreeNode newNode = new TreeNode(childNode.Text);
newNode.Tag = childNode.Tag;
newNode.Name = childNode.Name;
newNode.Checked = childNode.Checked;
if (childNode.Checked)
{
// Now we know this is checked, but what if the parent of this item was NOT checked.
//We need to head back up the tree to find the first parent that exists in the tree and add the hierarchy.
TreeNode[] nodestest = tvSelectedItems.TreeView.Nodes.Find(childNode.Parent.Name, true);
if (nodestest.Length > 0)
{
tvSelectedItems.TreeView.Nodes.Find(childNode.Parent.Name,true)[0].Nodes.Add(newNode);
}
else
{
AddParents(childNode);// Find the parent(s) and add them to the tree with their CheckState matching the original node's state
}
}
//recurse
IterateTreeNodes(childNode, newNode);
}
}
private void AddParents(TreeNode node)
{
if (node.Parent != null)// Check if parent is null (would mean we're looking at the root item
{
TreeNode[] nodestest = tvSelectedItems.TreeView.Nodes.Find(node.Parent.Name, true);
if (nodestest.Length > 0)
{
TreeNode[] nodes = tvDirectory.Nodes.Find(node.Name, true);
TreeNode newNode = new TreeNode(nodes[0].Text);
newNode.Name = nodes[0].Name;
newNode.Tag = nodes[0].Tag;
newNode.Checked = nodes[0].Checked;
tvSelectedItems.TreeView.Nodes[node.Parent.Name].Nodes.Add(newNode);
}
else
{
AddParents(node.Parent);
TreeNode newNode = new TreeNode(node.Text);
newNode.Name = node.Name;
newNode.Tag = node.Tag;
newNode.Checked = node.Checked;
tvSelectedItems.TreeView.Nodes.Find(node.Parent.Name,true)[0].Nodes.Add(newNode);
}
}
else // deal with root node
{
TreeNode rootNode = new TreeNode(node.Text);
rootNode.Name = node.Name;
rootNode.Tag = node.Tag;
rootNode.Checked = node.Checked;
tvSelectedItems.TreeView.Nodes.Add(rootNode);
}
}
I use Win forms treeview class. I need to have possibililty to navigate to specified node by its fullpath propety. i.e. I have fullpath ("country\region1\city2") and wish to select this node by this path automatically. Can anyone help me?
This is treeview navigation by msdn
private void PrintRecursive(TreeNode treeNode)
{
// Print the node.
System.Diagnostics.Debug.WriteLine(treeNode.Text);
MessageBox.Show(treeNode.Text);
// Print each node recursively.
foreach (TreeNode tn in treeNode.Nodes)
{
PrintRecursive(tn);
}
}
// Call the procedure using the TreeView.
private void CallRecursive(TreeView treeView)
{
// Print each node recursively.
TreeNodeCollection nodes = treeView.Nodes;
foreach (TreeNode n in nodes)
{
PrintRecursive(n);
}
}
This could help you too
codeproject variant of explorer with treeview navigation
I am using the following code to get the contents of a folder into a TreeView. But the current code always adds the contents to the root of the TreeView. It does not add them as child nodes of their parent folder's node.
Can you help me?
void Recurse(string path)
{
DirectoryInfo info = new DirectoryInfo(path);
TreeNode root = new TreeNode(info.Name);
string[] sub = Directory.GetDirectories(info.FullName);
TreeNode node = new TreeNode();
MailTree.Nodes.Add(root);
if (sub.Length == 0) {
}
else
{
foreach(string i in sub)
{
DirectoryInfo subinfo = new DirectoryInfo(i);
root.Nodes.Add(subinfo.Name);
Recurse(i);
}
//MailTree.Nodes.Add(root);
}
}
You should be passing a root node as part of your Rescure method, something like Rescure(string path, TreeNode currentRoot).
Now, you can call currentRoot.Nodes.Add(root) in place of MailTree.Nodes.Add(root), which will ensure that the brances are added only to the current level. You also need to change your call in the loop to Rescure(i, root).
Finally, your initial call to Rescure should include a reference to a pre-created root node, so something like Rescure(initialDirectory, initialRootNode).
One thing I would add is that your method and variable names should be changed to reflect their meaning. Yes, you are recursing, but why? A better name for the method might be TraverseDirectory. Similarly, rather than foreach(string i in sub), why not foreach(string directoryName in sub)? Having clear code is almost as important as having correct code.
The recursive part of your function is always adding the child nodes to the root. You need to add in the "parent node" as a parameter of your recursive function. Something like this:
void Recurse(string path, TreeNode parentNode)
{
DirectoryInfo info = new DirectoryInfo(path);
TreeNode node = new TreeNode(info.Name);
if (parentNode == null)
MailTree.Nodes.Add(node);
else
parentNode.Nodes.Add(node);
string[] sub = Directory.GetDirectories(path);
if (sub.Length != 0)
{
foreach(string i in sub)
{
Recurse(i, node);
}
}
}
I can't se any error in the code at a first glance, but I can suggesto to take another approach: the one you show is too expensive. Just fill a level on the tree, and put some dummy item as a leaf in each nodes you add. Then intercept the NodeExpanding event, remove the dummy node, and add the subnodes ( applying recursively the same strategy of adding the dummy child nodes )