C# treeview getting duplicate nodes - c#

By the beginning of this week Iwas having a problem with TreeView not displaying children. Everything got worked out through recursiveness. However, a new and unexpected problem arose: the methods i'm using are getting duplicate nodes on some specific DataTables.
Having this DataTable of two columns:
ParentOT ChildOT
20120601 20120602
20120601 20120603
20120601 20120604
20120601 20120611
20120601 20120612
20120602 20120605
20120602 20120606
20120602 20120607
20120602 20120608
20120602 20120610
20120603 20120607
20120603 20120608
20120603 20120609
If I try to display its Treeview I get the right treeview, but five times consecutively (the times the parent appears as parent in parentOT records).
The Methods are these:
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["OTPadre"].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);
}
}
With Tables with just 1 great parent appearing 1 time only, it works great. How can i prevent the TreeView from duplicating??

I'll leave the answer for the sake of completion. This solution came courtesy of #King King
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);
}
}
}
You create this class
And you use afterwards like this.
treeView1.LoadFromDataTable(DataTable);
Be sure to use it with a String type DataTable. If you have a int type Table, you can do something like this:
DataTable stringDataTable = intDataTable.Clone();
stringDataTable.Columns[0].DataType = typeof(string);
stringDataTable.Columns[1].DataType = typeof(string);
foreach (DataRow dr in intDataTable.Rows)
{
stringDataTable.ImportRow(dr);
}
treeView1.LoadFromDataTable(stringDataTable);

Related

How to convert string separated by new line and comma to DataTable in C#

I have a string like this:
"Product,Price,Condition
Cd,13,New
Book,9,Used
"
Which is being passed like this:
"Product,Price,Condition\r\Cd,13,New\r\nBook,9,Used"
How could I convert it to DataTable?
Trying to do it with this helper function:
DataTable dataTable = new DataTable();
bool columnsAdded = false;
foreach (string row in data.Split(new string[] { "\r\n" }, StringSplitOptions.None))
{
DataRow dataRow = dataTable.NewRow();
foreach (string cell in row.Split(','))
{
string[] keyValue = cell.Split('~');
if (!columnsAdded)
{
DataColumn dataColumn = new DataColumn(keyValue[0]);
dataTable.Columns.Add(dataColumn);
}
dataRow[keyValue[0]] = keyValue[1];
}
columnsAdded = true;
dataTable.Rows.Add(dataRow);
}
return dataTable;
However I don't get that "connecting cells with appropriate columns" part - my cells don't have ~ in string[] keyValue = cell.Split('~'); and I obviously get an IndexOutOfRange at DataColumn dataColumn = new DataColumn(keyValue[0]);
Based on your implementation, I have written the code for you, I have not tested it. But you can use the concept.
DataRow dataRow = dataTable.NewRow();
int i = 0;
foreach (string cell in row.Split(','))
{
if (!columnsAdded)
{
DataColumn dataColumn = new DataColumn(cell);
dataTable.Columns.Add(dataColumn);
}
else
{
dataRow[i] = cell;
}
i++;
}
if(columnsAdded)
{
dataTable.Rows.Add(dataRow);
}
columnsAdded = true;
You can do that simply with Linq (and actually there is LinqToCSV on Nuget, maybe you would prefer that):
void Main()
{
string data = #"Product,Price,Condition
Cd,13,New
Book,9,Used
";
var table = ToTable(data);
Form f = new Form();
var dgv = new DataGridView { Dock = DockStyle.Fill, DataSource = table };
f.Controls.Add(dgv);
f.Show();
}
private DataTable ToTable(string CSV)
{
DataTable dataTable = new DataTable();
var lines = CSV.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var colname in lines[0].Split(','))
{
dataTable.Columns.Add(new DataColumn(colname));
}
foreach (var row in lines.Where((r, i) => i > 0))
{
dataTable.Rows.Add(row.Split(','));
}
return dataTable;
}
You can split given string into flattened string array in one call. Then you can iterate through the array and populate list of objects.
That part is optional, since you can immediately populate DataTable but I think it's way easier (more maintainable) to work with strongly-typed objects when dealing with DataTable.
string input = "Product,Price,Condition\r\nCd,13,New\r\nBook,9,Used";
string[] deconstructedInput = input.Split(new string[] { "\r\n", "," }, StringSplitOptions.None);
List<Product> products = new List<Product>();
for (int i = 3; i < deconstructedInput.Length; i += 3)
{
products.Add(new Product
{
Name = deconstructedInput[i],
Price = Decimal.Parse(deconstructedInput[i + 1]),
Condition = deconstructedInput[i + 2]
});
}
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Condition { get; set; }
}
So, products collection holds 2 objects which you can easily iterate over and populate your DataTable.
Note: This requires further checks to avoid possible runtime exceptions, also it is not dynamic. That means, if you have differently structured input it won't work.
DataTable dataTable = new DataTable();
dataTable.Columns.Add(new DataColumn(nameof(Product.Name)));
dataTable.Columns.Add(new DataColumn(nameof(Product.Price)));
dataTable.Columns.Add(new DataColumn(nameof(Product.Condition)));
foreach (var product in products)
{
var row = dataTable.NewRow();
row[nameof(Product.Name)] = product.Name;
row[nameof(Product.Price)] = product.Price;
row[nameof(Product.Condition)] = product.Condition;
dataTable.Rows.Add(row);
}

Tree View takes too long time to load nodes

I have a Folder structure with more than 25 thousand folders. This structure will get increase by 2000-3000 folders every time user copy existing structure in new root node.
My table is having 3 columns.
FOLDER_ID FOLDER_NAME PARENT_FOLDER_ID
1 Folder1
2 Folder1.1 1
3 Folder1.2 1
4 Folder1.1.1 2
5 Folder1.1.2 2
6 Folder1.2.1 3
7 Folder1.2.2 3
I am saving whole structure in database & retrieving it using following code.
public void PopulateTree(TreeView objTreeView)
{
objTreeView.Nodes.Clear();
dt = obj.Get_Folder_Structure(1);
if (dt != null)
{
foreach (DataRow dataRow in dt.Rows)
{
if (dataRow["PARENT_FOLDER_ID"] == DBNull.Value)
{
TreeNode treeRoot = new TreeNode();
treeRoot.Text = dataRow["FOLDER_NAME"].ToString();
treeRoot.Value = dataRow["FOLDER_ID"].ToString();
// treeRoot.ImageUrl = "~/Images/Folder1.gif";
// treeRoot.ExpandAll();
objTreeView.Nodes.Add(treeRoot);
foreach (TreeNode childnode in GetChildNode(Convert.ToInt64(dataRow["FOLDER_ID"])))
{
treeRoot.ChildNodes.Add(childnode);
}
}
}
}
}
private TreeNodeCollection GetChildNode(long parentid)
{
TreeNodeCollection childtreenodes = new TreeNodeCollection();
DataView dataView1 = new DataView(dt);
String strFilter = "" + "PARENT_FOLDER_ID" + "=" + parentid.ToString() + "";
dataView1.RowFilter = strFilter;
if (dataView1.Count > 0)
{
foreach (DataRow dataRow in dataView1.ToTable().Rows)
{
TreeNode childNode = new TreeNode();
childNode.Text = dataRow["FOLDER_NAME"].ToString();
childNode.Value = dataRow["FOLDER_ID"].ToString();
// childNode.ImageUrl = "~/Images/oInboxF.gif";
// childNode.ExpandAll();
foreach (TreeNode cnode in GetChildNode(Convert.ToInt64(dataRow["FOLDER_ID"])))
{
childNode.ChildNodes.Add(cnode);
}
childtreenodes.Add(childNode);
}
}
return childtreenodes;
}
Everything is working fine with this code. Only problem I am facing is it takes too long time to load my web page.(more than 15 minutes)
Can anyone help me to reduce this time. or suggest any other way to save & retrieve this data with less time?

How can I delete all child rows and the parents in a DataSet?

So, I have a DataRelation as is shown below:
Oraciones1 = new DataRelation("Antecedentes",
dsPubs.Tables["Consecuente"].Columns["Id"],
dsPubs.Tables["Antecedentes"].Columns["Id"]);
dsPubs.Relations.Add(Oraciones1);
where dsPubs is dsPubs = new DataSet() and that DataSet has two tables which Both have a data relation. Then; I am iterating over the parents to get the childs, just to get some task done as is shown next:
foreach (DataRow rowAuthor in dsPubs.Tables["Consecuente"].Rows) {
foreach (DataRow rowTitle in rowAuthor.GetChildRows(Oraciones1)) {
}
}
and what I want to do is to remove all the child and the parent from an a specified value which is passed through a function(i.e).
public void function(string value ){
foreach (DataRow rowAuthor in dsPubs.Tables["Consecuente"].Rows){
foreach (DataRow rowTitle in rowAuthor.GetChildRows(Oraciones1){
if(value==rowTitle["id_d"].ToString()){
//remove all the child and the parent from the specified variable "value"
}
}
}
}
I tried to get it done by using rowTitle.Delete() and rowAuthor.Delete() method but it seemed to not be working, because I think it removes the whole table, and a the time The foreach wanted to continue to grab another value from the table "Consecuente" it crashed.
Thank you a lot!!
var innerRows = new List<DataRow>();
var outerRows = new List<DataRow>();
foreach (DataRow rowAuthor in dsPubs.Tables["Consecuente"].Rows)
{
foreach (DataRow rowTitle in rowAuthor.GetChildRows(Oraciones1))
{
if (value == rowTitle["id_d"].ToString())
{
//remove all the child and the parent from the specified variable "value"
innerRows.Add(rowTitle);
outerRows.Add(rowAuthor);
}
}
}
foreach(DataRow row in innerRows)
{
row.Delete();
}
foreach (DataRow row in outerRows)
{
row.Delete();
}

Outter joins in LINQ query

This code works good it takes and matches all the unit_no and vehiclename together but it only shows the matches i also need the ones that dont match on.
I am loading a datagrid in WPF with SQLserver data and another Datagrid from oracle data.
private void GetSQLOraclelinqData()
{
var TstarData = GetTrackstarTruckData();
var M5Data = GetM5Data();
DataTable ComTable = new DataTable();
foreach (DataColumn OraColumn in M5Data.Columns)
{
ComTable.Columns.Add(OraColumn.ColumnName, OraColumn.DataType);
}
foreach (DataColumn SQLColumn in TstarData.Columns)
{
if (SQLColumn.ColumnName == "VehicleName")
ComTable.Columns.Add(SQLColumn.ColumnName + "2", SQLColumn.DataType);
else
ComTable.Columns.Add(SQLColumn.ColumnName, SQLColumn.DataType);
}
var results = M5Data.AsEnumerable().Join(TstarData.AsEnumerable(),
a => a.Field<String>("Unit_No"),
b => b.Field<String>("VehicleName"),
(a, b) =>
{
DataRow row = ComTable.NewRow();
row.ItemArray = a.ItemArray.Concat(b.ItemArray).ToArray();
ComTable.Rows.Add(row);
return row;
}).ToList();

Reading XML Data and Storing in DataTable

I have an log file like this..
This is the segment 1
============================
<MAINELEMENT><ELEMENT1>10-10-2013 10:10:22.444</ELEMENT1><ELEMENT2>1111</ELEMENT2>
<ELEMENT3>Message 1</ELEMENT3></MAINELEMENT>
<MAINELEMENT><ELEMENT1>10-10-2013 10:10:22.555</ELEMENT1><ELEMENT2>1111</ELEMENT2>
<ELEMENT3>Message 2</ELEMENT3></MAINELEMENT>
This is the segment 2
============================
<MAINELEMENT><ELEMENT1>10-11-2012 10:10:22.444</ELEMENT1><ELEMENT2>2222</ELEMENT2>
<ELEMENT3>Message 1</ELEMENT3></MAINELEMENT>
<MAINELEMENT><ELEMENT1>10-11-2012 10:10:22.555</ELEMENT1><ELEMENT2>2222</ELEMENT2>
<ELEMENT3>Message 2</ELEMENT3></MAINELEMENT>
How can I read this into DataTable excluding the data This is the segment 1 and This is the segment 2 and ====== lines completely.
I would like to have the Datatable as with Columns as "ELEMENT1", "ELEMENT2", "ELEMENT3" and fill the details with the content between those tags in the order of print of line.
It should not change the sequence of the order of records in the table while inserting.
HtmlAgilityPack seems to be a good tool for what you need:
using HtmlAgilityPack;
class Program
{
static void Main(string[] args)
{
var doc = new HtmlDocument();
doc.Load("log.txt");
var dt = new DataTable();
bool hasColumns = false;
foreach (HtmlNode row in doc
.DocumentNode
.SelectNodes("//mainelement"))
{
if (!hasColumns)
{
hasColumns = true;
foreach (var column in row.ChildNodes
.Where(node => node.GetType() == typeof(HtmlNode)))
{
dt.Columns.Add(column.Name);
}
}
dt.Rows.Add(row.ChildNodes
.Where(node => node.GetType() == typeof(HtmlNode))
.Select(node => node.InnerText).ToArray());
}
}
}
could do this, where stringData is the data from the file you have
var array = stringData.Split(new[] { "============================" }, StringSplitOptions.RemoveEmptyEntries);
var document = new XDocument(new XElement("Root"));
foreach (var item in array)
{
if(!item.Contains("<"))
continue;
var subDocument = XDocument.Parse("<Root>" + item.Substring(0, item.LastIndexOf('>') + 1) + "</Root>");
foreach (var element in subDocument.Root.Descendants("MAINELEMENT"))
{
document.Root.Add(element);
}
}
var table = new DataTable();
table.Columns.Add("ELEMENT1");
table.Columns.Add("ELEMENT2");
table.Columns.Add("ELEMENT3");
var rows =
document.Descendants("MAINELEMENT").Select(el =>
{
var row = table.NewRow();
row["ELEMENT1"] = el.Element("ELEMENT1").Value;
row["ELEMENT2"] = el.Element("ELEMENT2").Value;
row["ELEMENT3"] = el.Element("ELEMENT3").Value;
return row;
});
foreach (var row in rows)
{
table.Rows.Add(row);
}
foreach (DataRow dataRow in table.Rows)
{
Console.WriteLine("{0},{1},{2}", dataRow["ELEMENT1"], dataRow["ELEMENT2"], dataRow["ELEMENT3"]);
}
I'm not so sure where you problem is.
You can use XElement for reading the xml and manually creating DataTable.
For Reading the XML See Xml Parsing using XElement
Then you can create dynamically the datatable.
Heres an example of creating a datatable in code
https://sites.google.com/site/bhargavaclub/datatablec
But why do you want to use a DataTable ? There are a lot of downsides...

Categories