C# Adding Visible Nodes to Node Array - c#

I'm trying to create a function that will add each visible node in a tree to a node array and then return it.
This is the code I have so far, but struggling to figure out how to add them.
Note: The tree has a maximum of 8 nodes.
private Node[] activeClients(AdvTree tree)
{
Node[] activeClients = new Node[8];
foreach (Node client in tree.Nodes)
{
if (client.IsVisible)
{
//Add Visible Node to activeClients Node Array
}
}
return activeClients;
}

May be something like:
var visibleNodes = tree.Nodes.Where(client=>client.IsVisible)
especially if you are talking about small numbers (8 elements) and not compute intensive function, dynamic array (or vector) like List<T>, IEnumerable<T> is a right choice.
And in this way, your code also scales better in the future.

I actually figured out I didn't need a Node Array, but thanks for the help guys.
I used NodeCollection instead and it worked perfect for my needs.
private NodeCollection activeClients(AdvTree tree)
{
NodeCollection activeClients = new NodeCollection();
foreach (Node client in tree.Nodes)
{
if (client.IsVisible)
{
//Add Visible Node to activeClients Node Array
activeClients.Add(client, eTreeAction.Code);
}
}
return activeClients;
}

Related

Get list of all checked nodes and its subnodes in treeview

I have a treeview list check boxes and the list contains nodes, subnodes and in some cases subnode of subnode. When user check some items i want to get list of selected items.
On this why I get only selcted items of main node:
foreach (System.Windows.Forms.TreeNode aNode in tvSastavnica.Nodes)
{
if (aNode.Checked == true)
{
Console.WriteLine(aNode.Text);
}
}
How to travers through whole treeview and get checked items in subnodes?
If you like LINQ, you can create an extension method that traverses the whole treeview:
internal static IEnumerable<TreeNode> Descendants(this TreeNodeCollection c)
{
foreach (var node in c.OfType<TreeNode>())
{
yield return node;
foreach (var child in node.Nodes.Descendants())
{
yield return child;
}
}
}
Then you can perform every operations you want using LINQ. In your case, getting a list of selected nodes is easy:
var selectedNodes = myTreeView.Nodes.Descendants()
.Where(n => n.Checked)
.Select(n => n.Text)
.ToList();
An advantage of this approach is it is generic.
However, because the Descendant() method traverses the whole tree, it might be a bit less efficient than the answer given by #mybirthname because it only cares about nodes that are checked with their parents. I dont known if your use case includes this constraint.
EDIT: Now #mybirthname answer has been edited, it is doing the same. Now you have the loop and the LINQ solution, both are recursive.
public void GetCheckedNodes(TreeNodeCollection nodes)
{
foreach(System.Windows.Forms.TreeNode aNode in nodes)
{
//edit
if(!aNode.Checked)
continue;
Console.WriteLine(aNode.Text);
if(aNode.Nodes.Count != 0)
GetCheckedNodes(aNode.Nodes);
}
}
You don't make look back into the child notes, using recursion you can do it.
You need method like this ! In your code just call once GetCheckedNodes(tvSastavnica.Nodes) and all checked nodes should be displayed !
my way:
void LookupChecks(TreeNodeCollection nodes, List<TreeNode> list)
{
foreach (TreeNode node in nodes)
{
if (node.Checked)
list.Add(node);
LookupChecks(node.Nodes, list);
}
}
useage:
var list = new List<TreeNode>();
LookupChecks(TreeView.Nodes, list);

A special C# Tree algorithm in Umbraco CMS

I'm creating a special tree algorithm and I need a bit of help with the code that I currently have, but before you take a look on it please let me explain what it really is meant to do.
I have a tree structure and I'm interacting with a node (any of the nodes in the tree(these nodes are Umbraco CMS classes)) so upon interaction I render the tree up to the top (to the root) and obtain these values in a global collection (List<Node> in this particular case). So far, it's ok, but then upon other interaction with another node I must check the list if it already contains the parents of the clicked node if it does contain every parent and it doesn't contain this node then the interaction is on the lowest level (I hope you are still with me?).
Unfortunately calling the Contains() function in Umbraco CMS doesn't check if the list already contains the values which makes the list add the same values all over again even through I added the Contains() function for the check.
Can anyone give me hand here if he has already met such a problem? I exchanged the Contains() function for the Except and Union functions, and they yield the same result - they do contain duplicates.
var currentValue = (string)CurrentPage.technologies;
List<Node> globalNodeList = new List<Node>();
string[] result = currentValue.Split(',');
foreach (var item in result)
{
var node = new Node(int.Parse(item));
if (globalNodeList.Count > 0)
{
List<Node> nodeParents = new List<Node>();
if (node.Parent != null)
{
while (node != null)
{
if (!nodeParents.Contains(node))
{
nodeParents.Add(node);
}
node = (Node)node.Parent;
}
}
else { globalNodeList.Add(node); }
if (nodeParents.Count > 0)
{
var differences = globalNodeList.Except<Node>(globalNodeList);
globalNodeList = globalNodeList.Union<Node>(differences).ToList<Node>();
}
}
else
{
if (node.Parent != null)
{
while (node != null)
{
globalNodeList.Add(node);
node = (Node)node.Parent;
}
}
else
{
globalNodeList.Add(node);
}
}
}
}
If I understand your question, you only want to see if a particular node is an ancestor of an other node. If so, just (string) check the Path property of the node. The path property is a comma separated string. No need to build the list yourself.
Just myNode.Path.Contains(",1001") will work.
Small remarks.
If you are using Umbraco 6, use the IPublishedContent instead of Node.
If you would build a list like you do, I would rather take you can provide the Umbraco helper with multiple Id's and let umbraco build the list (from cache).
For the second remark, you are able to do this:
var myList = Umbraco.Content(1001,1002,1003);
or with a array/list
var myList = Umbraco.Content(someNode.Path.Split(','));
and because you are crawling up to the root, you might need to add a .Reverse()
More information about the UmbracoHelper can be found in the documentation: http://our.umbraco.org/documentation/Reference/Querying/UmbracoHelper/
If you are using Umbraco 4 you can use #Library.NodesById(...)

TreeNode Remove() not working

Long story short:
I traverse a generic tree and collect some nodes to a list according to a filter
after a complete traversal I start removing each node one by one
remove collected nodes
foreach (TreeData nd in nodeBucket.Reverse<TreeData>())
{
if (nd.node.Parent != null)
{
nd.node.Remove();
}
}
The tree has about 2000 nodes. Somehow when all nodes should be deleted there are a couple of nodes that don't get removed, Remove() is called upon them and they have a null parent afterwords. The nodes that don't get deleted are always the same.
Also tried node.Parent.Nodes.Remove(node), no luck.
tree.BeginUpdate();
tree.EndUpdate();
tree.Invalidate();
tree.Refresh();
tree.Update();
not working.
TreeData struct looks like this:
struct TreeData
{
public TreeData( TreeNode node)
{
this.node = node;
this.parent = node.Parent;
this.level = node.Level;
}
public TreeNode node;
public TreeNode parent;
public int level;
}
Issue fixed: the problem was related to duplicate nodes (the filtering was checking node names).
If you want to keep the current node but only need to remove the children
While(node.Nodes.Count>0)
node.Nodes[0].Remove();

How to traverse a multi-hierarchy array in C#

Say rootNode is a multi-hierarchy data structure.
rootNode.Add(node1);
rootNode.Add(node2);
node1.Add(node3);
node1.Add(node4);
node3.Add(node5);
If use foreach to traverse rootNode will only get node1, node2. How do I traverse all nodes in rootNode?
foreach(var node in rootNode){...}
You can traverse the tree using recursion.
VisitNode(Node n){
foreach(var cn in n.Children){
VisitNode(cn);
}
//Do what you want to do with your node here
Console.Writeline(n.Value);
}
Here is an example of breadth first traversal.
Make a recursive call:
TraverseNodes(parentNode)
{
for each (Node node in parentNode)
{
if (node.Nodes.Count>0)
TraverseNodes(node);
}
}
You can setup a simple recursive function
//Pseudo-code
public void traverse(Node n)
{
if(n hasChildren)
{
foreach(Node child in n.children)
{
traverse(child);
}
}
}
The easiest way would be recursion. What is recursion? See this answer for an example.
public void TraverseNodes(Node parentNode)
{
//iterate through child nodes
foreach(var node in parentNode)
{
//action
//iterate though child's child nodes.
TraverseNodes(node);
}
}
Basically you are performing the same operation on all the child items by calling the same method (TraverseNodes) on all of the parent items (starting from the first parent).
If your structure isn't too deep then you can safely use the recursive method given in the other answers.
If, however, your structure is potentially very deep then using recursion runs the risk of blowing the call stack and causing a StackOverflowException.
Here's an example of a non-recursive way of traversing your structure:
var stack = new Stack<TNode>();
stack.Push(rootNode);
while (stack.Count > 0)
{
var node = stack.Pop();
// do whatever you need to do with each node here
foreach (var childNode in node)
{
stack.Push(childNode);
}
}
Even if the (working) recursive methods have been posted yet, I'd like to contribute two additional methods.
The first one "pushes" each node in the tree into an Action<Node> that can "consume" it.
public void TraverseWithAction(Action<Node> nodeAction) {
nodeAction(this);
foreach(Node n in this.children) {
n.TraverseWithAction(nodeAction);
}
}
Usage example:
rootNode.TraverseWithAction(n => buffer.Append(n.ToString()));
The second one provides an IEnumerable<Node> over the root node and all its child nodes, recursively. (And, yes, there are only two loops but they can handle trees deeper than two.)
public IEnumerable<Node> TraverseAsEnumerable() {
yield return this;
foreach(Node n in this.children) {
foreach (Node n2 in n.TraverseAsEnumerable()) {
yield return n2;
}
}
}
Usage example:
foreach (Node n in rootNode.TraverseAsEnumerable()) {
// do something with n
}
Both methods use recursion so they might fail on very deep structures.

TreeNodeCollection reference problem

First off we have the almighty code!
List nodes = new List();
TreeNode Temp = new TreeNode();
TreeNodeCollection nodeList = treeViewTab4DirectoryTree.Nodes;
while (nodeList.Count != 0)
{
Temp = nodeList[0];
while (Temp.FirstNode != null)
{
Temp = Temp.FirstNode;
}
if (!nodes.Contains(Temp.FullPath))
{
nodes.Add(Temp.Text);
}
nodeList.Remove(Temp);
}
Now the problem: I have written the code above with the intent of creating a List containing the text from all the nodes in the tree. That works perfectly. The problem I am having is when I remove the nodes from my variable they are also being removed from the actual list. The question would be how can I make a copy of the list of nodes so I can play with them without messing with the ACTUAL list. How do I make a copy of it without just making a reference to it? Any help will be greatly appreciated!
Your problem arises because "nodeList" is a reference to treeViewTab4DirectoryTree.Nodes, rather than a copy of it.
The solution depends entirely on the what type of TreeNodeCollection you're using (WinForms, ASP.net, something else?), as you'll need to look for a .Copy(), .Clone(), .ToArray() method or similar to take a copy of the contents of the collection, rather than a reference to the existing collection.
If, for example, you're using asp.net and thus the System.Web.UI.WebControls.TreeNodeCollection, you could use the .CopyTo method in a way similar to this:
TreeNode[] x = null;
treeViewTab4DirectoryTree.Nodes.CopyTo(x, 0);
Updated to show stack based approach:
List<String> result = new List<String>();
Stack<IEnumerator> nodeColls = new Stack<IEnumerator>();
IEnumerator nodes = treeViewTab4DirectoryTree.Nodes.GetEnumerator();
nodeColls.Push(null);
while (nodes != null)
{
while (nodes.MoveNext())
{
result.add(nodes.Current.FullPath);
if (nodes.Current.FirstNode != null)
{
nodeColls.Push(nodes);
nodes = nodes.Current.Nodes.GetEnumerator();
}
}
nodes = nodeColls.Pop();
}
The code below does not work as was mentioned in comments, because it doesn't traverse the entire tree, but only takes the first leaf node of each top-level branch.
I actually thought the original code (in the question) did so too, because I thought the Remove would actually remove the top-level node after finding the first leaf node under it; but instead, it tries to remove the leaf node from the collection of top-level nodes, and just ignores it if it can't find it.
Original post, non-functioning code
First of all, why do you need to remove the items from your list?
List<string> nodes = new List<string>();
foreach (TreeNode tn in treeViewTab4DirectoryTree.Nodes)
{
TreeNode temp = tn;
while (Temp.FirstNode != null)
{
Temp = Temp.FirstNode;
}
if (!nodes.Contains(Temp.FullPath))
{
nodes.Add(Temp.Text);
}
}
To answer your concrete question, assuming the Nodes collection implements IEnumerable, use:
List<TreeNode> nodeList = new List<TreeNode>(treeViewTab4DirectoryTree.Nodes);
If you do decide to stick with your while loop, you can save an instatiation by changing
TreeNode Temp = new TreeNode();
to
TreeNode Temp = null;
... you're never actually using the object you create, at least in the part of the code you've shown.

Categories

Resources