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.
Related
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
I get data from tow database, db1,db2, then Now I want to create treeView for the data in the resulting datagridview; in datagrid view there are: id, name, director,the first record is the prim director, that mean he has not up director(he is owner), each record has no other record or has more records(child), and each child has grandchild and so on, this scenario Just Like in the this page:
I want to create treeview (parent and child and grandchild and so on), depending on xml file
when i used this snippet after some :
void setTree()
{
{
foreach(DataGridViewRow dt in DataGridView1.Rows)
{
var per = this.DataGridView1.Rows.Cast<DataGridViewRow>().Select(n => new person
{
name = dt.Cells[0].Value.ToString(),
Sex = dt.Cells[1].Value.ToString(),
Status = dt.Cells[2].Value.ToString(),
child = dt.Cells[3].Value.ToString(),
id = dt.Cells[4].Value.ToString(),
father = dt.Cells[5].Value.ToString()
}).ToList();
var rootTreeNode = GetTree(per, "").First();.........(1)
treeView1.Nodes.Add(rootTreeNode);
}
}
}
private TreeNode[] GetTree(List<person> per, string parent)
{
return per.Where(p => p.father == parent).Select(p =>
{
var node = new TreeNode(p.name);
node.Tag = p.id;
node.Nodes.AddRange(GetTree(per, p.id));
return node;
}).ToArray();
}
Now, when I use this code, I get error at mark(1),it say:Additional information: Sequence contains no elements.
thank you
after several readings in internet and attempts to solve this small problem, I successed finally.
this is the solution:
{
.............
TreeNode tn = new TreeNode(this.DataGridView2.Rows[0].Cells[0].Value.ToString());//text
tn.Tag = this.DataGridView2.Rows[0].Cells[4].Value.ToString();// id
tn.Name = this.DataGridView2.Rows[0].Cells[5].Value.ToString();//directorid
treeView1.Nodes.Add(tn);
settree(tn);
}
public void settree(TreeNode ns)
{
foreach (DataGridViewRow dr in DataGridView2.Rows)
{
if (dr.Cells[5].Value.ToString() == ns.Tag.ToString())
{
TreeNode tsn = new TreeNode(dr.Cells[0].Value.ToString());
tsn.Tag = dr.Cells[4].Value.ToString();
tsn.Name = dr.Cells[5].Value.ToString();
ns.Nodes.Add(tsn);
settree(tsn);
}
}
}
i will be happy if you benefit from this code.
I'm getting a nullreferenceexception using the htmlagilitypack when my search returns nothing. I need to know how to handle this in code. I'm trying to use ?? but I'm both not using it right and not really sure how to use it anyway. I really just want to know how to run some method if nodes is empty. I could probably just check with an IF if there's no better way.
public DataTable tableIntoTable(HtmlDocument doc)
{
var table = new DataTable("MyTable");
table.Columns.Add("raw", typeof(string));
var xpath = #"//th[#class='ddlabel'] | //table[not(.//*[contains(#*,'pldefault') or contains(#*,'ntdefault') or contains(#*,'bgtabon')])]";
var nodes = doc.DocumentNode.SelectNodes(xpath);
foreach (var node in nodes ?? new HtmlAgilityPack.HtmlNodeCollection {null})
//new is underlined in red, not sure how it's supposed to work
{
table.Rows.Add(node.InnerHtml);
}
return table;
}
Well, if the exception is caused by nodes being null, then don't try to iterate through it if it is null.
public DataTable tableIntoTable(HtmlDocument doc)
{
var table = new DataTable("MyTable");
table.Columns.Add("raw", typeof(string));
var xpath = #"//th[#class='ddlabel'] | //table[not(.//*[contains(#*,'pldefault') or contains(#*,'ntdefault') or contains(#*,'bgtabon')])]";
var nodes = doc.DocumentNode.SelectNodes(xpath);
// Don't iterate if nodes is null.
if (nodes != null)
{
foreach (var node in nodes)
{
table.Rows.Add(node.InnerHtml);
}
}
return table;
}
If you really like the null-coalescing operator for its beauty (like me), try this:
foreach (var node in nodes ?? Enumerable.Empty<HtmlNode>())
{
// whatever
}
Try this one:
public DataTable tableIntoTable(HtmlDocument doc)
{
var table = new DataTable("MyTable");
table.Columns.Add("raw", typeof(string));
var xpath = #"//th[#class='ddlabel'] | //table[not(.//*[contains(#*,'pldefault') or contains(#*,'ntdefault') or contains(#*,'bgtabon')])]";
var nodes = doc.DocumentNode.SelectNodes(xpath);
if (nodes != null && nodes.Count > 0)
{
foreach (var node in nodes)
{
table.Rows.Add(node.InnerHtml);
}
}
return table;
}
Do not add any check if you are iterating nodes using foreach loop. It will simply skip the loop if nodes is null.
public DataTable tableIntoTable(HtmlDocument doc)
{
var table = new DataTable("MyTable");
table.Columns.Add("raw", typeof(string));
var xpath = #"//th[#class='ddlabel'] | //table[not(.//*[contains(#*,'pldefault') or contains(#*,'ntdefault') or contains(#*,'bgtabon')])]";
var nodes = doc.DocumentNode.SelectNodes(xpath);
foreach (var node in nodes)
{
table.Rows.Add(node.InnerHtml);
}
return table;
}
I think your problem is a line above when you are getting the nodes. Just declare the node nullable.
public DataTable tableIntoTable(HtmlDocument doc)
{
var table = new DataTable("MyTable");
table.Columns.Add("raw", typeof(string));
var xpath = #"//th[#class='ddlabel'] | //table[not(.//*contains(#*,'pldefault') or contains(#*,'ntdefault') or contains(#*,'bgtabon')])]";
HtmlAgilityPack.HtmlNode? node = doc.DocumentNode.SelectNodes(xpath);
foreach (var node in nodes)
{
table.Rows.Add(node.InnerHtml);
}
return table;
}
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
{ }
}
If the logic within this method is run from an event handler such as Button_Click it works perfectly, but, when running this from a method such as below I get the error:
hostView.SelectedNode.Nodes.Add(newNode);
Object reference not set to an instance of an object.
Here is my code:
private void SetupHostTree()
{
// Set internal host names
using (var reader = File.OpenText("Configuration.ini"))
{
List<string> hostnames = ParseInternalHosts(reader).ToList();
foreach (string s in hostnames)
{
TreeNode newNode = new TreeNode(s);
hostView.SelectedNode.Nodes.Add(newNode);
string title = s;
TabPage myTabPage = new TabPage(title);
myTabPage.Name = s;
tabControl1.TabPages.Add(myTabPage);
}
}
}
Maybe there are no Selected Nodes :)
Probably because no node is currently selected in the hostView TreeView.
The documentation says that the TreeView.SelectedNode property will return null when no node is currently selected. And since you've combined it into an expression, the entire expression is failing because there is no Nodes collection on a null object!
Try this code:
private void SetupHostTree()
{
// Set internal host names
using (var reader = File.OpenText("Configuration.ini"))
{
List<string> hostnames = ParseInternalHosts(reader).ToList();
foreach (string s in hostnames)
{
// Ensure that a node is currently selected
TreeNode selectedNode = hostView.SelectedNode;
if (selectedNode != null)
{
TreeNode newNode = new TreeNode(s);
selectedNode.Nodes.Add(newNode);
}
else
{
// maybe do nothing, or maybe add the new node to the root
}
string title = s;
TabPage myTabPage = new TabPage(title);
myTabPage.Name = s;
tabControl1.TabPages.Add(myTabPage);
}
}
}