I'm having a hard time trying to get a Treeview to display child notes.
I have a DataTable which is filled with Data from a query.
The table is something like this.
| ParentOT | ChildOT
-------------------
1 | 2
1 | 3
1 | 4
4 | 5
5 | 6
now, what I need is to order this data in a TreeView.
The result must be something like this (using this same table)
1
|
--2
|
--3
|
--4
|
--5
|
--6
I tried to this on Windows Forms and the only thing I can get is the tree to show only 1 set of childs. like this
1
|
--2
|
--3
|
--4
|
--5
|
5
|
--6
I tried to do it like this:
DataTable arbolSub = mssql_cnn.ejecutarSqlSelect(q2);
//Metodo 2: muestra las ot correctamente pero no muestra mas detalle de subOT.
if (trvOTHs.Nodes.Count > 0)
{
trvOTHs.Nodes.Clear();
}
trvOTHs.BeginUpdate();
if (arb.Rows.Count > 0)
{
string otPadre = arb.Rows[0][0].ToString();
int nivel = 0;
trvOTHs.Nodes.Add(arbolSub.Rows[0]["OT Padre"].ToString());
for (int i = 0; i < arbolSub.Rows.Count; i++)
{
//trvOTHs.Nodes.Add(arbolSub.Rows[0]["OT Padre"].ToString());
if (arbolSub.Rows[i]["OT Padre"].ToString() == otPadre)
{
if (trvOTHs.Nodes[nivel].Text == otPadre)
{
trvOTHs.Nodes[nivel].Nodes.Add(arbolSub.Rows[i]["OT Hija"].ToString());
}
}
else
{
otPadre = arbolSub.Rows[i+1]["OT Padre"].ToString();
TreeNode nodo = new TreeNode(otPadre.ToString());
trvOTHs.Nodes.Add(nodo);
nivel++;
}
}
trvOTHs.Nodes[0].Remove();
trvOTHs.ExpandAll();
}
trvOTHs.EndUpdate();
where trvOTHs is a TreeView.
Please Help! Thanks
EDIT: Thanks for the reply. I finally worked around this,using a idea given by a friend and using something like the suggested solution by #Mohammad abumazen.
I ended up using two methods recursively:
private TreeView cargarOtPadres(TreeView trv, int otPadre, DataTable datos)
{
if (datos.Rows.Count > 0)
{
foreach (DataRow dr in datos.Select("OTPadre='"+ otPadre+"'"))
{
TreeNode nodoPadre = new TreeNode();
nodoPadre.Text = dr["OTPadre"].ToString();
trv.Nodes.Add(nodoPadre);
cargarSubOts(ref nodoPadre, int.Parse(dr["OTHija"].ToString()), datos);
}
}
return trv;
}
private void cargarSubOts(ref TreeNode nodoPadre, int otPadre, DataTable datos)
{
DataRow[] otHijas = datos.Select("OTPadre='" + otPadre +"'");
foreach (DataRow drow in otHijas)
{
TreeNode hija = new TreeNode();
hija.Text = drow["OTHija"].ToString();
nodoPadre.Nodes.Add(hija);
cargarSubOts(ref hija, int.Parse(drow["OTHija"].ToString()), datos);
}
}
This did it. I leave it here in case anyone needs it. Thanks again.
The main issue in your code that you are not looking for the child parent in the tree view to add it under , you add them all under main parent.
i made some changes to your code hopefully it work straight forward or with minor changes at your side:
if (arb.Rows.Count > 0)
{
TreeNode MainNode = new TreeNode();
string otPadre = arb.Rows[0][0].ToString();
int nivel = 0;
MainNode.Text = otPadre;
trvOTHs.Nodes.Add(MainNode);
for (int i = 0; i < arbolSub.Rows.Count; i++)
{
TreeNode child = new TreeNode();
child.Text = row["OT Hija"].ToString();
if (arbolSub.Rows[i]["OT Padre"].ToString() == otPadre)
{
MainNode.Nodes.Add(child);
}
else
{
FindParent(MainNode, row["OT Padre"].ToString(), child);
}
}
trvOTHs.ExpandAll();
}
this function to find the parent node :
private void FindParent(TreeNode ParentNode, string Parent, TreeNode ChildNode)
{
foreach (TreeNode node in ParentNode.Nodes)
{
if (node.Text.ToString() == Parent)
{
node.Nodes.Add(ChildNode);
}
else
{
FindParent(node, Parent, ChildNode);
}
}
}
I would do something like this, You should consider using Dictionary and HashSet for maximum performance:
//use this extension method for convenience
public static class TreeViewExtension {
public static void LoadFromDataTable(this TreeView tv, DataTable dt){
var parentNodes = dt.AsEnumerable()
.GroupBy(row => (string)row[0])
.ToDictionary(g=> g.Key, value=> value.Select(x=> (string)x[1]));
Stack<KeyValuePair<TreeNode,IEnumerable<string>>> lookIn = new Stack<KeyValuePair<TreeNode,IEnumerable<string>>>();
HashSet<string> removedKeys = new HashSet<string>();
foreach (var node in parentNodes) {
if (removedKeys.Contains(node.Key)) continue;
TreeNode tNode = new TreeNode(node.Key);
lookIn.Push(new KeyValuePair<TreeNode,IEnumerable<string>>(tNode,node.Value));
while (lookIn.Count > 0) {
var nodes = lookIn.Pop();
foreach (var n in nodes.Value) {
IEnumerable<string> children;
TreeNode childNode = new TreeNode(n);
nodes.Key.Nodes.Add(childNode);
if (parentNodes.TryGetValue(n, out children)) {
lookIn.Push(new KeyValuePair<TreeNode,IEnumerable<string>>(childNode,children));
removedKeys.Add(n);
}
}
}
tv.Nodes.Add(tNode);
}
}
}
//usage
treeView1.LoadFromDataTable(yourDataTable);
NOTE the input DataTable should contain data as you posted in your question. There is no need for other kinds of Sub-DataTable.
Thanks for the reply. I finally worked around this,using a idea given by a friend and using something like the suggested solution by #Mohammad abumazen.
I ended up using two methods recursively:
private TreeView cargarOtPadres(TreeView trv, int otPadre, DataTable datos)
{
if (datos.Rows.Count > 0)
{
foreach (DataRow dr in datos.Select("OTPadre='"+ otPadre+"'"))
{
TreeNode nodoPadre = new TreeNode();
nodoPadre.Text = dr["OTPadre"].ToString();
trv.Nodes.Add(nodoPadre);
cargarSubOts(ref nodoPadre, int.Parse(dr["OTHija"].ToString()), datos);
}
}
return trv;
}
private void cargarSubOts(ref TreeNode nodoPadre, int otPadre, DataTable datos)
{
DataRow[] otHijas = datos.Select("OTPadre='" + otPadre +"'");
foreach (DataRow drow in otHijas)
{
TreeNode hija = new TreeNode();
hija.Text = drow["OTHija"].ToString();
nodoPadre.Nodes.Add(hija);
cargarSubOts(ref hija, int.Parse(drow["OTHija"].ToString()), datos);
}
}
This did it. I leave it here in case anyone needs it. Thanks again.
Related
Hi have a data Table with 3 fields and my expected tree view will like this below image.
My data table returns the details like this.
And i tried the below code. Here child node not getting listing properly
public void BuildTree(DataTable dt, TreeView trv, Boolean expandAll)
{
trv.Nodes.Clear();
TreeNode node = default(TreeNode);
TreeNode subNode = default(TreeNode);
foreach (DataRow row in dt.Rows)
{
node = Searchnode(row[0].ToString(), trv);
if (node != null)
{
subNode = new TreeNode(row[1].ToString());
node.Nodes.Add(subNode);
}
else
{
node = new TreeNode(row[0].ToString());
subNode = new TreeNode(row[1].ToString());
node.Nodes.Add(subNode);
trv.Nodes.Add(node);
}
}
if (expandAll)
{
trv.ExpandAll();
}
}
private TreeNode Searchnode(string nodetext, TreeView trv)
{
foreach (TreeNode node in trv.Nodes)
{
if (node.Text == nodetext)
{
return node;
}
}
return null;
}
I'm suposing that datatable is previosly ordered by CustomerName, DeliverySchedule, Name
Initialize test data:
private void InitializeDataTable() {
dt = new DataTable();
dt.Columns.Add("DeliverySchedule");
dt.Columns.Add("Name");
dt.Columns.Add("CustomerName");
AddRow("Daily", "Test", "Team Venkat");
AddRow("Daily", "TestB", "Team Venkat");
AddRow("Weekly", "OtherName", "Team Venkat");
AddRow("Weekly", "OtherName2", "Team Venkat");
AddRow("Daily", "Test", "Team2");
AddRow("Weekly", "Test", "Team2");
}
private void AddRow(string schedule, string name, string customer) {
DataRow row = dt.NewRow();
row[0] = schedule;
row[1] = name;
row[2] = customer;
dt.Rows.Add(row);
}
Load tree from DataTable using a three level loop:
private void LoadBtn_Click(object sender, EventArgs e) {
int i = 0;
treeView1.Nodes.Clear();
while (i < dt.Rows.Count) {
DataRow row = dt.Rows[i];
string customer = row.Field<string>(2);
TreeNode customerNode = treeView1.Nodes.Add(customer);
while (i < dt.Rows.Count && row.Field<string>(2) == customer) {
string schedule = row.Field<string>(0);
TreeNode scheduleNode = customerNode.Nodes.Add(schedule);
while (i < dt.Rows.Count && row.Field<string>(2) == customer && schedule == row.Field<string>(0)) {
string report = row.Field<string>(1);
scheduleNode.Nodes.Add(report);
if (++i < dt.Rows.Count)
row = dt.Rows[i];
}
}
}
}
This is the code which I am using, but I am always getting a blank string from the function.
How do I solve such a problem?
private string GetArrayofCheckedNodes()
{
string arrCheckedNodes = "";
ArrayList al = new ArrayList();
foreach (TreeNode node in TreeView1.Nodes)
{
if (node.Checked == true) // Checking whether a node is checked or not.
{
al.Add(node.Text);
}
}
for (int i = 0; i < al.Count; i++)
{
arrCheckedNodes += al[i].ToString() + " , ";
}
return arrCheckedNodes;
}
I'm assuming you want ALL checked nodes. TreeView1.Nodes only returns the first level so you will need to recurse down the tree. Also, you can use string.Join() to join the resultant values together.
private string GetArrayofCheckedNodes()
{
return string.Join(" , ", GetCheckedNodes(treeView1.Nodes));
}
public List<string> GetCheckedNodes(TreeNodeCollection nodes)
{
List<string> nodeList = new List<string>();
if (nodes == null)
{
return nodeList;
}
foreach (TreeNode childNode in nodes)
{
if (childNode.Checked)
{
nodeList.Add(childNode.Text);
}
nodeList.AddRange(GetCheckedNodes(childNode.Nodes));
}
return nodeList;
}
I want to traverse through a treeview & set the value property to be its position in the tree as shown below
A[val:1]->A1[val:11]
l l--->A2[val:12]
l----->A3[val:13]
B[val:2]->B1[val:21]
l l--->B2[val:22]
C[val:3]->C1[val:31]
l l--->C2[val:32]
I have written a recursive which returns me all the nodes but i am unable to assign the desired position to its nodes.
private void TraverseTreeNode(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
TraverseTreeNode(node.ChildNodes);
}
}
Considering that TreeNode.Value is of type string, this will, starting at level = 1:
private static void TraverseTreeNode(TreeNodeCollection nodes, int parentNumber)
{
var childNumber = 1;
foreach (TreeNode node in nodes)
{
node.Value = string.Format("{0}{1}", parentNumber, childNumber ).Substring(0,node.Depth+1);
TraverseTreeNode(node.ChildNodes, parentNumber);
childNumber++;
if (node.Depth == 0) { parentNumber++; }
}
}
Only works for two levels but is easily extendable by adding additional parameters to TraverseTreeNode.
UPDATE
The following will work for any depth in hierarchy:
private static void TraverseTreeNode(TreeNodeCollection nodes, int parentNumber)
{
var childNumber = 1;
foreach (TreeNode node in nodes)
{
node.Value = node.Parent != null && node.Parent.Value != null
? string.Format("{0}{1}", node.Parent.Value, childNumber)
: string.Format("{0}{1}", parentNumber, childNumber).Substring(0, node.Depth + 1);
TraverseTreeNode(node.ChildNodes, parentNumber);
childNumber++;
if (node.Depth == 0) { parentNumber++; }
}
}
As you need recursive method, try this
private void Caller()
{
TraverseTreeNode(treeView1.Nodes);
}
private void TraverseTreeNode(TreeNodeCollection nodes)
{
int index = 1;
foreach (TreeNode node in nodes)
{
node.Text = (node.Parent != null ? node.Parent.Text : string.Empty) + index++;
TraverseTreeNode(node.Nodes);
}
}
I have a custom ASP.NET treeview control, which uses existing MS Treeview. I create and recreate the treeview upon postbacks (it is in UpdatePanel) from a stored IEnumerable.
Some items are added like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack && !Page.IsAsync)
{
DD.Items = null;
DD.Items.Add(new TreeviewItem("Choice 1", "4", "-1"));// = items;
DD.Items.Add(new TreeviewItem("something", "1", "-1"));
DD.Items.Add(new TreeviewItem("Europe", "2", "-1"));
DD.Items.Add(new TreeviewItem("pff", "3", "-1"));
}}
The control is initialized and loaded in it's OnLoad using BuildTreeFromItemCollection():
public void BuildTreeFromItemCollection()
{
BuildTreeFromItemCollection(this.Items, null);
}
public void BuildTreeFromItemCollection(IEnumerable<StoredItem> items, TreeNode parentNode)
{
IEnumerable<TreeviewItem> tvItems = items.Cast<TreeviewItem>();
var nodes = tvItems.Where(x => parentNode == null ? int.Parse(x.Parent) <= 0 : x.Parent == parentNode.Value);
TreeNode childNode;
foreach (var i in nodes)
{
childNode = new TreeNode(i.Name, i.Value)
{
PopulateOnDemand = this.PopulateOnDemand
};
if (parentNode == null)
{
TvHierarchy.Nodes.Add(childNode);
}
else
{
parentNode.ChildNodes.Add(childNode);
}
this.BuildTreeFromItemCollection(items, childNode);
}
}
TreeNodePopulate is handled like so:
void TvHierarchy_TreeNodePopulate(object sender, TreeNodeEventArgs e)
{
this.EnsureChildControls();
IEnumerable<StoredItem> childItems = NodePopulator(e.Node.Value);
foreach (StoredItem item in childItems)
{
TreeNode newNode = new TreeNode(item.Name, item.Value);
newNode.PopulateOnDemand = this.PopulateOnDemand;
e.Node.ChildNodes.Add(newNode);
}
this.Items.AddRange(childItems);
}
and this Func is temporarily attached to the NodePopulator:
private IEnumerable<StoredItem> ItemLoader(string val)
{
List<StoredItem> itemList = new List<StoredItem>();
Random r = new Random();
for (int i = 0; i <= 4; i++)
{
int rand = r.Next(10,100);
itemList.Add(new TreeviewItem("test " + rand.ToString(), rand.ToString(), val));
}
return itemList;
}
Unfortunately the BuildTreeFromItemCollection falls into infinite loop after a couple of node expansions, around 4th level, and i'm left with stack overflow.
The exact exception shows up on the line
var nodes = tvItems.Where(x => parentNode == null ? int.Parse(x.Parent) <= 0 : x.Parent == parentNode.Value);
but the Call stack looks already filled up. Where's the problem?
So it seems that the whole problem was in the code generating new nodes in this test function:
int rand = r.Next(10,100);
itemList.Add(new TreeviewItem("test " + rand.ToString(), rand.ToString(), val));
So when an ID was generated which already was earlier the code went into infinite loop. After expanding range to (10,10000) everything works fine.
I have a windows application in c# with a tree view .now i want to check all nodes of that tree view including node's of nodes .
i tried this code :
for (int i = 0; i < treevwaccess.Nodes.Count; i++)
{
formid = treevwaccess.Nodes[i].Name;
access = treevwaccess.Nodes[i].Checked;
user.updateaccesslevel(lblId.Text, formid, access);
}
but this code only check nodes in level 0
You have to use either a Stack or a recursive method like this:
DoStuff(treevwaccess.Nodes);
...
void DoStuff(TreeNodeCollection nodes)
{
foreach(TreeNode node in nodes)
{
user.updateaccesslevel(lblId.Text, node.Name, node.Checked);
DoStuff(node.Nodes);
}
}
You need to create and call recursive function like this
static void main()
{
var treevwaccess = new System.Windows.Forms.TreeView();
CheckAll(treevwaccess.Nodes);
}
static void CheckAll(System.Windows.Forms.TreeNodeCollection nodes )
{
foreach (System.Windows.Forms.TreeNode node in nodes)
{
var formid = node.Name;
var access = node.Checked;
user.updateaccesslevel(lblId.Text, formid, access);
CheckAll(node.Nodes);
}
}
You have to check child node of root node too.
For that please try this code.
for (int i = 0; i < treevwaccess.Nodes.Count; i++)
{
formid = treevwaccess.Nodes[i].Name;
access = treevwaccess.Nodes[i].Checked;
user.updateaccesslevel(lblId.Text, formid, access);
CheckChildNodes(treevwaccess.Nodes[i]);
}
void CheckChildNodes(TreeNode node)
{
if (node.Nodes.Count > 0)
{
for (int i = 0; i < node.Nodes.Count; i++)
{
formid = node.Nodes[i].Name;
access = node.Nodes[i].Checked;
user.updateaccesslevel(lblId.Text, formid, access);
}
}
}
This will help you.