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
Related
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 didn't succeed to remove specific item from linked list,the method is - public void removeFromList(string itemtoremove, LinkedList List).
How do i write method that removes specific item from linked list?
my code is:
public class Node
{
public Node next; //saves the adress
public Person data; //saves the person
}
public class LinkedList
{
private Node head; //starts from the begging
public void AddFirst(Person data)
{
Node toAdd = new Node();
toAdd.data = data; // in data he saves the object
toAdd.next = head;
head = toAdd;
}
public void removeFromList(string itemtoremove, LinkedList List) //
{
Node current = head;
Node current1 = current.next;
while (current1 != null)
{
if (current1.data.instrument == itemtoremove)
???
}
}
}
Your method already has a problem before you even implement the algorithm. You are skipping the head node. You also don't need to pass the linked list in as a parameter to the instance method.
public void removeFromList(string itemtoremove)
{
Node previousNode = null;
Node current = head;
while (current != null) //starting with current1 would ignore the head node
{
// we found a match
if (current.data.instrument == itemtoremove){
// head node is treated slightly differently
if(current == head){
// set the next node to the new head
head = current.next;
// set to null so GC can clean it up
current = null;
return;
}
else {
//update the previous node's link
previousNode.next = current.next;
current = null;
return;
}
}
// move on to the next
previousNode = current;
current = current.next;
}
}
I have a list of a class, the class also has a class which is used to display map in the tree view.
public class Option
{
public Guid Id;
public string Title;
public string Description;
public List<GotoOption> GotoOptions;
public bool IsEnd;
public string GotoValueParent;
public Option()
{
this.IsEnd = false;
this.GotoOptions = new List<GotoOption>();
}
}
public class GotoOption
{
public Guid GotoId;
public string Value;
}
So an Option can have many GotoOptions and these are mapped by the Guid, so if my tree view looked like:
Tree
1.1. Branch
1.2. Branch
1.3. Branch
There will be 4 options but the tree view will have 3 GotoOptions which link to the branches.
So my goal is to basically create a recursive loop so I don't have to manually create a loop, but I got no idea how to start it off.
Currently I have -
private void PopulateTreeView(Option option)
{
if (option != null)
{
TreeNode node = new TreeNode();
node.Text = option.Title;
node.Tag = option;
pages.Nodes.Add(node);
foreach (GotoOption op in option.GotoOptions)
{
Option ops = Options.FirstOrDefault(i => i.Id == op.GotoId);
TreeNode inner = new TreeNode();
inner.Text = ops.Title;
inner.Tag = ops;
node.Nodes.Add(inner);
foreach (GotoOption op2 in ops.GotoOptions)
{
Option opps = Options.FirstOrDefault(i => i.Id == op2.GotoId);
TreeNode inner2 = new TreeNode();
inner2.Text = opps.Title;
inner2.Tag = opps;
inner.Nodes.Add(inner2);
}
}
}
}
Which is looping for 3 layers only, but we could have 10-25 odd layers and that's a lot of manual code. I have been looking at how it works with files and folders http://www.dotnetperls.com/recursive-file-list but I can't seem to convert it from how it works there to getting it to work with my code. Any help would be great.
Managed to solve, I created an optional parameter passing in the created node, if the parameter is not passed in, it creates a new one.
private void PopulateTreeView(Option option, TreeNode existingNode = null)
{
if (option != null)
{
TreeNode newNode = new TreeNode();
newNode.Text = option.Title;
newNode.Tag = option;
if (existingNode == null)
{
pages.Nodes.Add(newNode);
}
else
{
existingNode.Nodes.Add(newNode);
}
foreach (GotoOption gotoOption in option.GotoOptions)
{
Option newOption = Options.FirstOrDefault(i => i.Id == gotoOption.GotoId);
PopulateTreeView(newOption, newNode);
}
}
}
private void CreateTreeView()
{
var roots = Options.Select(z => z.Id)
.Except(Options.SelectMany(z => z.GotoOptions.Select(x => x.GotoId)))
.Select(z => Options.Single(x => x.Id == z));
var treeNodes = roots.Select(GetNode);
foreach (var treeNode in treeNodes)
{
pages.Nodes.Add(treeNode);
}
}
private TreeNode GetNode(Option option)
{
var node = new TreeNode
{
Text = option.Title,
Tag = option
};
foreach (var child in option.GotoOptions.Select(z => Options.Single(x => x.Id == z.GotoId)))
{
node.Nodes.Add(GetNode(child));
}
return node;
}
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);
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