I am having a problem with selecting a certain child node.
What I want to achieve: I you have this treeview for example (one parent with two child nodes): Parent -Child with a value 5 -Child with a value 2.
I want to add these two values and assign them to Parent node: Parent result 7 -Child 5 -Child 2.
Of course, a bigger treeview would have several parents and lots of children and they will all add up to one root node.
How can I do this?? pls help.
thx, Caslav
You could do something like the following. It assumes the value you want is part of the text (the last value after the last space).
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace TreeViewRecurse
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
RecurseTreeViewAndSumValues(treeView1.Nodes);
}
public void RecurseTreeViewAndSumValues(TreeNodeCollection treeNodeCol)
{
int tree_node_sum = 0;
foreach (TreeNode tree_node in treeNodeCol)
{
if (tree_node.Nodes.Count > 0)
{
RecurseTreeViewAndSumValues(tree_node.Nodes);
}
string[] node_split = tree_node.Text.Split(' ');
string num = node_split[node_split.Length - 1];
int parse_res = 0;
bool able_to_parse = int.TryParse(num, out parse_res);
if (able_to_parse)
{
tree_node_sum += parse_res;
}
}
if (treeNodeCol[0].Parent != null)
{
string[] node_split_parent = treeNodeCol[0].Parent.Text.Split(' ');
node_split_parent[node_split_parent.Length - 1] = tree_node_sum.ToString();
treeNodeCol[0].Parent.Text = string.Join(" ", node_split_parent);
}
}
}
}
private TreeNode SearchTree(TreeNodeCollection nodes, string searchtext)
{
TreeNode n_found_node = null;
bool b_node_found = false;
foreach (TreeNode node in nodes)
{
if (node.Tag.ToString() as string == searchtext)
{
b_node_found = true;
n_found_node = node;
}
if (!b_node_found)
{
n_found_node = SearchTree(node.Nodes, searchtext);
}
}
return n_found_node;
}
Source:
http://www.experts-exchange.com/Programming/Languages/C_Sharp/Q_21895513.html
I used a modified version of Redburn's answer to find a treenode by name:
private TreeNode GetNodeByName(TreeNodeCollection nodes, string searchtext)
{
TreeNode n_found_node = null;
bool b_node_found = false;
foreach (TreeNode node in nodes)
{
//show(node.Name + ":" +searchtext);
if (node.Name == searchtext)
{
//show("score!");
b_node_found = true;
n_found_node = node;
return n_found_node;
}
if (!b_node_found)
{
//show("here");
n_found_node = f_get_node_by_name(node.Nodes, searchtext);
if (n_found_node!=null)
{
return n_found_node;
}
}
}
return null;
}
Dunno if this matches your request, but this will add all childs > parent node
private void button2_Click(object sender, EventArgs e)
{
int grandTotal = CalculateNodes(this.treeView1.Nodes);
}
private int CalculateNodes(TreeNodeCollection nodes)
{
int grandTotal = 0;
foreach (TreeNode node in nodes)
{
if (node.Nodes.Count > 0)
{
int childTotal = CalculateNodes(node.Nodes);
node.Text = childTotal.ToString();
grandTotal += childTotal;
}
else
{
grandTotal += Convert.ToInt32(node.Text);
}
}
return grandTotal;
}
you should do some error checking etc etc to make it solid
You could inherit from TreeNode with something like this:
public class TreeNodeEx : TreeNode {
// only displayed when having no children
public int Value { get; set; }
public bool HasChildren {
get { return Nodes.Count > 0; }
}
public int GetSumOfChildren() {
if (!HasChildren)
return Value;
var children = Nodes.Cast<TreeNode>().OfType<TreeNodeEx>();
int sum = 0;
foreach (var child in children)
sum += child.GetSumOfChildren();
return sum;
}
}
Like this:
public class TotalingTreeNode : TreeNode
{
private int _value = 0;
public int Value
{
get
{
if (this.Nodes.Count > 1)
return GetTotaledValue();
else
return _value;
}
set
{
if (this.Nodes.Count < 1)
_value = value;
}
}
private int GetTotaledValue()
{
foreach (TotalingTreeNode t in this.Nodes.Cast<TotalingTreeNode>())
{
_value += t.Value;
}
return _value;
}
}
In WinForms a childnode of a tree knows its Parent. So you can reach the parent at any time using the TreeNode.Parent property. Vice versa every Node knows it's child nodes. You can reach them using Node.Nodes. This collection has an indexer that allows you to access the child nodes using an int or a string.
To find a TreeNode with a special Key use the following code:
treeView.Nodes.Find("nodeKey", true);
You can finde a description of this method at MSDN
Related
I have to create an indented navigation menu using below data from a .csv file:
ID;MenuName;ParentID;isHidden;LinkURL1;Company;NULL;False;/company2;About Us;1;False;/company/aboutus3;Mission;1;False;/company/mission4;Team;2;False;/company/aboutus/team5;Client 2;10;False;/references/client26;Client 1;10;False;/references/client17;Client 4;10;True;/references/client48;Client 5;10;True;/references/client510;References;NULL;False;/references
Using this data I have to develop an application that will parse the file and present the content in a console as the example below:
. Company.... About Us....... Team.... Mission. References.... Client 1.... Client 2
Menu items should be indented (depending on the parent), hidden items (isHidden==true) shouldn't be presented and items should be ordered alphabetically. So far I tried:
using (StreamReader sr = new StreamReader(#"file.csv"))
{
// Read the stream to a string, and write the string to the console.
string [] lines = sr.ReadToEnd().Split(/*';', */'\n');
for (int i = 1; i < lines.Length; i++)
{
Console.WriteLine($"String no {i} is : {lines[i-1]}");
}
}
With this i'm getting the lines but I'm stuck after that. I'm new in coding so any help will be appreciated :)
heres some code that should help you get off.
Working sample:
https://dotnetfiddle.net/L37Gjr
It first parses the data to a seperate object. This then gets used to build a m-ary tree, or a hierachical structure of connected nodes. (a node has a reference to 0 or more children).
https://en.wikipedia.org/wiki/M-ary_tree
Then tree traversal (use google if you need to know more) is used to insert and print the output, There is still something wrong however. it now uses level order traversal to print, this however comes up with an error:
Found root:1 - Company
Found root:10 - References
-------------------
1 - Company
2 - About Us
3 - Mission
4 - Team
10 - References
6 - Client 1
5 - Client 2
As you can see, it prints 4 - Team on the wrong level. I'll leave it to you to fix it (because i ran out of time), and if not i hope i gave you plenty ideas to go off and research on your own.
// sample for https://stackoverflow.com/questions/61395486/read-csv-file-and-return-indented-menu-c-sharp by sommmen
using System;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public class Node<T>
{
public T Data {get;set;}
public List<Node<T>> Children { get; set;}
public Node()
{
Children = new List<Node<T>>();
}
// Tree traversal in level order
public List<Node<T>> LevelOrder()
{
List<Node<T>> list = new List<Node<T>>();
Queue<Node<T>> queue = new Queue<Node<T>>();
queue.Enqueue(this);
while(queue.Count != 0)
{
Node<T> temp = queue.Dequeue();
foreach (Node<T> child in temp.Children)
queue.Enqueue(child);
list.Add(temp);
}
return list;
}
public List<Node<T>> PreOrder()
{
List<Node<T>> list = new List<Node<T>>();
list.Add(this);
foreach (Node<T> child in Children)
list.AddRange(child.PreOrder());
return list;
}
public List<Node<T>> PostOrder()
{
List<Node<T>> list = new List<Node<T>>();
foreach (Node<T> child in Children)
list.AddRange(child.PreOrder());
list.Add(this);
return list;
}
}
public class Entity
{
public int id {get;set;}
public string menuName {get;set;}
public int? parentID {get;set;}
public bool isHidden {get;set;}
public string linkURL {get;set;}
}
public static void Main()
{
var data = #"ID;MenuName;ParentID;isHidden;LinkURL
1;Company;NULL;False;/company
2;About Us;1;False;/company/aboutus
3;Mission;1;False;/company/mission
4;Team;2;False;/company/aboutus/team
5;Client 2;10;False;/references/client2
6;Client 1;10;False;/references/client1
7;Client 4;10;True;/references/client4
8;Client 5;10;True;/references/client5
10;References;NULL;False;/references";
var lines = data.Split('\n');
var rootNodes = new List<Node<Entity>>();
var childItems = new List<Entity>();
// Parse the data to entities
// Items without a parent are used as rootnodes to build a tree
foreach(var row in lines.Skip(1))
{
var columns = row.Split(';');
var id = Convert.ToInt32(columns[0]);
var menuName = columns[1];
var parentID = ToNullableInt(columns[2]);
var isHidden = Convert.ToBoolean(columns[3]);
var linkURL = columns[4];
var entity = new Entity()
{
id = id,
menuName = menuName,
parentID = parentID,
isHidden = isHidden,
linkURL = linkURL
};
if(parentID == null)
{
Console.WriteLine("Found root:" + entity.id + " - " + entity.menuName);
rootNodes.Add(new Node<Entity>()
{
Data = entity
});
}
else
{
childItems.Add(entity);
}
}
// Add the childElements to their appropriate rootnode
foreach(var rootNode in rootNodes)
{
foreach(var childItem in childItems.OrderBy(a=>a.parentID).ThenBy(b=>b.menuName))
{
var newNode = new Node<Entity>()
{
Data = childItem
};
Insert(rootNode, newNode);
}
}
Console.WriteLine("-------------------");
foreach(var rootNode in rootNodes)
{
var indent = 0;
var previous = rootNode;
foreach(var node in rootNode.LevelOrder())
{
if(node.Data.isHidden) continue;
if(previous.Data.parentID != node.Data.parentID)
indent++;
for(var i = 0; i < indent; i++)
Console.Write("\t");
Console.WriteLine(node.Data.id + " - " + node.Data.menuName);
previous = node;
}
}
}
public static void Insert(Node<Entity> rootNode, Node<Entity> targetNode)
{
foreach(var current in rootNode.LevelOrder())
{
if(current.Data.id == targetNode.Data.parentID)
{
current.Children.Add(targetNode);
return;
}
}
}
public static int? ToNullableInt(string s)
{
int i;
if (int.TryParse(s, out i)) return i;
return null;
}
}
I have created a simple list class from scratch. This is for a class assignment that I have been working on for about a week - very new to lists. We can not use generics so trying to research my question below has not been fruitful. Although I did get to watch 7 tutorials on youtube by BetterCoder and I found some stuff in my book but nothing with an example of "merging".
I have three classes - my node, my list, and my program. In my list class, I am working on building a Merge() method which eventually will compare the data in the two lists and merge them into an ordered list.
Right now for some reason my Merge method - which is very basic to help me understand what is happening - is not working correctly. It has both lists passed to it, and is adding the data from listTwo to listOne BUT for some reason when it's printing to the console the second Node's Data shows twice :
EX: 1 -> 2 -> 2
instead of printing the head (1), the next (2) and then the next (3) which it should be.
EX: 1 -> 2 -> 3
In the program class I have proven with a write line that (listOne.firstNode.Next.Next.Data) = 3 . Which it should be.
Can someone help me figure out if the nodes in list one aren't pointing to each other correctly or whatever is going on?
My Merge Method must be passed both list objects (listOne and listTwo) and eventually I need to make those passed as references but I haven't figured that out quite yet and will focus on that later I suppose.
namespace LinkedList
{
//This is my Node Class
class Node
{
public object Data { get; set; }
public Node Next { get; set; }
public Node(object dataValue) : this(dataValue, null) { }
public Node(object dataValue, Node nextNode)
{
Data = dataValue;
Next = nextNode;
}
}
//This is my List Class
class List
{
public Node firstNode;
public int count;
public List()
{
firstNode = null;
}
public bool Empty
{
get { return this.count == 0; }
}
public int Count
{
get { return this.count; }
}
public object Add(int index, object o)
{
if (index < 0)
throw new ArgumentOutOfRangeException("Index: " + index);
if (index > count)
index = count;
Node current = this.firstNode;
if (this.Empty || index == 0)
{
this.firstNode = new Node(o, this.firstNode);
}
else
{
for (int i = 0; i < index - 1; i++)
current = current.Next;
current.Next = new Node(o, current.Next);
}
count++;
return o;
}
public object Add(object o)
{
return this.Add(count, o);
}
public object Merge(List a, List b)
{
a.Add(b.firstNode.Data);
return a;
}
public void Print()
{
while (this.count > 0)
{
Console.Write(firstNode.Data + "->");
if(firstNode.Next != null)
firstNode.Data = firstNode.Next.Data;
count--;
}
}
}
//And here is my Program
class Program
{
static void Main(string[] args)
{
List listOne = new List();
List listTwo = new List();
listOne.Add(1);
listOne.Add(2);
listTwo.Add(3);
listTwo.Print();
Console.WriteLine("");
listOne.Merge(listOne, listTwo);
Console.WriteLine("");
listOne.Print();
//This line below shows that the data "3" from listTwo is being added to listOne in the list Merge Method
//Console.WriteLine(listOne.firstNode.Next.Next.Data);
Console.ReadKey();
}
}
}
Actual problem in your print method
public void Print()
{
Node node = firstNode;
for (int i = 0; i < this.count; i++)
{
Console.Write(node.Data + "->");
if (node.Next != null)
node = node.Next;
}
}
Alex Sikilinda , you are right the merge method is incomplete.
public object Merge(List a, List b)
{
Node bNode = b.firstNode;
while (bNode != null)
{
a.Add(bNode.Data);
bNode = bNode.Next;
}
return a;
}
I would write it this way:
public void Merge(List b)
{
Node lastNode = GetLastNode();
if (lastNode != null)
{
lastNode.Next = b.firstNode;
}
else
{
this.firstNode = b.firstNode;
}
}
// this method is used to find the last node in current list
private Node GetLastNode()
{
if (this.firstNode == null)
{
return null;
}
Node current = this.firstNode;
while (current.Next != null)
{
current = current.Next;
}
return current;
}
First of all, I changed signature of Merge from public object Merge(List a, List b) to public void Merge(List b). Now we can use it like this:
listOne.Merge(listTwo);
This will link listOne's last element with the first element of listTwo and thus they are merged.
Now we need to change Print method since current version modifies the list, which shouldn't happen:
public void Print()
{
Node currentNode = this.firstNode;
while(currentNode != null)
{
Console.Write(currentNode.Data + ' ');
currentNode = currentNode.Next;
}
}
Instead of assigning the data back to first node I assign the
firstNode = firstNode.Next;
Please check the below Print Code
public void Print()
{
while (this.count > 0)
{
Console.Write(firstNode.Data + "->");
if (firstNode.Next != null)
firstNode = firstNode.Next;
count--;
}
}
I am trying to learn the basics of data algorithms in C# and when implementing the below binary search tree add in process I get stuck at understanding the following: when calling the tree1.add(20); method, on the first iteration of the while loop the current.Data has the value of 50 and on the second iteration of the loop the value of the same current.Data changes to 40.
Why the value of the current.Data doesn't stuck at the value of 50 after the first iteration and what is the process by which the current.Data receives the value of 40?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BinarySearchTree
{
public class Node
{
public int Data;
public Node LeftChild;
public Node RightChild;
}
public class BinarySearchTree
{
public Node root;
public BinarySearchTree()
{
root = null;
}
public void add(int data)
{
Node newNode = new Node();
newNode.Data = data;
if (root == null)
{
root = newNode;
}
else
{
Node current = root;
Node parent;
while (true)
{
parent = current;
if (data < current.Data)
{
current = current.LeftChild;
if (current == null)
{
parent.LeftChild = newNode;
break;
}
}
else
{
current = current.RightChild;
if (current == null)
{
parent.RightChild = newNode;
break;
}
}
}
}
}
}
class Program
{
static void Main(string[] args)
{
BinarySearchTree tree1 = new BinarySearchTree();
tree1.add(50);
tree1.add(40);
tree1.add(60);
tree1.add(20);
tree1.add(45);
tree1.add(55);
tree1.add(65);
Console.ReadLine();
}
}
}
The answer lays here:
while (true)
{
parent = current;
if (data < current.Data)
{
current = current.LeftChild; // THIS LINE
if (current == null)
{
parent.LeftChild = newNode;
break;
}
}
As you see, current is being revalued and now it is the left "child" of it self. After the first 3 add usages, the tree should look like this:
50
/ \
40 60
So first iteration - current is 50, second iteration , it goes to the left(definition of a BST) and it becomes 40 . The next iteration current will contain NULL , (The left child of 40) and will be placed inside the BST.
I want to create a treeview in c# which will group file by prefix (here the prefix is a marked by the separator _). The following files should give this tree:
Files list :
p_a
p_a_test
p_LIG
p_p
p_p_c
p_p_c2
p_p_ccc
p_p_test
p_tres
TestLineGraph1
TestLineGrpah
Corresponding tree:
|--p_
|--p_a
|--p_a_test
|--p_LIG
|--p_p
|--p_p_
|--p_p_c
|--p_p_c2
|--p_p_ccc
|--p_p_test
|--p_tres
TestLineGraph1
TestLineGrpah
Here's my attempt of code:
private GraphUINode(List<string> subNodes, GraphUINode parent, string name, int lvl = 0)
: base(parent.m_viewDataSubControl)
{
parent.Nodes.Add(this);
this.Name = name;
this.Text = name;
string currentPrefix = "";
int pertinentSubNodes = 0;
while (pertinentSubNodes < subNodes.Count -1 && subNodes[pertinentSubNodes].Split('_').Length < 2+ lvl)
pertinentSubNodes++;
for (int i = 0; i <= lvl; i++)
{
currentPrefix += subNodes[pertinentSubNodes].Split('_')[i] + "_";
}
List<String> children = new List<string>();
foreach (string child in subNodes)
{
// The child is in the same group than the previous one
if (child.StartsWith(currentPrefix))
{
children.Add(child);
}
else
{
// Create a node only if needed
if (children.Count > 1)
{
// Create the new node
new GraphUINode(children, this, currentPrefix, lvl + 1);
children.Clear();
children.Add(child);
}
else
{
new GraphTemplateNode(this, m_viewDataSubControl, child);
}
currentPrefix = "";
for (int i = 0; i <= lvl; i++)
{
currentPrefix += child.Split('_')[i] + "_";
}
}
}
}
But I miss a few ones in the final result:
How can I get its back? Even when I debug step by step I can't find the logical way to do it.
So the first thing that we'll want to do here is take our strings and turn them into a tree. Once we have a tree then mapping those nodes to a TreeView is quite easy.
We'll start out with the definition for the tree itself:
public class Node<T>
{
public Node(T value, IEnumerable<Node<T>> children)
{
Value = value;
Children = children;
}
public T Value { get; private set; }
public IEnumerable<Node<T>> Children { get; private set; }
}
Nice and easy, each node is just a value and a collection of children.
Next we'll write a method to take a sequence of sequences, and build a tree from it. The idea here is that we'll group all of the items based on the first value in their sequence, build a node for each group, and then recursively call the method on the group to get the children for that node.
public static IList<Node<T>> GroupToTree<T>(this IEnumerable<IEnumerable<T>> source)
{
return GroupToTree(source.Select(sequence => sequence.GetEnumerator()));
}
private static IList<Node<T>> GroupToTree<T>(IEnumerable<IEnumerator<T>> source)
{
return source.WhereHasNext()
.GroupBy(iterator => iterator.Current)
.Select(group => new Node<T>(group.Key, GroupToTree(group)))
.ToList();
}
//This ensures that the iterators all get disposed
private static IEnumerable<IEnumerator<T>> WhereHasNext<T>(
this IEnumerable<IEnumerator<T>> source)
{
foreach (var iterator in source)
{
if (iterator.MoveNext())
yield return iterator;
else
iterator.Dispose();
}
}
Now we can take the raw data, split each of the strings into sequences of strings, and then map each of the nodes that we have here into UI-based nodes for presentation:
List<string> rawData = new List<string>();
//TODO populate raw data
Func<Node<string>, TreeNode> selector = null;
selector = node => new TreeNode(node.Value, node.Children.Select(selector).ToArray());
var nodes = rawData.Select(line => line.Split('_').AsEnumerable())
.GroupToTree()
.Select(selector);
suppose i have saved file path in database. now i want to show those file path through treeview. i found one sample that works fine but do not know when there will be huge data in database then treeview population will be hang or take too long. here i am giving the code. please check and tell which can be rectified as a result performance will be good when there will be huge data in db.
public static class MyDataBase
{
private static List<string> fields = new List<string>();
public static void AddField(string field)
{
fields.Add(field);
}
public static IList<string> FieldsInMyColumn()
{
return fields;
}
}
public void CreateTreeView()
{
foreach (string field in MyDataBase.FieldsInMyColumn())
{
string[] elements = field.Split('\\');
TreeNode parentNode = null;
for (int i = 0; i < elements.Length - 1; ++i)
{
if (parentNode == null)
{
bool exits = false;
foreach (TreeNode node in myTreeview.Nodes)
{
if (node.Text == elements[i])
{
exits = true;
parentNode = node;
}
}
if (!exits)
{
TreeNode childNode = new TreeNode(elements[i]);
myTreeview.Nodes.Add(childNode);
parentNode = childNode;
}
}
else
{
bool exits = false;
foreach (TreeNode node in parentNode.Nodes)
{
if (node.Text == elements[i])
{
exits = true;
parentNode = node;
}
}
if (!exits)
{
TreeNode childNode = new TreeNode(elements[i]);
parentNode.Nodes.Add(childNode);
parentNode = childNode;
}
}
}
if (parentNode != null)
{
parentNode.Nodes.Add(elements[elements.Length - 1]);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
MyDataBase.AddField(#"c:\jsmith\project1\hello.cs");
MyDataBase.AddField(#"c:\jsmith\project1\what.cs");
MyDataBase.AddField(#"c:\jsmith\project2\hello.cs");
MyDataBase.AddField(#"c:\jsmith\project1\tdp.cs");
MyDataBase.AddField(#"c:\jsmith\project2\ship.cs");
MyDataBase.AddField(#"d:\jsmith\project1\hello404.cs");
MyDataBase.AddField(#"c:\jsmith1\project2\ship.cs");
CreateTreeView();
}
thanks
Depending on your framework version, maybe you can try something like this :
public void ProcessPath(IEnumerable<String> path, TreeNodeCollection nodes)
{
if (!path.Any())
return;
var node = nodes.Cast<TreeNode>().FirstOrDefault(n => n.Text == path.First());
if (node == null)
{
node = new TreeNode(text: path.First());
nodes.Add(node);
}
ProcessPath(path.Skip(1),node.ChildNodes);
}
public void CreateTreeView()
{
foreach (string field in MyDataBase.FieldsInMyColumn())
ProcessPath(field.Split('\\'),myTreeView.Nodes);
}
If you really have a huge amount of rows, you should probably look for a solution where you only load the child nodes upon click on a node
The first that I could suggest to improve is to use while instead of foreach:
instead of this in both places:
bool exits = false;
foreach (TreeNode node in myTreeview.Nodes)
{
if (node.Text == elements[i])
{
exits = true;
parentNode = node;
}
}
you can use
bool exits = false;
int j = 0;
while (!exits && j<myTreeview.Nodes.Count)
{
if (myTreeview.Nodes[j].Text == elements[i])
{
exits = true;
parentNode = node;
}
j++;
}
This way you won't need to iterate through the whole Nodes collection and the loop will finish just after it finds the parent node. Off course, you will go through the whole collection in the cases there isn't Node titled elements[i].
P.S. I think you meant exists instead of exits