After struggling with my first TreeView, it almost works. My problem is that in the Click Event, at the bottom of the code, the root node appears to be the only node in the collection, which is preventing me from checking all nodes when the root node is checked. I don't know what I did to cause this although I suspect I have probably added the nodes incorrectly.
There is a root node and 12 parentNode nodes in the tree. Each parentNode has multiple secondChild nodes. Each secondChild node has multiple thirdChild nodes. This is a Windows Form. All of the code is listed here. Any help is appreciated.
public Form1()
{
InitializeComponent();
FillTreeParentNodes();
}
public void FillTreeParentNodes()
{
connect.Open();
DataTable dtGroups = new DataTable("FirstChildNodes");
DataSet dsNodes = new DataSet();
dsNodes.Tables.Add(dtGroups);
SqlDataAdapter daAdapter = new SqlDataAdapter("RMM3DMTVColorDesc", connect);
daAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
daAdapter.Fill(dsNodes, "FirstChildNodes");
daAdapter.Dispose();
tvDiscountMaintenance.Nodes.Clear();
if (dsNodes.Tables[0].Rows.Count > 0)
{
TreeNode root = new TreeNode("Select All");
tvDiscountMaintenance.Nodes.Add(root);
foreach (DataRow row in dsNodes.Tables[0].Rows)
{
TreeNode parentNode = new TreeNode(row["DisColorDesc"].ToString());
parentNode.Text = row["DisColorDesc"].ToString();
root.Nodes.Add(parentNode);
FillChildNodes(parentNode, root);
}
}
}
private void FillChildNodes(TreeNode parentNode, TreeNode root)
{
DataTable dtSecondGroup = new DataTable("SecondChildNodes");
DataSet dsCNodes = new DataSet();
dsCNodes.Tables.Add(dtSecondGroup);
SqlDataAdapter daAdapter = new SqlDataAdapter("RMM3DMTVColorCodes", connect);
daAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
daAdapter.Fill(dsCNodes, "SecondChildNodes");
daAdapter.Dispose();
if (dsCNodes.Tables[0].Rows.Count > 0)
{
foreach (DataRow chRow in dsCNodes.Tables[0].Rows)
{
if (parentNode.Text.Equals(chRow["DisColorDesc"].ToString()))
{
TreeNode secondChild = new TreeNode();
secondChild.Text = chRow["DisColorCode"].ToString();
parentNode.Nodes.Add(secondChild);
FillThirdChildNodes(secondChild, root);
}
}
}
}
private void FillThirdChildNodes(TreeNode secondChild, TreeNode root)
{
DataTable dtThirdGroup = new DataTable("ThirdChildNodes");
DataSet dsThNodes = new DataSet();
dsThNodes.Tables.Add(dtThirdGroup);
SqlDataAdapter daAdapter = new SqlDataAdapter("RMM3DMTVCategories", connect);
daAdapter.SelectCommand.CommandType = CommandType.StoredProcedure;
daAdapter.Fill(dsThNodes, "ThirdChildNodes");
daAdapter.Dispose();
if (dsThNodes.Tables[0].Rows.Count > 0)
{
foreach (DataRow chRow in dsThNodes.Tables[0].Rows)
{
if (secondChild.Text.Equals(chRow["DisColorCode"].ToString()))
{
TreeNode thirdChild = new TreeNode();
thirdChild.Text = chRow["DisCategoryDesc"].ToString;
secondChild.Nodes.Add(thirdChild);
}
}
}
connect.Close();
}
private void tvDiscountMaintenance_Click(object sender, EventArgs e)
{
if (tvDiscountMaintenance.Nodes.Count > 0) // I think this is telling me that the root node is the only one in the Collection.
{
if (tvDiscountMaintenance.TopNode.Checked)
{
foreach (TreeNode node in tvDiscountMaintenance.Nodes)
{
node.ExpandAll();
node.Checked = true;
}
}
}
}
The Nodes collection of the treeview only tells you how many nodes in this collection exists, you're adding the child nodes to other nodes, so to see how many nodes in total exists you must traverse the node tree.
If you want to check just the nodes inside the root node you can check with tvDiscountMaintenance.Nodes[0].Nodes
Related
After loading elements and attributes of the below xml file in to a treeview, the nodes are edited and the treeview is saved back in to the same xml file. All elements and attributes need to be saved. However the attributes of only nested elements disappear during saving. After saving, all attribures of elements d & e are lost! This is because i am unable to retrieve the attribute values stored in to the tag property in addTreeNode function.(please see inline comments) Does anyone know of an easier or cleaner way to achieve this? Providing code snippets would be helpfull.
XML structure:
<?xml version="1.0" encoding="utf-8"?>
<root>
<a axa="1" axb="2" axc="3">content_of_tag _a</a>
<b bxa="10" bxb="20" bxc="30">content_of_tag_b</b>
<c cxa="11" cxb="21" cxc="31">
content_of_tag_c
<d dxa="101" dxb="201" dxc="301">
content_of_tag_d
<e exa="110" exb="210" exc="310">
content_of_tag_e
</e>
</d>
</c>
</root>
C# code:
private void Xml2TreeNode(XElement xNode, TreeNode treeNode)
{
if (xNode.HasElements) //if node has children
{
TreeNode tNode = null;
int i = 0;
foreach (XElement subNode in xNode.Elements())
{
if (subNode.Descendants().Count() > 0)
{
TreeNode tn = treeNode.Nodes.Add(subNode.Name.ToString().Trim());
tn.Nodes.Add(new TreeNode(subNode.FirstNode.ToString().Trim()));
treeNode.Nodes[i].Tag = subNode.Attributes().ToList(); //-------->this attribure values are NOT retrievable in saveNodes function!
tNode = tn; //add child nodes
}
else
{
TreeNode tn = treeNode.Nodes.Add(subNode.Name.ToString().Trim()); //show name of a leaf node element
tn.Nodes.Add(new TreeNode(subNode.Value.ToString().Trim())); //show value of above element as a child of its name
treeNode.Nodes[i].Tag = subNode.Attributes().ToList(); //---->these values are retrivable in saveNodes function
tNode = treeNode.Nodes[i++]; //add sibling node
}
addTreeNode(subNode, tNode); //recursively add child nodes
}
}
}
private void TreeNode2Xml(TreeNodeCollection tnc)
{
foreach (TreeNode node in tnc)
{
if (node.Nodes.Count > 0)
{
xr.WriteStartElement(node.Text);
if (node.Tag != null) //attribures retrieved here
{
List<XAttribute> attributeList = node.Tag as List<XAttribute>;
foreach (XAttribute attribute in attributeList)
{
xr.WriteAttributeString(attribute.Name.ToString(), attribute.Value.ToString());
}
}
saveNodes(node.Nodes);
xr.WriteEndElement();
}
else //No child nodes, so we just write the text
{
xr.WriteString(node.Text);
}
}
}
xr = new XmlTextWriter(filename, System.Text.Encoding.UTF8); //System.Text.Encoding.UTF8
xr.Formatting = Formatting.Indented;
xr.WriteStartDocument();
//Write our root node
xr.WriteStartElement(treeView1.Nodes[0].Text);
foreach (TreeNode node in tv.Nodes)
{
**TreeNode2Xml(node.Nodes);**
}
//Close the root node
xr.WriteEndElement();
xr.Close();
xDoc = XDocument.Load(dlg.FileName);
treeView1.Nodes.Clear();
treeView1.Nodes.Add(new TreeNode(xDoc.Document.Root.Name.ToString().Trim()));
TreeNode tNode = new TreeNode();
tNode = (TreeNode)treeView1.Nodes[0];
**Xml2TreeNode(xDoc.Root, tNode);**
treeView1.ExpandAll();
Exelent post of load and saving of data of treeview
Example Load/Save XML-Treeview
Save Data Code:
//We use this in the export and the saveNode
//functions, though it's only instantiated once.
private StreamWriter sr;
public void exportToXml(TreeView tv, string filename)
{
sr = new StreamWriter(filename, false, System.Text.Encoding.UTF8);
//Write the header
sr.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
//Write our root node
sr.WriteLine("<" + treeView1.Nodes[0].Text + ">");
foreach (TreeNode node in tv.Nodes)
{
saveNode(node.Nodes);
}
//Close the root node
sr.WriteLine("</" + treeView1.Nodes[0].Text + ">");
sr.Close();
}
private void saveNode(TreeNodeCollection tnc)
{
foreach (TreeNode node in tnc)
{
//If we have child nodes, we'll write
//a parent node, then iterrate through
//the children
if (node.Nodes.Count > 0)
{
sr.WriteLine("<" + node.Text + ">");
saveNode(node.Nodes);
sr.WriteLine("</" + node.Text + ">");
}
else //No child nodes, so we just write the text
sr.WriteLine(node.Text);
}
}
Load Data
//Open the XML file, and start to populate the treeview
private void populateTreeview()
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.Title = "Open XML Document";
dlg.Filter = "XML Files (*.xml)|*.xml";
dlg.FileName = Application.StartupPath + "\\..\\..\\example.xml";
if (dlg.ShowDialog() == DialogResult.OK)
{
try
{
//Just a good practice -- change the cursor to a
//wait cursor while the nodes populate
this.Cursor = Cursors.WaitCursor;
//First, we'll load the Xml document
XmlDocument xDoc = new XmlDocument();
xDoc.Load(dlg.FileName);
//Now, clear out the treeview,
//and add the first (root) node
treeView1.Nodes.Clear();
treeView1.Nodes.Add(new
TreeNode(xDoc.DocumentElement.Name));
TreeNode tNode = new TreeNode();
tNode = (TreeNode)treeView1.Nodes[0];
//We make a call to addTreeNode,
//where we'll add all of our nodes
addTreeNode(xDoc.DocumentElement, tNode);
//Expand the treeview to show all nodes
treeView1.ExpandAll();
}
catch(XmlException xExc)
//Exception is thrown is there is an error in the Xml
{
MessageBox.Show(xExc.Message);
}
catch(Exception ex) //General exception
{
MessageBox.Show(ex.Message);
}
finally
{
this.Cursor = Cursors.Default; //Change the cursor back
}
}
}
//This function is called recursively until all nodes are loaded
private void addTreeNode(XmlNode xmlNode, TreeNode treeNode)
{
XmlNode xNode;
TreeNode tNode;
XmlNodeList xNodeList;
if (xmlNode.HasChildNodes) //The current node has children
{
xNodeList = xmlNode.ChildNodes;
for(int x=0; x<=xNodeList.Count-1; x++)
//Loop through the child nodes
{
xNode = xmlNode.ChildNodes[x];
treeNode.Nodes.Add(new TreeNode(xNode.Name));
tNode = treeNode.Nodes[x];
addTreeNode(xNode, tNode);
}
}
else //No children, so add the outer xml (trimming off whitespace)
treeNode.Text = xmlNode.OuterXml.Trim();
}
Fixed the issue by making small changes in Xml2TreeNode() function
private void Xml2TreeNode(XElement xNode, TreeNode treeNode)
{
if (xNode.HasElements) //if node has children
{
TreeNode tNode = null;
int i = 0;
foreach (XElement subNode in xNode.Elements())
{
if (subNode.Descendants().Count() > 0)
{//handle non-leaf node
TreeNode tn = treeNode.Nodes.Add(subNode.Name.ToString().Trim());
tn.Nodes.Add(new TreeNode(subNode.FirstNode.ToString().Trim()));
tn.Tag = treeNode.Nodes[i].Tag = subNode.Attributes().ToList(); //---->these values are retrived in SaveNodes function
tNode = tn; //add child nodes
}
else
{//handle leaf node
TreeNode tn = treeNode.Nodes.Add(subNode.Name.ToString().Trim()); //show name of a leaf node element
tn.Nodes.Add(new TreeNode(subNode.Value.ToString().Trim())); //show value of above element as a child of its name
tn.Tag = treeNode.Nodes[i].Tag = subNode.Attributes().ToList(); //---->these values are retrived in SaveNodes function
tNode = treeNode.Nodes[i++]; //add sibling node
}
Xml2TreeNode(subNode, tNode); //recursively add child nodes
}
}
}
private void Xml2TreeNode(XElement xNode, TreeNode treeNode)
{
if (xNode.HasElements) //if node has children
{
TreeNode tNode = null;
int i = 0;
foreach (XElement subNode in xNode.Elements())
{
if (subNode.Descendants().Count() > 0)
{//handle non-leaf node
TreeNode tn = treeNode.Nodes.Add(subNode.Name.ToString().Trim());
////tn.Nodes.Add(new TreeNode(subNode.FirstNode.ToString().Trim())); //adds extra element-value to node
tn.Tag = treeNode.Nodes[i].Tag = subNode.Attributes().ToList(); //---->these values are retrived in TreeNode2Xml function
tNode = tn; //add child nodes
}
else
{//handle leaf node
TreeNode tn = treeNode.Nodes.Add(subNode.Name.ToString().Trim()); //show name of a leaf node element
tn.Nodes.Add(new TreeNode(subNode.Value.ToString().Trim())); //show value of above element as a child of its name
tn.Tag = treeNode.Nodes[i].Tag = subNode.Attributes().ToList(); //---->these values are retrived in TreeNode2Xml function
tNode = treeNode.Nodes[i++]; //add sibling node
}
Xml2TreeNode(subNode, tNode); //recursively add child nodes
}
}
}
I have a SQL table looking like this:
orderID customerName orderDate valueTotal
================================================================
1 JohnA 01/02/2013 100
2 AmandaF 01/02/2013 140
3 JohnA 05/03/2013 58
4 FredM 05/03/2013 200
And I want to order this information on a treeView by either orderDate or customerName, depending on user settings so that it looks like this if ordered by customerName:
JohnA
01/02/2013
05/03/2013
AmandaF
01/02/2013
FredM
05/03/2013
Or like this if ordered by orderDate:
01/02/2013
JohnA
AmandaF
05/03/2013
JohnA
FredM
What would be the best way to achieve this?
EDIT:
I'm using windows forms
If you're using ADO.Net, test this:
Dictionary<string, List<string>> groups = new Dictionary<string, List<string>>();
//set these dynamic
string groupingFieldName = "customerName";
string targetFieldName = "orderDate";
SqlDataReader rdr = sqlCmd.ExecuteReader();
while (rdr.Read())
{
if (!groups.ContainsKey(rdr[groupingFieldName].ToString()))
{
groups.Add(rdr[groupingFieldName].ToString(), new List<string>());
}
groups[rdr[groupingFieldName].ToString()].Add(rdr[targetFieldName].ToString());
}
//next, iterate the dictionary and populate the treeView
foreach (KeyValuePair<string, List<string>> group in groups)
{
//add to treeView
}
Please note that this is not tested. You still need to test it.
I've ended up creating two functions to perform this in a more dynamic and scalable way.
public static TreeNodeCollection SqlToTreeNodeHierarchy(this SqlDataReader dataReader, TreeNode parent)
{
// create a parent TreeNode if we don't have one, so we can anchor the new TreeNodes to it
// I think this will work better than a list since we might be given a real parent..
if (parent == null)
{
parent = new TreeNode("topNode");
}
while (dataReader.Read())
{
//at the beginning of each row, reset the parent
var parentNode = parent;
for (var i = 0; i < dataReader.FieldCount; i++)
{
// Adds a new TreeNode as a child of parentNode if it doesn't already exist
// at this level, else it will return the existing TreeNode and save
// it onto parentNode. This way, subsequent TreeNodes will always be a child
// of this one, until a new row begins and the parent TreeNode is reset.
parentNode = AddUniqueNode(dataReader[i].ToString(), parentNode);
}
}
return parent.Nodes;
}
public static TreeNode AddUniqueNode(string text, TreeNode parentNode)
{
// if parentNode is null, create new treeNode and return it
if (parentNode == null)
{
return new TreeNode {Name = text, Text = text};
}
// if parentNode is not null, do a find for child nodes at this level containing the key
// we're after (text and name have the same value) and return the first one it finds
foreach (var childNode in parentNode.Nodes.Find(text, false))
{
return childNode;
}
// Node does not yet exist, so just add a new node to the parentNode and return that
return parentNode.Nodes.Add(text, text);
}
Then I just need to call the functions as follows:
using (var sqlConn = new SqlConnection(connectionString))
{
sqlConn.Open();
const string query = "SELECT orderDate, customerName from MAIN";
using (var sqlCommand = new SqlCommand(query, sqlConn))
{
using (var sqlDataReader = sqlCommand.ExecuteReader())
{
var treeNodeCollection = sqlDataReader.SqlToTreeNodeHierarchy(null);
foreach (TreeNode treeNode in treeNodeCollection)
{
nativeTreeView.Nodes.Add(treeNode);
}
}
}
}
This way I can make it scale with as many child nodes as I want and it also gives me the flexibility of loading the child nodes only when on expand by doing another SQL query and passing the parent as the TreeNode that was just expanded.
I want to add to my treeview some nodes with childs, but have a problem how to add nodes with for example ToolTipText. I want do it with TreeNodeCollection.
It is possible or how could I change my code?
Here is my code where all nodes are root nodes.
protected void CreateTreeView(TreeNodeCollection parentNode, int parentID, DataTable mytab)
{
foreach (DataRow dta in mytab.Rows)
{
if (Convert.ToInt32(dta["parent_id"]) == parentID)
{
String key = dta["id"].ToString();
String text = dta["host_ip"].ToString();
TreeNode tn = new TreeNode();
tn.Name = dta["id"].ToString();
tn.Text = dta["host_ip"].ToString();
tn.ToolTipText = dta["description"].ToString();
parentNode.Add(tn);
TreeNodeCollection newParentNode = parentNode;
CreateTreeView(newParentNode, Convert.ToInt32(dta["id"]), mytab);
}
}
}
Calling code:
CreateTreeView(treeView1.Nodes, 0, dt);
If someone had with this problem here is an example:
void add_tooltiptext(DataTable mytab)
{
try
{
foreach (DataRow nodes in mytab.Rows)
{
TreeNode[] found = treeView1.Nodes.Find(nodes["id"].ToString(), true);
for (int i = 0; i < found.Length; i++)
{
treeView1.SelectedNode = found[i];
treeView1.SelectedNode.ToolTipText = nodes["description"].ToString();
}
}
}
catch
{ }
}
I have a trouble with Expand / Collapse icon in TreeView
What I get : http://i.imgur.com/dl5Lg.jpg
What I did :
C# code :
public static void TreeLoad(TreeView tree, string #source)
{
XmlDocument document = new XmlDocument();
//TreeView tree = new TreeView();
try
{
if (File.Exists(source))
{
document.Load(source);
tree.Nodes.Clear();
XmlNodeList category = document.SelectNodes("/parent/Categories");
//XmlNodeList links = document.SelectNodes("/parent/Categories/link");
foreach (XmlNode node in category)
{
TreeNode t1 = new TreeNode(node.Attributes["Name"].Value);
tree.Nodes.Add(t1);
//t1.ShowCheckBox = true;
if (node.HasChildNodes)
{
//foreach (XmlNode nod in links)
foreach (XmlNode nod in node.ChildNodes)
{
TreeNode t2 = new TreeNode(nod.Attributes["name"].Value);
tree.Nodes.Add(t2);
}
}
}
//tree.Nodes[0].CollapseAll();
//document.Save(source);
}
else
{
messages = NOTFOUND;
}
}
catch (Exception ect)
{
//exist.InnerText = ect.Message;
messages = ect.Message;
}
finally
{
// document.Save(source);
}
//return tree;
}
URLStorageCtrl.TreeLoad(tree, "example.xml");
ASP.NET code
<asp:TreeView ID="tree" runat="server"></asp:TreeView>
I'm using 4-tier architecture so please do not redirect me to design page, I use only coding.
yeah, of course. you added all nodes to tree as its root.
this code:
tree.Nodes.Add(t2);
change to :
t1.ChildNodes.Add(t2);
Is there a way I can keep the text of a Parent node but remove the link? The treeview parent node is used just as a header, no navigation. Any help would be great. Thanks.
private void BindNodes(string PtID)
{
if (PtID != "")
{
int PatientID = Convert.ToInt32(PtID);
DBConnection dbObj = new DBConnection();
if (Session["Activepatient"] != null)
{
string[] SubChild = new string[4];
SubChild[0] = "Demographics";
SubChild[1] = "Medication Reviews";
SubChild[2] = "Drug Testing & Monitoring";
SubChild[3] = "Other Program";
TreeNode oTrParent = new TreeNode();
//trv_patient.ParentNodeStyle = "None";
//oTrParent.SelectAction.Style.Add("display", "none");
TreeNode oTrSubChild1;
TreeNode oTrSubChild;
for (int i = 0; i < 4; i++)
{
oTrSubChild1 = new TreeNode();
oTrSubChild1.Text = SubChild[i];
if (i == 1)
{
PatientInfoCollection patientCollection = new PatientInfoCollection();
patientCollection = dbObj.GetMedicationReviews(PatientID);
foreach (PatientInfo au in patientCollection)
{
oTrSubChild = new TreeNode();
PatientInfo Patient = new PatientInfo();
oTrSubChild.Text = au.DateRcvd.Value.ToString("MM-dd-yyyy")
oTrSubChild.Target = au.ReviewId.ToString();
oTrSubChild1.ChildNodes.Add(oTrSubChild);
}
oTrSubChild = new TreeNode();
oTrSubChild.Text = "Add Medication Review";
oTrSubChild.Target = "New";
oTrSubChild1.ChildNodes.Add(oTrSubChild);
}
trv_patient.Nodes.Add(oTrSubChild1);
}
}
}
}
If temp is such node whose text you want to be plain text and not link then use code below.
TreeNode temp = new TreeNode("text");
temp.SelectAction = TreeNodeSelectAction.None;
treeView1.Nodes.Add(temp);
This is what your looking for. You have to set the SelectAction of the parent node TreeNode to TreeNodeSelectAction.None. I even show how to do it with the child too.
ASP.NET
<asp:TreeView
ID="MyTreeView"
ShowCheckBoxes="Parent,Leaf,All"
runat="server"
ShowLines="true"
ShowExpandCollapse="true">
</asp:TreeView>
C#
//Make A Parent Node
var Parent = new TreeNode();
Parent.Text = "Parent 1";
// Make the parent nodes not be hyperlinks but plain text
Parent.SelectAction = TreeNodeSelectAction.None;
//You can do the same with a child node like so
var Child = new TreeNode();
Child.Text = "Child 1";
// Make the child nodes not be hyperlinks but plain text
Child.SelectAction = TreeNodeSelectAction.None;
//Add the child node to the parent node
Parent.ChildNodes.Add(Child);
//Finally add the parent node with children to the treeview
MyTreeView.Nodes.Add(Parent);
I'm not sure if I understand what you want. Assuming that it's WinForms and that you just want to disable the ability to select the root node in a TreeView you could just handle the BeforeSelect event of the TreeView and then have the following code:
if (e.Node.Parent == null)
e.Cancel = true;
TreeNode root = new TreeNode("Root");
root.SelectAction = TreeNodeSelectAction.None;